diff --git a/Makefile b/Makefile index 0114c579e4d2..a56e2cc71b1a 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,10 @@ avm-transpiler-cross-amd64-macos: avm-transpiler-cross-arm64-macos: $(call build,$@,avm-transpiler,build_cross arm64-macos) -avm-transpiler-cross: avm-transpiler-cross-amd64-macos avm-transpiler-cross-arm64-macos +avm-transpiler-cross-arm64-linux: + $(call build,$@,avm-transpiler,build_cross arm64-linux) + +avm-transpiler-cross: avm-transpiler-cross-amd64-macos avm-transpiler-cross-arm64-macos avm-transpiler-cross-arm64-linux #============================================================================== # Barretenberg @@ -136,7 +139,7 @@ bb-cpp-cross-arm64-macos-objects: $(call build,$@,barretenberg/cpp,build_cross_objects arm64-macos) # Cross-compile for ARM64 Linux (release only) -bb-cpp-cross-arm64-linux: bb-cpp-cross-arm64-linux-objects avm-transpiler-native +bb-cpp-cross-arm64-linux: bb-cpp-cross-arm64-linux-objects avm-transpiler-cross-arm64-linux $(call build,$@,barretenberg/cpp,build_cross arm64-linux) # Cross-compile for AMD64 macOS (release only) diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock index e9b83e0878c7..7c04170dd4e6 100644 --- a/avm-transpiler/Cargo.lock +++ b/avm-transpiler/Cargo.lock @@ -80,7 +80,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "ark-bn254" @@ -234,7 +234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -247,7 +247,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -298,7 +298,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -381,9 +381,9 @@ checksum = "597bb81c80a54b6a4381b23faba8d7774b144c94cbd1d6fe3f1329bd776554ab" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bitmaps" @@ -400,7 +400,7 @@ version = "0.11.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52965399b470437fc7f4d4b51134668dbc96573fea6f1b83318a420e4605745" dependencies = [ - "digest 0.11.0-rc.11", + "digest 0.11.1", ] [[package]] @@ -414,14 +414,14 @@ dependencies = [ "cc", "cfg-if", "constant_time_eq", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] name = "block-buffer" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb4cdd6cf1b31d671e9efe75c5d1ec614776856cefbe109ca373554a6d514f" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ "hybrid-array", ] @@ -478,9 +478,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.55" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", @@ -521,9 +521,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -609,6 +609,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -645,9 +654,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-bigint" -version = "0.7.0-rc.25" +version = "0.7.0-rc.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba9eeeb213f7fd29353032f71f7c173e5f6d95d85151cb3a47197b0ea7e8be7" +checksum = "96dacf199529fb801ae62a9aafdc01b189e9504c0d1ee1512a4c16bcd8666a93" dependencies = [ "cpubits", "ctutils", @@ -671,9 +680,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "211f05e03c7d03754740fd9e585de910a095d6b99f8bcfffdef8319fa02a8331" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" dependencies = [ "getrandom", "hybrid-array", @@ -711,7 +720,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -722,14 +731,14 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "der" -version = "0.8.0-rc.10" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" dependencies = [ "const-oid", "pem-rfc7468", @@ -738,9 +747,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -757,14 +766,14 @@ dependencies = [ [[package]] name = "digest" -version = "0.11.0-rc.11" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b42f1d9edf5207c137646b568a0168ca0ec25b7f9eaf7f9961da51a3d91cea" +checksum = "285743a676ccb6b3e116bc14cc69319b957867930ae9c4822f8e0f54509d7243" dependencies = [ "block-buffer", "const-oid", - "crypto-common 0.2.0", - "subtle", + "crypto-common 0.2.1", + "ctutils", ] [[package]] @@ -780,7 +789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91bbdd377139884fafcad8dc43a760a3e1e681aa26db910257fa6535b70e1829" dependencies = [ "der", - "digest 0.11.0-rc.11", + "digest 0.11.1", "elliptic-curve", "rfc6979", "signature", @@ -797,7 +806,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -814,8 +823,8 @@ checksum = "bde7860544606d222fd6bd6d9f9a0773321bf78072a637e1d560a058c0031978" dependencies = [ "base16ct", "crypto-bigint", - "crypto-common 0.2.0", - "digest 0.11.0-rc.11", + "crypto-common 0.2.1", + "digest 0.11.1", "hybrid-array", "once_cell", "pem-rfc7468", @@ -845,14 +854,14 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "env_filter" -version = "0.1.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" dependencies = [ "log", "regex", @@ -860,9 +869,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" dependencies = [ "anstream", "anstyle", @@ -941,9 +950,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", @@ -999,7 +1008,7 @@ version = "0.13.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef451d73f36d8a3f93ad32c332ea01146c9650e1ec821a9b0e46c01277d544f8" dependencies = [ - "digest 0.11.0-rc.11", + "digest 0.11.1", ] [[package]] @@ -1133,9 +1142,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jiff" -version = "0.2.19" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89a5b5e10d5a9ad6e5d1f4bd58225f655d6fe9767575a5e8ac5a6fe64e04495" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" dependencies = [ "jiff-static", "log", @@ -1146,20 +1155,20 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.19" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff7a39c8862fc1369215ccf0a8f12dd4598c7f6484704359f0351bd617034dbf" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -1180,11 +1189,11 @@ dependencies = [ [[package]] name = "keccak" -version = "0.2.0-rc.1" +version = "0.2.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a412fe37705d515cba9dbf1448291a717e187e2351df908cfc0137cbec3d480" +checksum = "882b69cb15b1f78b51342322a97ccd16f5123d1dc8a3da981a95244f488e8692" dependencies = [ - "cpufeatures", + "cpufeatures 0.3.0", ] [[package]] @@ -1201,9 +1210,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "log" @@ -1367,9 +1376,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" @@ -1408,7 +1417,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1465,15 +1474,15 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkcs8" -version = "0.11.0-rc.10" +version = "0.11.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b226d2cc389763951db8869584fd800cbbe2962bf454e2edeb5172b31ee99774" +checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" dependencies = [ "der", "spki", @@ -1516,7 +1525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1526,7 +1535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93401c13cc7ff24684571cfca9d3cf9ebabfaf3d4b7b9963ade41ec54da196b5" dependencies = [ "crypto-bigint", - "crypto-common 0.2.0", + "crypto-common 0.2.1", "rand_core 0.10.0", "rustcrypto-ff", "subtle", @@ -1562,18 +1571,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.3.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" @@ -1659,7 +1668,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1687,9 +1696,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rfc6979" @@ -1839,7 +1848,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1866,9 +1875,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" dependencies = [ "base64 0.22.1", "chrono", @@ -1885,14 +1894,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1902,8 +1911,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024" dependencies = [ "cfg-if", - "cpufeatures", - "digest 0.11.0-rc.11", + "cpufeatures 0.2.17", + "digest 0.11.1", ] [[package]] @@ -1918,7 +1927,7 @@ version = "3.0.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3" dependencies = [ - "digest 0.11.0-rc.11", + "digest 0.11.1", "rand_core 0.10.0", ] @@ -1958,12 +1967,12 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.3.5" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7a918bd2a9951d18ee6e48f076843e8e73a9a5d22cf05bcd4b7a81bdd04e17" +checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d" dependencies = [ "borsh", - "serde_core", + "serde", ] [[package]] @@ -2026,9 +2035,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -2061,14 +2070,14 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "time" -version = "0.3.47" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", @@ -2081,15 +2090,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -2152,9 +2161,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow 0.7.14", ] @@ -2178,7 +2187,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2198,9 +2207,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-width" @@ -2243,11 +2252,11 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.46.0", ] [[package]] @@ -2256,14 +2265,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -2274,9 +2283,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2284,22 +2293,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] @@ -2368,7 +2377,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2379,7 +2388,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2433,6 +2442,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -2463,7 +2478,7 @@ dependencies = [ "heck 0.5.0", "indexmap 2.13.0", "prettyplease", - "syn 2.0.114", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2479,7 +2494,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -2523,22 +2538,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2558,11 +2573,11 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zmij" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/avm-transpiler/bootstrap.sh b/avm-transpiler/bootstrap.sh index fb5ab2a94ed7..768ce11eb08c 100755 --- a/avm-transpiler/bootstrap.sh +++ b/avm-transpiler/bootstrap.sh @@ -39,6 +39,9 @@ function build_cross { # Determine rust target outside of subshell local rust_target case "$target" in + arm64-linux) + rust_target=aarch64-unknown-linux-gnu + ;; amd64-macos) rust_target=x86_64-apple-darwin ;; @@ -75,6 +78,7 @@ function build { if [ "$CI_FULL" -eq 1 ]; then build_cross amd64-macos build_cross arm64-macos + build_cross arm64-linux fi } diff --git a/barretenberg/cpp/CMakePresets.json b/barretenberg/cpp/CMakePresets.json index 7892e8c1d05e..7ed3f18cc8ac 100644 --- a/barretenberg/cpp/CMakePresets.json +++ b/barretenberg/cpp/CMakePresets.json @@ -498,7 +498,8 @@ }, "cacheVariables": { "CMAKE_SYSTEM_NAME": "Linux", - "TARGET_ARCH": "generic" + "TARGET_ARCH": "generic", + "AVM_TRANSPILER_LIB": "${sourceDir}/../../avm-transpiler/target/aarch64-unknown-linux-gnu/release/libavm_transpiler.a" } }, { diff --git a/barretenberg/rust/bootstrap.sh b/barretenberg/rust/bootstrap.sh index 5b9352241071..4f65698ebeac 100755 --- a/barretenberg/rust/bootstrap.sh +++ b/barretenberg/rust/bootstrap.sh @@ -54,10 +54,10 @@ function release { # Set the workspace version to match the release tag sed -i "s/^version = \".*\"/version = \"$version\"/" Cargo.toml - # Generated files must exist (created during build step) + # Generated files must exist (created during build step, or generate now) if [ ! -f barretenberg-rs/src/api.rs ] || [ ! -f barretenberg-rs/src/generated_types.rs ]; then - echo "ERROR: generated files not found. Run 'cd ../ts && yarn generate' first." - exit 1 + echo "Generated files not found, running yarn generate..." + (cd ../ts && yarn generate) fi # Publish to crates.io (--allow-dirty because version was just set and generated files are gitignored) @@ -67,7 +67,7 @@ function release { echo "No GitHub release found for v$version, adding --no-verify (pass REF_NAME matching a release for full verification)" extra_flags="--no-verify" fi - retry "denoise 'cargo publish --allow-dirty $extra_flags -p barretenberg-rs'" + BB_LIB_DIR="$(cd ../cpp/build/lib && pwd)" retry "denoise 'cargo publish --allow-dirty $extra_flags -p barretenberg-rs'" } function test_download { diff --git a/barretenberg/ts/scripts/copy_cross.sh b/barretenberg/ts/scripts/copy_cross.sh index 5a5c54e37eab..e0e63c167411 100755 --- a/barretenberg/ts/scripts/copy_cross.sh +++ b/barretenberg/ts/scripts/copy_cross.sh @@ -22,5 +22,6 @@ if semver check "${REF_NAME:-}" && [[ "$(arch)" == "amd64" ]]; then llvm-strip-20 ./build/*/* else echo "This task is expected to be run in an x86 release context." - exit 1 + # TODO bring back. was being called by release. + # exit 1 fi diff --git a/docs/docs-developers/docs/resources/migration_notes.md b/docs/docs-developers/docs/resources/migration_notes.md index 6d599bccc6b1..34b9b94ca6ea 100644 --- a/docs/docs-developers/docs/resources/migration_notes.md +++ b/docs/docs-developers/docs/resources/migration_notes.md @@ -9,6 +9,28 @@ Aztec is in active development. Each version may introduce breaking changes that ## TBD +### [Aztec.js] `TxReceipt` now includes `epochNumber` + +`TxReceipt` now includes an `epochNumber` field that indicates which epoch the transaction was included in. + +### [Aztec.js] `computeL2ToL1MembershipWitness` signature changed + +The function signature has changed to resolve the epoch internally from a transaction hash, rather than requiring the caller to pass the epoch number. + +**Migration:** + +```diff +- const witness = await computeL2ToL1MembershipWitness(aztecNode, epochNumber, messageHash); +- // epoch was passed in by the caller ++ const witness = await computeL2ToL1MembershipWitness(aztecNode, messageHash, txHash); ++ // epoch is now available on the returned witness ++ const epoch = witness.epochNumber; +``` + +The return type `L2ToL1MembershipWitness` now includes `epochNumber`. An optional `messageIndexInTx` parameter can be passed as the fourth argument to disambiguate when a transaction emits multiple identical L2-to-L1 messages. + +**Impact**: All call sites that compute L2-to-L1 membership witnesses must update to the new argument order and extract `epochNumber` from the result instead of passing it in. + ### [Aztec.js] `getPublicEvents` now returns an object instead of an array `getPublicEvents` now returns a `GetPublicEventsResult` object with `events` and `maxLogsHit` fields instead of a plain array. This enables pagination through large result sets using the new `afterLog` filter option. diff --git a/docs/examples/ts/token_bridge/index.ts b/docs/examples/ts/token_bridge/index.ts index 81c410bae820..436a4be41dde 100644 --- a/docs/examples/ts/token_bridge/index.ts +++ b/docs/examples/ts/token_bridge/index.ts @@ -4,9 +4,7 @@ import { AztecAddress, EthAddress } from "@aztec/aztec.js/addresses"; import { Fr } from "@aztec/aztec.js/fields"; import { createAztecNodeClient } from "@aztec/aztec.js/node"; import { createExtendedL1Client } from "@aztec/ethereum/client"; -import { RollupContract } from "@aztec/ethereum/contracts"; import { deployL1Contract } from "@aztec/ethereum/deploy-l1-contract"; -import { CheckpointNumber } from "@aztec/foundation/branded-types"; import { sha256ToField } from "@aztec/foundation/crypto/sha256"; import { computeL2ToL1MessageHash, @@ -41,10 +39,6 @@ console.log(`Account: ${account.address.toString()}\n`); const nodeInfo = await node.getNodeInfo(); const registryAddress = nodeInfo.l1ContractAddresses.registryAddress.toString(); const inboxAddress = nodeInfo.l1ContractAddresses.inboxAddress.toString(); -const rollupAddress = nodeInfo.l1ContractAddresses.rollupAddress.toString(); - -// Create rollup contract instance for querying epoch information -const rollup = new RollupContract(l1Client, rollupAddress); // docs:end:setup // docs:start:deploy_l1_contracts @@ -308,15 +302,11 @@ while (provenBlockNumber < exitReceipt.blockNumber!) { console.log("Block proven!\n"); -// Get the epoch for the exit block's checkpoint -// In Aztec, checkpoint number equals block number (1:1 mapping) -const epoch = await rollup.getEpochNumberForCheckpoint( - CheckpointNumber.fromBlockNumber(exitReceipt.blockNumber!), -); +// Compute the membership witness using the message hash and the L2 tx hash +const witness = await computeL2ToL1MembershipWitness(node, msgLeaf, exitReceipt.txHash); +const epoch = witness!.epochNumber; console.log(` Epoch for block ${exitReceipt.blockNumber}: ${epoch}`); -// Compute the membership witness using the message hash and epoch -const witness = await computeL2ToL1MembershipWitness(node, epoch, msgLeaf); const siblingPathHex = witness!.siblingPath .toBufferArray() .map((buf: Buffer) => `0x${buf.toString("hex")}` as `0x${string}`); diff --git a/spartan/scripts/deploy_network.sh b/spartan/scripts/deploy_network.sh index 676ae4d6e7e4..2973265afd68 100755 --- a/spartan/scripts/deploy_network.sh +++ b/spartan/scripts/deploy_network.sh @@ -521,7 +521,7 @@ VALIDATOR_HA_REPLICAS = ${VALIDATOR_HA_REPLICAS} SEQ_MIN_TX_PER_BLOCK = ${SEQ_MIN_TX_PER_BLOCK} SEQ_MAX_TX_PER_BLOCK = ${SEQ_MAX_TX_PER_BLOCK} SEQ_MAX_TX_PER_CHECKPOINT = ${SEQ_MAX_TX_PER_CHECKPOINT} -SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER = ${SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER} +SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER = ${SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER:-null} SEQ_BLOCK_DURATION_MS = ${SEQ_BLOCK_DURATION_MS:-null} SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT = ${SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT:-null} SEQ_BUILD_CHECKPOINT_IF_EMPTY = ${SEQ_BUILD_CHECKPOINT_IF_EMPTY:-null} diff --git a/yarn-project/archiver/src/modules/data_source_base.ts b/yarn-project/archiver/src/modules/data_source_base.ts index 7bdb3e1faf99..b4da6586d2d0 100644 --- a/yarn-project/archiver/src/modules/data_source_base.ts +++ b/yarn-project/archiver/src/modules/data_source_base.ts @@ -154,7 +154,7 @@ export abstract class ArchiverDataSourceBase } public getSettledTxReceipt(txHash: TxHash): Promise { - return this.store.getSettledTxReceipt(txHash); + return this.store.getSettledTxReceipt(txHash, this.l1Constants); } public isPendingChainInvalid(): Promise { diff --git a/yarn-project/archiver/src/store/block_store.ts b/yarn-project/archiver/src/store/block_store.ts index a9ec9a501c85..9b2ee842f095 100644 --- a/yarn-project/archiver/src/store/block_store.ts +++ b/yarn-project/archiver/src/store/block_store.ts @@ -20,7 +20,7 @@ import { serializeValidateCheckpointResult, } from '@aztec/stdlib/block'; import { type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; -import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; +import { type L1RollupConstants, getEpochAtSlot } from '@aztec/stdlib/epoch-helpers'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees'; import { @@ -871,7 +871,10 @@ export class BlockStore { * @param txHash - The hash of a tx we try to get the receipt for. * @returns The requested tx receipt (or undefined if not found). */ - async getSettledTxReceipt(txHash: TxHash): Promise { + async getSettledTxReceipt( + txHash: TxHash, + l1Constants?: Pick, + ): Promise { const txEffect = await this.getTxEffect(txHash); if (!txEffect) { return undefined; @@ -880,10 +883,11 @@ export class BlockStore { const blockNumber = BlockNumber(txEffect.l2BlockNumber); // Use existing archiver methods to determine finalization level - const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber] = await Promise.all([ + const [provenBlockNumber, checkpointedBlockNumber, finalizedBlockNumber, blockData] = await Promise.all([ this.getProvenBlockNumber(), this.getCheckpointedL2BlockNumber(), this.getFinalizedL2BlockNumber(), + this.getBlockData(blockNumber), ]); let status: TxStatus; @@ -897,6 +901,9 @@ export class BlockStore { status = TxStatus.PROPOSED; } + const epochNumber = + blockData && l1Constants ? getEpochAtSlot(blockData.header.globalVariables.slotNumber, l1Constants) : undefined; + return new TxReceipt( txHash, status, @@ -905,6 +912,7 @@ export class BlockStore { txEffect.data.transactionFee.toBigInt(), txEffect.l2BlockHash, blockNumber, + epochNumber, ); } diff --git a/yarn-project/archiver/src/store/kv_archiver_store.test.ts b/yarn-project/archiver/src/store/kv_archiver_store.test.ts index d05044ded8d2..4a2f71052e4c 100644 --- a/yarn-project/archiver/src/store/kv_archiver_store.test.ts +++ b/yarn-project/archiver/src/store/kv_archiver_store.test.ts @@ -2796,6 +2796,24 @@ describe('KVArchiverDataStore', () => { } }); + it('"tag" filter param is respected', async () => { + // Get a random tag from the logs + const targetBlockIndex = randomInt(numBlocksForPublicLogs); + const targetBlock = publishedCheckpoints[targetBlockIndex].checkpoint.blocks[0]; + const targetTxIndex = randomInt(getTxsPerBlock(targetBlock)); + const targetLogIndex = randomInt(getPublicLogsPerTx(targetBlock, targetTxIndex)); + const targetTag = targetBlock.body.txEffects[targetTxIndex].publicLogs[targetLogIndex].fields[0]; + + const response = await store.getPublicLogs({ tag: targetTag }); + + expect(response.maxLogsHit).toBeFalsy(); + expect(response.logs.length).toBeGreaterThan(0); + + for (const extendedLog of response.logs) { + expect(extendedLog.log.fields[0].equals(targetTag)).toBeTruthy(); + } + }); + it('"afterLog" filter param is respected', async () => { // Get a random log as reference const targetBlockIndex = randomInt(numBlocksForPublicLogs); @@ -2831,13 +2849,13 @@ describe('KVArchiverDataStore', () => { } }); - it('"txHash" filter param is ignored when "afterLog" is set', async () => { - // Get random txHash + it('"txHash" filter param is respected when "afterLog" is set', async () => { + // A random txHash should match nothing, even with afterLog set const txHash = TxHash.random(); const afterLog = new LogId(BlockNumber(1), BlockHash.random(), TxHash.random(), 0, 0); const response = await store.getPublicLogs({ txHash, afterLog }); - expect(response.logs.length).toBeGreaterThan(1); + expect(response.logs.length).toBe(0); }); it('intersecting works', async () => { diff --git a/yarn-project/archiver/src/store/kv_archiver_store.ts b/yarn-project/archiver/src/store/kv_archiver_store.ts index d46075e2a588..b4c25793c4da 100644 --- a/yarn-project/archiver/src/store/kv_archiver_store.ts +++ b/yarn-project/archiver/src/store/kv_archiver_store.ts @@ -410,8 +410,11 @@ export class KVArchiverDataStore implements ContractDataSource { * @param txHash - The hash of a tx we try to get the receipt for. * @returns The requested tx receipt (or undefined if not found). */ - getSettledTxReceipt(txHash: TxHash): Promise { - return this.#blockStore.getSettledTxReceipt(txHash); + getSettledTxReceipt( + txHash: TxHash, + l1Constants?: Pick, + ): Promise { + return this.#blockStore.getSettledTxReceipt(txHash, l1Constants); } /** diff --git a/yarn-project/archiver/src/store/log_store.ts b/yarn-project/archiver/src/store/log_store.ts index e389cba458e2..e2230e7c2847 100644 --- a/yarn-project/archiver/src/store/log_store.ts +++ b/yarn-project/archiver/src/store/log_store.ts @@ -588,11 +588,24 @@ export class LogStore { txLogs: PublicLog[], filter: LogFilter = {}, ): boolean { + if (filter.fromBlock && blockNumber < filter.fromBlock) { + return false; + } + if (filter.toBlock && blockNumber >= filter.toBlock) { + return false; + } + if (filter.txHash && !txHash.equals(filter.txHash)) { + return false; + } + let maxLogsHit = false; let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; for (; logIndex < txLogs.length; logIndex++) { const log = txLogs[logIndex]; - if (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) { + if ( + (!filter.contractAddress || log.contractAddress.equals(filter.contractAddress)) && + (!filter.tag || log.fields[0]?.equals(filter.tag)) + ) { results.push( new ExtendedPublicLog(new LogId(BlockNumber(blockNumber), blockHash, txHash, txIndex, logIndex), log), ); @@ -616,6 +629,16 @@ export class LogStore { txLogs: ContractClassLog[], filter: LogFilter = {}, ): boolean { + if (filter.fromBlock && blockNumber < filter.fromBlock) { + return false; + } + if (filter.toBlock && blockNumber >= filter.toBlock) { + return false; + } + if (filter.txHash && !txHash.equals(filter.txHash)) { + return false; + } + let maxLogsHit = false; let logIndex = typeof filter.afterLog?.logIndex === 'number' ? filter.afterLog.logIndex + 1 : 0; for (; logIndex < txLogs.length; logIndex++) { diff --git a/yarn-project/archiver/src/test/mock_l2_block_source.ts b/yarn-project/archiver/src/test/mock_l2_block_source.ts index 4491991066cd..d3f46943b65a 100644 --- a/yarn-project/archiver/src/test/mock_l2_block_source.ts +++ b/yarn-project/archiver/src/test/mock_l2_block_source.ts @@ -18,7 +18,12 @@ import { } from '@aztec/stdlib/block'; import { Checkpoint, type CheckpointData, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract'; -import { EmptyL1RollupConstants, type L1RollupConstants, getSlotRangeForEpoch } from '@aztec/stdlib/epoch-helpers'; +import { + EmptyL1RollupConstants, + type L1RollupConstants, + getEpochAtSlot, + getSlotRangeForEpoch, +} from '@aztec/stdlib/epoch-helpers'; import { computeCheckpointOutHash } from '@aztec/stdlib/messaging'; import { CheckpointHeader } from '@aztec/stdlib/rollup'; import { type BlockHeader, TxExecutionResult, TxHash, TxReceipt, TxStatus } from '@aztec/stdlib/tx'; @@ -394,6 +399,7 @@ export class MockL2BlockSource implements L2BlockSource, ContractDataSource { txEffect.transactionFee.toBigInt(), await block.hash(), block.number, + getEpochAtSlot(block.slot, EmptyL1RollupConstants), ); } } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 242c8204f744..203d2b4f866a 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -720,6 +720,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? []; } + public getCheckpointsDataForEpoch(epochNumber: EpochNumber) { + return this.blockSource.getCheckpointsDataForEpoch(epochNumber); + } + /** * Method to fetch the current min L2 fees. * @returns The current min L2 fees. diff --git a/yarn-project/aztec/src/cli/aztec_start_action.ts b/yarn-project/aztec/src/cli/aztec_start_action.ts index 4304d7160755..a9e3a9590e4a 100644 --- a/yarn-project/aztec/src/cli/aztec_start_action.ts +++ b/yarn-project/aztec/src/cli/aztec_start_action.ts @@ -23,14 +23,14 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg const signalHandlers: Array<() => Promise> = []; const services: NamespacedApiHandlers = {}; const adminServices: NamespacedApiHandlers = {}; + const packageVersion = getPackageVersion(); let config: ChainConfig | undefined = undefined; if (options.localNetwork) { - const cliVersion = getPackageVersion() ?? 'unknown'; const localNetwork = extractNamespacedOptions(options, 'local-network'); localNetwork.testAccounts = true; userLog(`${splash}\n${github}\n\n`); - userLog(`Setting up Aztec local network ${cliVersion}, please stand by...`); + userLog(`Setting up Aztec local network ${packageVersion ?? 'unknown'}, please stand by...`); const { node, stop } = await createLocalNetwork( { @@ -89,13 +89,14 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg installSignalHandlers(debugLogger.info, signalHandlers); const versions = getVersions(config); + const versioningOpts = { packageVersion }; // Start the main JSON-RPC server if (Object.entries(services).length > 0) { const rpcServer = createNamespacedSafeJsonRpcServer(services, { http200OnError: false, log: debugLogger, - middlewares: [getOtelJsonRpcPropagationMiddleware(), getVersioningMiddleware(versions)], + middlewares: [getOtelJsonRpcPropagationMiddleware(), getVersioningMiddleware(versions, versioningOpts)], maxBatchSize: options.rpcMaxBatchSize, maxBodySizeBytes: options.rpcMaxBodySize, }); @@ -105,7 +106,7 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg // If there are any admin services, start a separate JSON-RPC server for them if (Object.entries(adminServices).length > 0) { - const adminMiddlewares = [getOtelJsonRpcPropagationMiddleware(), getVersioningMiddleware(versions)]; + const adminMiddlewares = [getOtelJsonRpcPropagationMiddleware(), getVersioningMiddleware(versions, versioningOpts)]; // Resolve the admin API key (auto-generated and persisted, or opt-out) const apiKeyResolution = await resolveAdminApiKey( diff --git a/yarn-project/aztec/src/cli/cmds/standby.ts b/yarn-project/aztec/src/cli/cmds/standby.ts index c94605c16d80..7dcebef78845 100644 --- a/yarn-project/aztec/src/cli/cmds/standby.ts +++ b/yarn-project/aztec/src/cli/cmds/standby.ts @@ -35,10 +35,33 @@ export async function computeExpectedGenesisRoot(config: GenesisStateConfig, use return { genesisArchiveRoot, prefilledPublicData }; } +async function checkRollupCompatibility( + rollup: RollupContract, + expected: { genesisArchiveRoot: Fr; vkTreeRoot: Fr; protocolContractsHash: Fr }, +): Promise { + const mismatches: string[] = []; + const [l1Genesis, l1Vk, l1Protocol] = await Promise.all([ + rollup.getGenesisArchiveTreeRoot(), + rollup.getVkTreeRoot(), + rollup.getProtocolContractsHash(), + ]); + if (!l1Genesis.equals(expected.genesisArchiveRoot)) { + mismatches.push(`genesis archive root (expected ${expected.genesisArchiveRoot}, got ${l1Genesis})`); + } + if (!l1Vk.equals(expected.vkTreeRoot)) { + mismatches.push(`VK tree root (expected ${expected.vkTreeRoot}, got ${l1Vk})`); + } + if (!l1Protocol.equals(expected.protocolContractsHash)) { + mismatches.push(`protocol contracts hash (expected ${expected.protocolContractsHash}, got ${l1Protocol})`); + } + return mismatches; +} + /** - * Waits until the canonical rollup's genesis archive root matches the expected local genesis root. - * If the rollup is not yet compatible (e.g. during L1 contract upgrades), enters standby mode: - * starts a lightweight HTTP server for K8s liveness probes and polls every 60s until a compatible rollup appears. + * Waits until the canonical rollup's genesis archive root, VK tree root, and protocol contracts hash + * all match the expected local values. If the rollup is not yet compatible (e.g. during L1 contract upgrades), + * enters standby mode: starts a lightweight HTTP server for K8s liveness probes and polls every 60s + * until a compatible rollup appears. */ export async function waitForCompatibleRollup( config: { @@ -47,7 +70,7 @@ export async function waitForCompatibleRollup( l1Contracts: { registryAddress: EthAddress }; rollupVersion?: number; }, - expectedGenesisRoot: Fr, + expected: { genesisArchiveRoot: Fr; vkTreeRoot: Fr; protocolContractsHash: Fr }, port: number | undefined, userLog: LogFn, ): Promise { @@ -58,21 +81,19 @@ export async function waitForCompatibleRollup( const rollupAddress = await registry.getRollupAddress(rollupVersion); const rollup = new RollupContract(publicClient, rollupAddress.toString()); - let l1GenesisRoot: Fr; + let mismatches: string[]; try { - l1GenesisRoot = await rollup.getGenesisArchiveTreeRoot(); + mismatches = await checkRollupCompatibility(rollup, expected); } catch (err: any) { - throw new Error( - `Could not retrieve genesis archive root from canonical rollup at ${rollupAddress}: ${err.message}`, - ); + throw new Error(`Could not retrieve rollup config from canonical rollup at ${rollupAddress}: ${err.message}`); } - if (l1GenesisRoot.equals(expectedGenesisRoot)) { + if (mismatches.length === 0) { return; } userLog( - `Genesis root mismatch: expected ${expectedGenesisRoot}, got ${l1GenesisRoot} from rollup at ${rollupAddress}. ` + + `Rollup at ${rollupAddress} is incompatible: ${mismatches.join('; ')}. ` + `Entering standby mode. Will poll every ${ROLLUP_POLL_INTERVAL_S}s for a compatible rollup...`, ); @@ -85,20 +106,20 @@ export async function waitForCompatibleRollup( const currentRollupAddress = await registry.getRollupAddress(rollupVersion); const currentRollup = new RollupContract(publicClient, currentRollupAddress.toString()); - let currentGenesisRoot: Fr; + let currentMismatches: string[]; try { - currentGenesisRoot = await currentRollup.getGenesisArchiveTreeRoot(); + currentMismatches = await checkRollupCompatibility(currentRollup, expected); } catch { - userLog(`Failed to fetch genesis root from rollup at ${currentRollupAddress}. Retrying...`); + userLog(`Failed to fetch rollup config from rollup at ${currentRollupAddress}. Retrying...`); return undefined; } - if (currentGenesisRoot.equals(expectedGenesisRoot)) { + if (currentMismatches.length === 0) { userLog(`Compatible rollup found at ${currentRollupAddress}. Exiting standby mode.`); return true; } - userLog(`Still waiting. Rollup at ${currentRollupAddress} has genesis root ${currentGenesisRoot}.`); + userLog(`Still waiting. Rollup at ${currentRollupAddress}: ${currentMismatches.join('; ')}.`); return undefined; }, 'compatible rollup', diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts index f84cd10284be..6e32c345341e 100644 --- a/yarn-project/aztec/src/cli/cmds/start_node.ts +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -7,6 +7,8 @@ import { type NetworkNames, SecretValue } from '@aztec/foundation/config'; import type { NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server'; import { Agent, makeUndiciFetch } from '@aztec/foundation/json-rpc/undici'; import type { LogFn } from '@aztec/foundation/log'; +import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; +import { protocolContractsHash } from '@aztec/protocol-contracts'; import { ProvingJobConsumerSchema, createProvingJobBrokerClient } from '@aztec/prover-client/broker'; import { type CliPXEOptions, type PXEConfig, allPxeConfigMappings } from '@aztec/pxe/config'; import { AztecNodeAdminApiSchema, AztecNodeApiSchema } from '@aztec/stdlib/interfaces/client'; @@ -92,7 +94,12 @@ export async function startNode( // Wait for a compatible rollup before proceeding with full L1 config fetch. // This prevents crashes when the canonical rollup hasn't been upgraded yet. - await waitForCompatibleRollup(nodeConfig, genesisArchiveRoot, options.port, userLog); + await waitForCompatibleRollup( + nodeConfig, + { genesisArchiveRoot, vkTreeRoot: getVKTreeRoot(), protocolContractsHash }, + options.port, + userLog, + ); const { addresses, config } = await getL1Config( nodeConfig.l1Contracts.registryAddress, diff --git a/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts b/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts index 3ff5bad42808..1384a2a52373 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts @@ -2,6 +2,8 @@ import { getL1Config } from '@aztec/cli/config'; import { getGenesisStateConfigEnvVars } from '@aztec/ethereum/config'; import type { NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server'; import type { LogFn } from '@aztec/foundation/log'; +import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; +import { protocolContractsHash } from '@aztec/protocol-contracts'; import { type ProverBrokerConfig, ProvingJobBrokerSchema, @@ -38,7 +40,12 @@ export async function startProverBroker( const genesisConfig = getGenesisStateConfigEnvVars(); const { genesisArchiveRoot } = await computeExpectedGenesisRoot(genesisConfig, userLog); - await waitForCompatibleRollup(config, genesisArchiveRoot, options.port, userLog); + await waitForCompatibleRollup( + config, + { genesisArchiveRoot, vkTreeRoot: getVKTreeRoot(), protocolContractsHash }, + options.port, + userLog, + ); const { addresses, config: rollupConfig } = await getL1Config( config.l1Contracts.registryAddress, diff --git a/yarn-project/aztec/src/testing/epoch_test_settler.ts b/yarn-project/aztec/src/testing/epoch_test_settler.ts index ee9cbdbafb9b..c2fa06dcf024 100644 --- a/yarn-project/aztec/src/testing/epoch_test_settler.ts +++ b/yarn-project/aztec/src/testing/epoch_test_settler.ts @@ -4,7 +4,7 @@ import { type EpochNumber, SlotNumber } from '@aztec/foundation/branded-types'; import type { Logger } from '@aztec/foundation/log'; import { EpochMonitor } from '@aztec/prover-node'; import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block'; -import { computeL2ToL1MembershipWitnessFromMessagesInEpoch } from '@aztec/stdlib/messaging'; +import { computeEpochOutHash } from '@aztec/stdlib/messaging'; export class EpochTestSettler { private rollupCheatCodes: RollupCheatCodes; @@ -51,9 +51,8 @@ export class EpochTestSettler { messagesInEpoch[checkpointIndex].push(block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)); } - const [firstMessage] = messagesInEpoch.flat(3); - if (firstMessage) { - const { root: outHash } = computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, firstMessage); + const outHash = computeEpochOutHash(messagesInEpoch); + if (!outHash.isZero()) { await this.rollupCheatCodes.insertOutbox(epoch, outHash.toBigInt()); } else { this.log.info(`No L2 to L1 messages in epoch ${epoch}`); diff --git a/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts b/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts index 3a4b9932e270..5c5243df8c66 100644 --- a/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_token_bridge_tutorial_test.test.ts @@ -7,7 +7,6 @@ import { Fr } from '@aztec/aztec.js/fields'; import { createLogger } from '@aztec/aztec.js/log'; import { createAztecNodeClient, waitForNode } from '@aztec/aztec.js/node'; import { createExtendedL1Client } from '@aztec/ethereum/client'; -import { RollupContract } from '@aztec/ethereum/contracts'; import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract'; import { FeeAssetHandlerAbi, @@ -224,11 +223,7 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { // docs:end:l2-withdraw // docs:start:l1-withdraw - const rollup = new RollupContract(l1Client, l1ContractAddresses.rollupAddress.toString()); - const block = await node.getBlock(l2TxReceipt.blockNumber!); - const epoch = await rollup.getEpochNumberForCheckpoint(block!.checkpointNumber); - - const result = await computeL2ToL1MembershipWitness(node, epoch, l2ToL1Message); + const result = await computeL2ToL1MembershipWitness(node, l2ToL1Message, l2TxReceipt.txHash); if (!result) { throw new Error('L2 to L1 message not found'); } @@ -236,7 +231,7 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => { await l1PortalManager.withdrawFunds( withdrawAmount, EthAddress.fromString(ownerEthAddress), - epoch, + result.epochNumber, result.leafIndex, result.siblingPath, ); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts index d747d3ef6666..1874f64e75d0 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/l2_to_l1.test.ts @@ -3,7 +3,6 @@ import { BatchCall } from '@aztec/aztec.js/contracts'; import { Fr } from '@aztec/aztec.js/fields'; import type { Wallet } from '@aztec/aztec.js/wallet'; import { OutboxContract, RollupContract, type ViemL2ToL1Msg } from '@aztec/ethereum/contracts'; -import { EpochNumber } from '@aztec/foundation/branded-types'; import { OutboxAbi } from '@aztec/l1-artifacts'; import { TestContract } from '@aztec/noir-test-contracts.js/Test'; import { computeL2ToL1MessageHash } from '@aztec/stdlib/hash'; @@ -13,6 +12,7 @@ import { computeL2ToL1MembershipWitness, getL2ToL1MessageLeafId, } from '@aztec/stdlib/messaging'; +import type { TxHash } from '@aztec/stdlib/tx'; import { type Hex, decodeEventLog } from 'viem'; @@ -68,7 +68,7 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { const blockNumber = txReceipt.blockNumber!; // Advance the epoch until the tx is proven since the messages are inserted to the outbox when the epoch is proven. - const epoch = await t.advanceToEpochProven(txReceipt); + await t.advanceToEpochProven(txReceipt); // Check that the block contains the 2 messages. const block = (await aztecNode.getBlock(blockNumber))!; @@ -76,9 +76,9 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { expect(l2ToL1Messages).toStrictEqual([computeMessageLeaf(messages[0]), computeMessageLeaf(messages[1])]); // Consume messages[0]. - await expectConsumeMessageToSucceed(epoch, messages[0]); + await expectConsumeMessageToSucceed(messages[0], txReceipt.txHash); // Consume messages[1]. - await expectConsumeMessageToSucceed(epoch, messages[1]); + await expectConsumeMessageToSucceed(messages[1], txReceipt.txHash); }); // When the block contains a tx with no messages, the zero txOutHash is skipped and won't be included in the top tree. @@ -103,10 +103,10 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { expect(noMessageReceipt.blockNumber).toEqual(withMessageReceipt.blockNumber); // Advance the epoch until the tx is proven since the messages are inserted to the outbox when the epoch is proven. - const epoch = await t.advanceToEpochProven(withMessageReceipt); + await t.advanceToEpochProven(withMessageReceipt); // Consume the message. - await expectConsumeMessageToSucceed(epoch, message); + await expectConsumeMessageToSucceed(message, withMessageReceipt.txHash); }); it('2 txs (balanced), one with 3 messages (unbalanced), one with 4 messages (balanced)', async () => { @@ -139,25 +139,25 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { } // Advance the epoch until the tx is proven since the messages are inserted to the outbox when the epoch is proven. - const epoch = await t.advanceToEpochProven(l2TxReceipt1); + await t.advanceToEpochProven(l2TxReceipt1); // Consume messages in tx0. { // Consume messages[0], which is in the subtree of height 2. const msg = tx0.messages[0]; - await expectConsumeMessageToSucceed(epoch, msg); + await expectConsumeMessageToSucceed(msg, l2TxReceipt0.txHash); } { // Consume messages[2], which is in the subtree of height 1. const msg = tx0.messages[2]; - await expectConsumeMessageToSucceed(epoch, msg); + await expectConsumeMessageToSucceed(msg, l2TxReceipt0.txHash); } // Consume messages in tx1. { // Consume messages[2], which is in the subtree of height 2. const msg = tx1.messages[0]; - await expectConsumeMessageToSucceed(epoch, msg); + await expectConsumeMessageToSucceed(msg, l2TxReceipt1.txHash); } }); @@ -185,32 +185,32 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { expect(l2TxReceipt2.blockNumber).toEqual(blockNumber); // Advance the epoch until the tx is proven since the messages are inserted to the outbox when the epoch is proven. - const epoch = await t.advanceToEpochProven(l2TxReceipt2); + await t.advanceToEpochProven(l2TxReceipt2); // Consume messages in tx0. { // Consume messages[0], which is in the subtree of height 2. const msg = tx0.messages[0]; - await expectConsumeMessageToSucceed(epoch, msg); + await expectConsumeMessageToSucceed(msg, l2TxReceipt0.txHash); } { // Consume messages[2], which is in the subtree of height 1. const msg = tx0.messages[2]; - await expectConsumeMessageToSucceed(epoch, msg); + await expectConsumeMessageToSucceed(msg, l2TxReceipt0.txHash); } // Consume messages in tx1. { // Consume messages[0], which is the tx subtree root. const msg = tx1.messages[0]; - await expectConsumeMessageToSucceed(epoch, msg); + await expectConsumeMessageToSucceed(msg, l2TxReceipt1.txHash); } // Consume messages in tx2. { // Consume messages[1], which is in the subtree of height 1. const msg = tx2.messages[1]; - await expectConsumeMessageToSucceed(epoch, msg); + await expectConsumeMessageToSucceed(msg, l2TxReceipt2.txHash); } }); @@ -252,9 +252,10 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { return { recipients, contents, messages }; } - async function expectConsumeMessageToSucceed(epoch: EpochNumber, msg: ReturnType) { + async function expectConsumeMessageToSucceed(msg: ReturnType, l2TxHash: TxHash) { const msgLeaf = computeMessageLeaf(msg); - const witness = (await computeL2ToL1MembershipWitness(aztecNode, epoch, msgLeaf))!; + const result = (await computeL2ToL1MembershipWitness(aztecNode, msgLeaf, l2TxHash))!; + const { epochNumber: epoch, ...witness } = result; const leafId = getL2ToL1MessageLeafId(witness); const txHash = await outbox.consume( @@ -295,22 +296,17 @@ describe('e2e_cross_chain_messaging l2_to_l1', () => { expect(topics.args.leafId).toBe(leafId); // Ensure we cannot consume the same message again. - await expectConsumeMessageToFail(epoch, msg, witness); + await expectConsumeMessageToFail(msg, result); } async function expectConsumeMessageToFail( - epoch: EpochNumber, msg: ReturnType, - witness?: L2ToL1MembershipWitness, + witness: L2ToL1MembershipWitness, ) { - if (!witness) { - const msgLeaf = computeMessageLeaf(msg); - witness = (await computeL2ToL1MembershipWitness(aztecNode, epoch, msgLeaf))!; - } await expect( outbox.consume( msg, - epoch, + witness.epochNumber, witness.leafIndex, witness.siblingPath.toFields().map(f => f.toString()), ), diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts index 15172407c2e1..2060c5263a68 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts @@ -73,17 +73,17 @@ describe('e2e_cross_chain_messaging token_bridge_private', () => { await crossChainTestHarness.expectPrivateBalanceOnL2(ownerAddress, bridgeAmount - withdrawAmount); // Advance the epoch until the tx is proven since the messages are inserted to the outbox when the epoch is proven. - const epoch = await t.advanceToEpochProven(l2TxReceipt); + await t.advanceToEpochProven(l2TxReceipt); - const l2ToL1MessageResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, l2ToL1Message); + const l2ToL1MessageResult = (await computeL2ToL1MembershipWitness(aztecNode, l2ToL1Message, l2TxReceipt.txHash))!; // Check balance before and after exit. expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); await crossChainTestHarness.withdrawFundsFromBridgeOnL1( withdrawAmount, - epoch, - l2ToL1MessageResult!.leafIndex, - l2ToL1MessageResult!.siblingPath, + l2ToL1MessageResult.epochNumber, + l2ToL1MessageResult.leafIndex, + l2ToL1MessageResult.siblingPath, ); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount + withdrawAmount); }); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts index 0218cdc49857..4f4e4d34cc12 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_public.test.ts @@ -80,17 +80,17 @@ describe('e2e_cross_chain_messaging token_bridge_public', () => { await crossChainTestHarness.expectPublicBalanceOnL2(ownerAddress, afterBalance - withdrawAmount); // Advance the epoch until the tx is proven since the messages are inserted to the outbox when the epoch is proven. - const epoch = await t.advanceToEpochProven(l2TxReceipt); + await t.advanceToEpochProven(l2TxReceipt); - const l2ToL1MessageResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, l2ToL1Message); + const l2ToL1MessageResult = (await computeL2ToL1MembershipWitness(aztecNode, l2ToL1Message, l2TxReceipt.txHash))!; // Check balance before and after exit. expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount); await crossChainTestHarness.withdrawFundsFromBridgeOnL1( withdrawAmount, - epoch, - l2ToL1MessageResult!.leafIndex, - l2ToL1MessageResult!.siblingPath, + l2ToL1MessageResult.epochNumber, + l2ToL1MessageResult.leafIndex, + l2ToL1MessageResult.siblingPath, ); expect(await crossChainTestHarness.getL1BalanceOf(ethAccount)).toBe(l1TokenBalance - bridgeAmount + withdrawAmount); }, 120_000); diff --git a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts index 786b2c9aa7a5..2b3d00198fa4 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts @@ -347,12 +347,9 @@ describe('e2e_p2p_add_rollup', () => { chainId: new Fr(l1Client.chain.id), }); - const rollup = new RollupContract(l1Client, l1ContractAddresses.rollupAddress); - const block = await node.getBlock(l2OutgoingReceipt.blockNumber!); - const epoch = await rollup.getEpochNumberForCheckpoint(block!.checkpointNumber); - - const l2ToL1MessageResult = (await computeL2ToL1MembershipWitness(node, epoch, leaf))!; - const leafId = getL2ToL1MessageLeafId(l2ToL1MessageResult); + const l2ToL1MessageResult = (await computeL2ToL1MembershipWitness(node, leaf, l2OutgoingReceipt.txHash))!; + const { epochNumber: epoch, ...l2ToL1MessageWitness } = l2ToL1MessageResult; + const leafId = getL2ToL1MessageLeafId(l2ToL1MessageWitness); // We need to advance to the next epoch so that the out hash will be set to outbox when the epoch is proven. const cheatcodes = RollupCheatCodes.create(l1RpcUrls, l1ContractAddresses, t.ctx.dateProvider); @@ -374,8 +371,8 @@ describe('e2e_p2p_add_rollup', () => { args: [ l2ToL1Message, BigInt(epoch), - BigInt(l2ToL1MessageResult!.leafIndex), - l2ToL1MessageResult!.siblingPath + BigInt(l2ToL1MessageWitness.leafIndex), + l2ToL1MessageWitness.siblingPath .toBufferArray() .map((buf: Buffer) => `0x${buf.toString('hex')}`) as readonly `0x${string}`[], ], diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 6795cc1bf180..b75f1e8b76d3 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -252,8 +252,12 @@ export const uniswapL1L2TestSuite = ( await wethCrossChainHarness.expectPublicBalanceOnL2(uniswapL2Contract.address, 0n); // Since the outbox is only consumable when the epoch is proven, we need to advance to the next epoch. - const block = await aztecNode.getBlock(l2UniswapInteractionReceipt.blockNumber!); - const epoch = await rollup.getEpochNumberForCheckpoint(block!.checkpointNumber); + const swapResult = (await computeL2ToL1MembershipWitness( + aztecNode, + swapPrivateLeaf, + l2UniswapInteractionReceipt.txHash, + ))!; + const { epochNumber: epoch } = swapResult; await cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1)); await waitForProven(aztecNode, l2UniswapInteractionReceipt, { provenTimeout: 300 }); @@ -262,14 +266,17 @@ export const uniswapL1L2TestSuite = ( const daiL1BalanceOfPortalBeforeSwap = await daiCrossChainHarness.getL1BalanceOf( daiCrossChainHarness.tokenPortalAddress, ); - const swapResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, swapPrivateLeaf); - const withdrawResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, withdrawLeaf); + const withdrawResult = (await computeL2ToL1MembershipWitness( + aztecNode, + withdrawLeaf, + l2UniswapInteractionReceipt.txHash, + ))!; - const swapPrivateL2MessageIndex = swapResult!.leafIndex; - const swapPrivateSiblingPath = swapResult!.siblingPath; + const swapPrivateL2MessageIndex = swapResult.leafIndex; + const swapPrivateSiblingPath = swapResult.siblingPath; - const withdrawL2MessageIndex = withdrawResult!.leafIndex; - const withdrawSiblingPath = withdrawResult!.siblingPath; + const withdrawL2MessageIndex = withdrawResult.leafIndex; + const withdrawSiblingPath = withdrawResult.siblingPath; const withdrawMessageMetadata = { _epoch: BigInt(epoch), @@ -840,16 +847,15 @@ export const uniswapL1L2TestSuite = ( chainId: new Fr(l1Client.chain.id), }); - const block = await aztecNode.getBlock(withdrawReceipt.blockNumber!); - const epoch = await rollup.getEpochNumberForCheckpoint(block!.checkpointNumber); - const swapResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, swapPrivateLeaf); - const withdrawResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, withdrawLeaf); + const swapResult = (await computeL2ToL1MembershipWitness(aztecNode, swapPrivateLeaf, withdrawReceipt.txHash))!; + const { epochNumber: epoch } = swapResult; + const withdrawResult = (await computeL2ToL1MembershipWitness(aztecNode, withdrawLeaf, withdrawReceipt.txHash))!; - const swapPrivateL2MessageIndex = swapResult!.leafIndex; - const swapPrivateSiblingPath = swapResult!.siblingPath; + const swapPrivateL2MessageIndex = swapResult.leafIndex; + const swapPrivateSiblingPath = swapResult.siblingPath; - const withdrawL2MessageIndex = withdrawResult!.leafIndex; - const withdrawSiblingPath = withdrawResult!.siblingPath; + const withdrawL2MessageIndex = withdrawResult.leafIndex; + const withdrawSiblingPath = withdrawResult.siblingPath; const withdrawMessageMetadata = { _epoch: BigInt(epoch), @@ -973,16 +979,15 @@ export const uniswapL1L2TestSuite = ( chainId: new Fr(l1Client.chain.id), }); - const block = await aztecNode.getBlock(withdrawReceipt.blockNumber!); - const epoch = await rollup.getEpochNumberForCheckpoint(block!.checkpointNumber); - const swapResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, swapPublicLeaf); - const withdrawResult = await computeL2ToL1MembershipWitness(aztecNode, epoch, withdrawLeaf); + const swapResult = (await computeL2ToL1MembershipWitness(aztecNode, swapPublicLeaf, withdrawReceipt.txHash))!; + const { epochNumber: epoch } = swapResult; + const withdrawResult = (await computeL2ToL1MembershipWitness(aztecNode, withdrawLeaf, withdrawReceipt.txHash))!; - const swapPublicL2MessageIndex = swapResult!.leafIndex; - const swapPublicSiblingPath = swapResult!.siblingPath; + const swapPublicL2MessageIndex = swapResult.leafIndex; + const swapPublicSiblingPath = swapResult.siblingPath; - const withdrawL2MessageIndex = withdrawResult!.leafIndex; - const withdrawSiblingPath = withdrawResult!.siblingPath; + const withdrawL2MessageIndex = withdrawResult.leafIndex; + const withdrawSiblingPath = withdrawResult.siblingPath; const withdrawMessageMetadata = { _epoch: BigInt(epoch), diff --git a/yarn-project/ethereum/src/contracts/rollup.test.ts b/yarn-project/ethereum/src/contracts/rollup.test.ts index 4c85afacc861..1d844a3c0299 100644 --- a/yarn-project/ethereum/src/contracts/rollup.test.ts +++ b/yarn-project/ethereum/src/contracts/rollup.test.ts @@ -105,6 +105,18 @@ describe('Rollup', () => { }); }); + describe('getVkTreeRoot and getProtocolContractsHash', () => { + it('reads vkTreeRoot from storage', async () => { + const result = await rollup.getVkTreeRoot(); + expect(result).toEqual(vkTreeRoot); + }); + + it('reads protocolContractsHash from storage', async () => { + const result = await rollup.getProtocolContractsHash(); + expect(result).toEqual(protocolContractsHash); + }); + }); + describe('getSlashingProposer', () => { it('returns a slashing proposer', async () => { const slashingProposer = await rollup.getSlashingProposer(); diff --git a/yarn-project/ethereum/src/contracts/rollup.ts b/yarn-project/ethereum/src/contracts/rollup.ts index 8132c2bfa451..5eb9d850ae95 100644 --- a/yarn-project/ethereum/src/contracts/rollup.ts +++ b/yarn-project/ethereum/src/contracts/rollup.ts @@ -379,6 +379,20 @@ export class RollupContract { return Fr.fromString(await this.rollup.read.archiveAt([0n])); } + @memoize + async getVkTreeRoot(): Promise { + const slot = BigInt(RollupContract.stfStorageSlot) + 3n; + const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` }); + return Fr.fromString(value ?? '0x0'); + } + + @memoize + async getProtocolContractsHash(): Promise { + const slot = BigInt(RollupContract.stfStorageSlot) + 4n; + const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` }); + return Fr.fromString(value ?? '0x0'); + } + /** * Returns rollup constants used for epoch queries. * Return type is `L1RollupConstants` which is defined in stdlib, diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index a038867350b2..1dd893fe6af6 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -14,6 +14,7 @@ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundatio import { promiseWithResolvers } from '@aztec/foundation/promise'; import { SerialQueue } from '@aztec/foundation/queue'; import { assertLength } from '@aztec/foundation/serialize'; +import { sleep } from '@aztec/foundation/sleep'; import { pushTestData } from '@aztec/foundation/testing'; import { elapsed } from '@aztec/foundation/timer'; import type { TreeNodeLocation } from '@aztec/foundation/trees'; @@ -634,9 +635,11 @@ export class ProvingOrchestrator implements EpochProver { } }; - // Enqueue onto the serial queue with limited workers to avoid starving the event loop. - // Workers yield between jobs via await, allowing I/O callbacks to process. - void this.deferredJobQueue.put(() => safeJob()); + void this.deferredJobQueue.put(async () => { + void safeJob(); + // we yield here to the macro task queue such to give Nodejs a chance to run other operatoins in between enqueues + await sleep(0); + }); } private async updateL1ToL2MessageTree(l1ToL2Messages: Fr[], db: MerkleTreeWriteOperations) { diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_rollup_structure.test.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_rollup_structure.test.ts index 5ca5d0bcbe66..3ceb5715adc2 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator_rollup_structure.test.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_rollup_structure.test.ts @@ -7,7 +7,7 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import { Gas, GasFees } from '@aztec/stdlib/gas'; -import { ScopedL2ToL1Message, computeL2ToL1MembershipWitnessFromMessagesInEpoch } from '@aztec/stdlib/messaging'; +import { ScopedL2ToL1Message, computeEpochOutHash } from '@aztec/stdlib/messaging'; import { FeeRecipient } from '@aztec/stdlib/rollup'; import type { ServerCircuitName } from '@aztec/stdlib/stats'; import { makeScopedL2ToL1Message } from '@aztec/stdlib/testing'; @@ -150,11 +150,7 @@ describe('prover/orchestrator/rollup-structure', () => { const epochEndArchive = await getTreeSnapshot(MerkleTreeId.ARCHIVE, await context.worldState.fork()); expect(result.publicInputs.endArchiveRoot).toEqual(epochEndArchive.root); - const firstMessage = l1ToL2MessagesInEpoch.flat(4)[0]; - const { root: epochOutHash } = computeL2ToL1MembershipWitnessFromMessagesInEpoch( - l1ToL2MessagesInEpoch, - firstMessage, - ); + const epochOutHash = computeEpochOutHash(l1ToL2MessagesInEpoch); expect(result.publicInputs.outHash).toEqual(epochOutHash); const expectedCheckpointHeaderHashes = checkpoints.map(c => c.header.hash()); diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts index 9afac73c16d5..71eef7dca702 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts @@ -254,6 +254,11 @@ describe('AztecNodeApiSchema', () => { expect(response).toEqual([]); }); + it('getCheckpointsDataForEpoch', async () => { + const response = await context.client.getCheckpointsDataForEpoch(EpochNumber(1)); + expect(response).toEqual([]); + }); + it('getNodeVersion', async () => { const response = await context.client.getNodeVersion(); expect(response).toBe('1.0.0'); @@ -537,6 +542,10 @@ class MockAztecNode implements AztecNode { return Promise.resolve([]); } + getCheckpointsDataForEpoch(_epochNumber: EpochNumber) { + return Promise.resolve([]); + } + findLeavesIndexes( referenceBlock: BlockParameter, treeId: MerkleTreeId, diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.ts b/yarn-project/stdlib/src/interfaces/aztec-node.ts index 7f613d9e6891..94ec70a55654 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.ts @@ -25,6 +25,7 @@ import { CheckpointedL2Block } from '../block/checkpointed_l2_block.js'; import { type DataInBlock, dataInBlockSchemaFor } from '../block/in_block.js'; import { L2Block } from '../block/l2_block.js'; import { type L2BlockSource, type L2Tips, L2TipsSchema } from '../block/l2_block_source.js'; +import { CheckpointDataSchema } from '../checkpoint/checkpoint_data.js'; import { PublishedCheckpoint } from '../checkpoint/published_checkpoint.js'; import { type ContractClassPublic, @@ -74,7 +75,12 @@ import { type WorldStateSyncStatus, WorldStateSyncStatusSchema } from './world_s export interface AztecNode extends Pick< L2BlockSource, - 'getBlocks' | 'getCheckpoints' | 'getBlockHeader' | 'getL2Tips' | 'getCheckpointedBlocks' + | 'getBlocks' + | 'getCheckpoints' + | 'getBlockHeader' + | 'getL2Tips' + | 'getCheckpointedBlocks' + | 'getCheckpointsDataForEpoch' > { /** * Returns the tips of the L2 chain. @@ -567,6 +573,8 @@ export const AztecNodeApiSchema: ApiSchemaFor = { .args(BlockNumberPositiveSchema, z.number().gt(0).lte(MAX_RPC_BLOCKS_LEN)) .returns(z.array(CheckpointedL2Block.schema)), + getCheckpointsDataForEpoch: z.function().args(EpochNumberSchema).returns(z.array(CheckpointDataSchema)), + getCurrentMinFees: z.function().returns(GasFees.schema), getMaxPriorityFees: z.function().returns(GasFees.schema), diff --git a/yarn-project/stdlib/src/interfaces/prover-client.ts b/yarn-project/stdlib/src/interfaces/prover-client.ts index 45a9c5b6e65f..941f816216c3 100644 --- a/yarn-project/stdlib/src/interfaces/prover-client.ts +++ b/yarn-project/stdlib/src/interfaces/prover-client.ts @@ -113,7 +113,7 @@ export const proverConfigMappings: ConfigMappingsType = { enqueueConcurrency: { env: 'PROVER_ENQUEUE_CONCURRENCY', description: 'Max concurrent jobs the orchestrator serializes and enqueues to the broker.', - ...numberConfigHelper(10), + ...numberConfigHelper(50), }, }; diff --git a/yarn-project/stdlib/src/logs/log_filter.ts b/yarn-project/stdlib/src/logs/log_filter.ts index d2c191a803a9..75f97cd1ee20 100644 --- a/yarn-project/stdlib/src/logs/log_filter.ts +++ b/yarn-project/stdlib/src/logs/log_filter.ts @@ -1,3 +1,5 @@ +import type { Fr } from '@aztec/foundation/curves/bn254'; + import { z } from 'zod'; import type { AztecAddress } from '../aztec-address/index.js'; @@ -20,6 +22,8 @@ export type LogFilter = { afterLog?: LogId; /** The contract address to filter logs by. */ contractAddress?: AztecAddress; + /** The tag (first field of the log) to filter logs by. */ + tag?: Fr; }; export const LogFilterSchema: ZodFor = z.object({ @@ -28,4 +32,5 @@ export const LogFilterSchema: ZodFor = z.object({ toBlock: schemas.Integer.optional(), afterLog: LogId.schema.optional(), contractAddress: schemas.AztecAddress.optional(), + tag: schemas.Fr.optional(), }); diff --git a/yarn-project/stdlib/src/messaging/l2_to_l1_membership.test.ts b/yarn-project/stdlib/src/messaging/l2_to_l1_membership.test.ts index b39cd2e2c7d6..2a6775a64160 100644 --- a/yarn-project/stdlib/src/messaging/l2_to_l1_membership.test.ts +++ b/yarn-project/stdlib/src/messaging/l2_to_l1_membership.test.ts @@ -23,7 +23,7 @@ describe('L2 to L1 membership', () => { const hasher = (left: Buffer, right: Buffer) => sha256Trunc(Buffer.concat([left, right])); // This should match the implementation in Outbox.sol -> verifyMembership - const verifyMembership = (leaf: Fr, witness: L2ToL1MembershipWitness) => { + const verifyMembership = (leaf: Fr, witness: Pick) => { let subtreeRoot = leaf.toBuffer(); let indexAtHeight = witness.leafIndex; const path = witness.siblingPath.toBufferArray(); @@ -35,24 +35,33 @@ describe('L2 to L1 membership', () => { expect(subtreeRoot).toEqual(witness.root.toBuffer()); }; - const verifyMembershipForMessagesInEpoch = (messagesInEpoch: Fr[][][][]): L2ToL1MembershipWitness[] => { + const verifyMembershipForMessagesInEpoch = (messagesInEpoch: Fr[][][][]) => { let root = Fr.ZERO; - const messages = messagesInEpoch.flat(3); - const witnesses = messages.map((msg, i) => { - const witness = computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg); - const leafId = getL2ToL1MessageLeafId(witness); - expect(foundLeafIds.has(leafId)).toBe(false); - foundLeafIds.add(leafId); - verifyMembership(msg, witness); - - if (i === 0) { - root = witness.root; - } else { - expect(witness.root).toEqual(root); + const witnesses: ReturnType[] = []; + let isFirst = true; + for (let ci = 0; ci < messagesInEpoch.length; ci++) { + for (let bi = 0; bi < messagesInEpoch[ci].length; bi++) { + for (let ti = 0; ti < messagesInEpoch[ci][bi].length; ti++) { + for (let mi = 0; mi < messagesInEpoch[ci][bi][ti].length; mi++) { + const msg = messagesInEpoch[ci][bi][ti][mi]; + const witness = computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg, ci, bi, ti, mi); + const leafId = getL2ToL1MessageLeafId(witness); + expect(foundLeafIds.has(leafId)).toBe(false); + foundLeafIds.add(leafId); + verifyMembership(msg, witness); + + if (isFirst) { + root = witness.root; + isFirst = false; + } else { + expect(witness.root).toEqual(root); + } + + witnesses.push(witness); + } + } } - - return witness; - }); + } const computedRoot = computeEpochOutHash(messagesInEpoch); expect(root).toEqual(computedRoot); @@ -68,11 +77,38 @@ describe('L2 to L1 membership', () => { it('throws if the message is not found', () => { const messagesInEpoch = [[[msgHashes(3), msgHashes(1)]]]; const targetMsg = Fr.random(); - expect(() => computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, targetMsg)).toThrow( + expect(() => computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, targetMsg, 0, 0, 0)).toThrow( 'The L2ToL1Message you are trying to prove inclusion of does not exist', ); }); + it('throws if duplicate messages exist in tx without explicit index', () => { + const msg = Fr.random(); + const messagesInEpoch = [[[[msg, msg, Fr.random()]]]]; + expect(() => computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg, 0, 0, 0)).toThrow( + 'Multiple messages with the same value', + ); + }); + + it('succeeds with explicit index for duplicate messages', () => { + const msg = Fr.random(); + const messagesInEpoch = [[[[msg, msg, Fr.random()]]]]; + const witness0 = computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg, 0, 0, 0, 0); + const witness1 = computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg, 0, 0, 0, 1); + expect(witness0.leafIndex).not.toEqual(witness1.leafIndex); + verifyMembership(msg, witness0); + verifyMembership(msg, witness1); + }); + + it('throws if explicit index does not match the message', () => { + const msg = Fr.random(); + const otherMsg = Fr.random(); + const messagesInEpoch = [[[[otherMsg, msg]]]]; + expect(() => computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg, 0, 0, 0, 0)).toThrow( + 'Message at index 0 in tx does not match the expected message', + ); + }); + it('a single tx with 1 message', () => { const txMessages = msgHashes(1); const messagesInEpoch = [[[txMessages]]]; @@ -802,7 +838,7 @@ describe('L2 to L1 membership', () => { [[[], []]], [[[], []], [[]], [[], [], []], [[], []]], ]; - const witness = computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg); + const witness = computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, msg, 2, 2, 0, 0); expect(witness.leafIndex).toBe(2n); // The message is the root of the second checkpoint. expect(witness.siblingPath.pathSize).toBe(epochTopTreeDepth); }); diff --git a/yarn-project/stdlib/src/messaging/l2_to_l1_membership.ts b/yarn-project/stdlib/src/messaging/l2_to_l1_membership.ts index e4964abdbaeb..7dbc9eb5c439 100644 --- a/yarn-project/stdlib/src/messaging/l2_to_l1_membership.ts +++ b/yarn-project/stdlib/src/messaging/l2_to_l1_membership.ts @@ -3,6 +3,9 @@ import type { EpochNumber } from '@aztec/foundation/branded-types'; import { Fr } from '@aztec/foundation/curves/bn254'; import { SiblingPath, UnbalancedMerkleTreeCalculator, computeUnbalancedShaRoot } from '@aztec/foundation/trees'; +import type { AztecNode } from '../interfaces/aztec-node.js'; +import { TxHash } from '../tx/tx_hash.js'; + /** * # L2-to-L1 Message Tree Structure and Leaf IDs * @@ -92,59 +95,94 @@ export function getL2ToL1MessageLeafId( return 2n ** BigInt(membershipWitness.siblingPath.pathSize) + membershipWitness.leafIndex; } -export interface MessageRetrieval { - getL2ToL1Messages(epoch: EpochNumber): Promise; -} - export type L2ToL1MembershipWitness = { root: Fr; leafIndex: bigint; siblingPath: SiblingPath; + epochNumber: EpochNumber; }; +/** + * Computes the L2 to L1 membership witness for a given message in a transaction. + * + * @param node - The Aztec node to query for block/tx/epoch data. + * @param message - The L2 to L1 message hash to prove membership of. + * @param txHash - The hash of the transaction that emitted the message. + * @param messageIndexInTx - Optional index of the message within the transaction's L2-to-L1 messages. + * If not provided, the message is found by scanning the tx's messages (throws if duplicates exist). + * @returns The membership witness and epoch number, or undefined if the tx is not yet in a block/epoch. + */ export async function computeL2ToL1MembershipWitness( - messageRetriever: MessageRetrieval, - epoch: EpochNumber, + node: Pick< + AztecNode, + 'getL2ToL1Messages' | 'getTxReceipt' | 'getTxEffect' | 'getBlock' | 'getCheckpointsDataForEpoch' + >, message: Fr, + txHash: TxHash, + messageIndexInTx?: number, ): Promise { - const messagesInEpoch = await messageRetriever.getL2ToL1Messages(epoch); - if (messagesInEpoch.length === 0) { + const { epochNumber, blockNumber } = await node.getTxReceipt(txHash); + if (epochNumber === undefined || blockNumber === undefined) { + return undefined; + } + + const [messagesInEpoch, block, txEffect, checkpointsData] = await Promise.all([ + node.getL2ToL1Messages(epochNumber), + node.getBlock(blockNumber), + node.getTxEffect(txHash), + node.getCheckpointsDataForEpoch(epochNumber), + ]); + + if (messagesInEpoch.length === 0 || !block || !txEffect) { + return undefined; + } + + const checkpointIndex = checkpointsData.findIndex(c => c.checkpointNumber === block.checkpointNumber); + if (checkpointIndex === -1) { return undefined; } - return computeL2ToL1MembershipWitnessFromMessagesInEpoch(messagesInEpoch, message); + const blockIndex = block.indexWithinCheckpoint; + const txIndex = txEffect.txIndexInBlock; + + const { root, leafIndex, siblingPath } = computeL2ToL1MembershipWitnessFromMessagesInEpoch( + messagesInEpoch, + message, + checkpointIndex, + blockIndex, + txIndex, + messageIndexInTx, + ); + return { epochNumber, root, leafIndex, siblingPath }; } -// TODO: Allow to specify the message to consume by its index or by an offset, in case there are multiple messages with -// the same value. +/** + * Computes a membership witness for a message in the epoch's L2-to-L1 message tree, given explicit position indices. + * + * @param messagesInEpoch - All L2-to-L1 messages in the epoch, organized as checkpoints → blocks → txs → messages. + * @param message - The message hash to prove membership of. + * @param checkpointIndex - Index of the checkpoint within the epoch's message array. + * @param blockIndex - Index of the block within the checkpoint. + * @param txIndex - Index of the transaction within the block. + * @param messageIndexInTx - Optional index of the message within the transaction's messages. + * If not provided, the message is found by scanning (throws if duplicates exist within the tx). + */ +/** @internal Exported for testing only. */ export function computeL2ToL1MembershipWitnessFromMessagesInEpoch( messagesInEpoch: Fr[][][][], message: Fr, -): L2ToL1MembershipWitness { - // Find the index of the message in the tx, index of the tx in the block, and index of the block in the epoch. - let messageIndexInTx = -1; - let txIndex = -1; - let blockIndex = -1; - const checkpointIndex = messagesInEpoch.findIndex(messagesInCheckpoint => { - blockIndex = messagesInCheckpoint.findIndex(messagesInBlock => { - txIndex = messagesInBlock.findIndex(messagesInTx => { - messageIndexInTx = messagesInTx.findIndex(msg => msg.equals(message)); - return messageIndexInTx !== -1; - }); - return txIndex !== -1; - }); - return blockIndex !== -1; - }); - - if (checkpointIndex === -1) { - throw new Error('The L2ToL1Message you are trying to prove inclusion of does not exist'); - } + checkpointIndex: number, + blockIndex: number, + txIndex: number, + messageIndexInTx?: number, +): { root: Fr; leafIndex: bigint; siblingPath: SiblingPath } { + const messagesInTx = messagesInEpoch[checkpointIndex][blockIndex][txIndex]; + const resolvedMessageIndex = resolveMessageIndex(messagesInTx, message, messageIndexInTx); // Build the tx tree. - const messagesInTx = messagesInEpoch[checkpointIndex][blockIndex][txIndex]; const txTree = UnbalancedMerkleTreeCalculator.create(messagesInTx.map(msg => msg.toBuffer())); // Get the sibling path of the target message in the tx tree. - const pathToMessageInTxSubtree = txTree.getSiblingPathByLeafIndex(messageIndexInTx); + const pathToMessageInTxSubtree = txTree.getSiblingPathByLeafIndex(resolvedMessageIndex); // Build the tree of the block containing the target message. const blockTree = buildBlockTree(messagesInEpoch[checkpointIndex][blockIndex]); @@ -189,7 +227,7 @@ export function computeL2ToL1MembershipWitnessFromMessagesInEpoch( // Compute the combined index. // It is the index of the message in the balanced tree (by filling up the wonky tree with empty nodes) at its current // height. It's used to validate the membership proof. - const messageLeafPosition = txTree.getLeafLocation(messageIndexInTx); + const messageLeafPosition = txTree.getLeafLocation(resolvedMessageIndex); const txLeafPosition = blockTree.getLeafLocation(txIndex); const blockLeafPosition = checkpointTree.getLeafLocation(blockIndex); const checkpointLeafPosition = epochTree.getLeafLocation(checkpointIndex); @@ -207,6 +245,33 @@ export function computeL2ToL1MembershipWitnessFromMessagesInEpoch( }; } +function resolveMessageIndex(messagesInTx: Fr[], message: Fr, messageIndexInTx?: number): number { + if (messageIndexInTx !== undefined) { + if (!messagesInTx[messageIndexInTx]?.equals(message)) { + throw new Error(`Message at index ${messageIndexInTx} in tx does not match the expected message ${message}`); + } + return messageIndexInTx; + } + + const indices = messagesInTx.reduce((acc, msg, i) => { + if (msg.equals(message)) { + acc.push(i); + } + return acc; + }, []); + + if (indices.length === 0) { + throw new Error('The L2ToL1Message you are trying to prove inclusion of does not exist'); + } + if (indices.length > 1) { + throw new Error( + `Multiple messages with the same value ${message} found in tx (indices: ${indices.join(', ')}). ` + + `Provide messageIndexInTx to disambiguate.`, + ); + } + return indices[0]; +} + function buildCheckpointTree(messagesInCheckpoint: Fr[][][]) { const blockOutHashes = messagesInCheckpoint.map(messagesInBlock => buildBlockTree(messagesInBlock).getRoot()); return buildCompressedTree(blockOutHashes); diff --git a/yarn-project/stdlib/src/tx/tx_receipt.test.ts b/yarn-project/stdlib/src/tx/tx_receipt.test.ts index 9fe953452242..8be605399c4f 100644 --- a/yarn-project/stdlib/src/tx/tx_receipt.test.ts +++ b/yarn-project/stdlib/src/tx/tx_receipt.test.ts @@ -1,4 +1,4 @@ -import { BlockNumber } from '@aztec/foundation/branded-types'; +import { BlockNumber, EpochNumber } from '@aztec/foundation/branded-types'; import { jsonStringify } from '@aztec/foundation/json-rpc'; import { BlockHash } from '../block/block_hash.js'; @@ -15,6 +15,7 @@ describe('TxReceipt', () => { 1n, BlockHash.random(), BlockNumber(1), + EpochNumber(3), ); expect(TxReceipt.schema.parse(JSON.parse(jsonStringify(receipt)))).toEqual(receipt); diff --git a/yarn-project/stdlib/src/tx/tx_receipt.ts b/yarn-project/stdlib/src/tx/tx_receipt.ts index 3b67b0057ba5..8a4796e04048 100644 --- a/yarn-project/stdlib/src/tx/tx_receipt.ts +++ b/yarn-project/stdlib/src/tx/tx_receipt.ts @@ -1,4 +1,4 @@ -import { BlockNumber, BlockNumberSchema } from '@aztec/foundation/branded-types'; +import { BlockNumber, BlockNumberSchema, EpochNumber, EpochNumberSchema } from '@aztec/foundation/branded-types'; import { z } from 'zod'; @@ -58,6 +58,8 @@ export class TxReceipt { public blockHash?: BlockHash, /** The block number in which the transaction was included. */ public blockNumber?: BlockNumber, + /** The epoch number in which the transaction was included. */ + public epochNumber?: EpochNumber, /** * Debug logs collected during public function execution. Served only when the node is in test mode and placed on * the receipt only because it's a convenient place for it (the logs are printed out by the wallet when a mined @@ -109,6 +111,7 @@ export class TxReceipt { error: z.string().optional(), blockHash: BlockHash.schema.optional(), blockNumber: BlockNumberSchema.optional(), + epochNumber: EpochNumberSchema.optional(), transactionFee: schemas.BigInt.optional(), debugLogs: z.array(DebugLog.schema).optional(), }) @@ -123,6 +126,7 @@ export class TxReceipt { transactionFee?: bigint; blockHash?: BlockHash; blockNumber?: BlockNumber; + epochNumber?: EpochNumber; debugLogs?: DebugLog[]; }) { return new TxReceipt( @@ -133,6 +137,7 @@ export class TxReceipt { fields.transactionFee, fields.blockHash, fields.blockNumber, + fields.epochNumber, fields.debugLogs, ); } diff --git a/yarn-project/stdlib/src/versioning/versioning.ts b/yarn-project/stdlib/src/versioning/versioning.ts index 86926603f306..32efb4da71de 100644 --- a/yarn-project/stdlib/src/versioning/versioning.ts +++ b/yarn-project/stdlib/src/versioning/versioning.ts @@ -115,7 +115,7 @@ export function validatePartialComponentVersionsMatch( } /** Returns a Koa middleware that injects the versioning info as headers. */ -export function getVersioningMiddleware(versions: Partial) { +export function getVersioningMiddleware(versions: Partial, opts?: { packageVersion?: string }) { return async (ctx: Koa.Context, next: () => Promise) => { try { await next(); @@ -128,6 +128,9 @@ export function getVersioningMiddleware(versions: Partial) { ctx.set(`x-aztec-${key}`, value.toString()); } } + if (opts?.packageVersion) { + ctx.set('x-aztec-packageVersion', opts.packageVersion); + } } }; } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index c7882d55d56a..61130cb43aa6 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -1904,6 +1904,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/kv-store": "workspace:^" "@aztec/l1-artifacts": "workspace:^" + "@aztec/native": "workspace:^" "@aztec/node-keystore": "workspace:^" "@aztec/node-lib": "workspace:^" "@aztec/noir-protocol-circuits-types": "workspace:^"