diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index a0d1d73bdda..b743cb11f59 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { "core": "25.0.0", - "prover": "16.5.0", + "prover": "17.0.0", "zkstack_cli": "0.1.2" } diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index fb43133868b..c245e7341d0 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -276,8 +276,8 @@ jobs: --wallet-creation localhost \ --l1-batch-commit-data-generator-mode rollup \ --base-token-address ${{ env.CUSTOM_TOKEN_ADDRESS }} \ - --base-token-price-nominator 3 \ - --base-token-price-denominator 2 \ + --base-token-price-nominator 314 \ + --base-token-price-denominator 1000 \ --set-as-default false \ --ignore-prerequisites @@ -332,8 +332,8 @@ jobs: --wallet-creation localhost \ --l1-batch-commit-data-generator-mode validium \ --base-token-address ${{ env.CUSTOM_TOKEN_ADDRESS }} \ - --base-token-price-nominator 3 \ - --base-token-price-denominator 2 \ + --base-token-price-nominator 314 \ + --base-token-price-denominator 1000 \ --set-as-default false \ --ignore-prerequisites diff --git a/Cargo.lock b/Cargo.lock index 4160b66ed1d..8af4e90323c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -101,6 +101,16 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "alloy-rlp" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" +dependencies = [ + "arrayvec 0.7.6", + "bytes", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -133,9 +143,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -148,43 +158,167 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] [[package]] name = "arr_macro" @@ -294,9 +428,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.11" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" +checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" dependencies = [ "futures-core", "memchr", @@ -420,9 +554,9 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -473,9 +607,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -484,13 +618,13 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -505,9 +639,9 @@ version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -542,17 +676,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-rs" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f95446d919226d587817a7d21379e6eb099b97b45110a7f272a444ca5c54070" +checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" dependencies = [ "aws-lc-sys", "mirai-annotations", @@ -562,11 +707,11 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ddc4a5b231dd6958b140ff3151b6412b3f4321fab354f399eec8f14b06df62" +checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" dependencies = [ - "bindgen 0.69.4", + "bindgen 0.69.5", "cc", "cmake", "dunce", @@ -577,18 +722,46 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.6" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.3.4", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.31", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 0.1.2", + "tower 0.4.13", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +dependencies = [ + "async-trait", + "axum-core 0.4.5", "bytes", "futures-util", "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "itoa", "matchit", @@ -612,9 +785,26 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.4" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -658,6 +848,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.1.1" @@ -711,6 +907,12 @@ dependencies = [ "regex", ] +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "beef" version = "0.5.2" @@ -722,9 +924,9 @@ dependencies = [ [[package]] name = "bigdecimal" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" +checksum = "8f850665a0385e070b64c38d2354e6c104c8479c59868d1e48a0c13ee2c7a1c1" dependencies = [ "autocfg", "libm", @@ -755,19 +957,19 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "regex", "rustc-hash", "shlex", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags 2.6.0", "cexpr", @@ -777,20 +979,20 @@ dependencies = [ "lazycell", "log", "prettyplease", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "regex", "rustc-hash", "shlex", - "syn 2.0.77", + "syn 2.0.85", "which", ] [[package]] name = "bip39" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" +checksum = "33415e24172c1b7d6066f6d999545375ab8e1d95421d6784bdfff9496f292387" dependencies = [ "bitcoin_hashes", "serde", @@ -815,11 +1017,21 @@ dependencies = [ "serde", ] +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + [[package]] name = "bitcoin_hashes" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "hex-conservative", +] [[package]] name = "bitflags" @@ -986,6 +1198,18 @@ dependencies = [ "piper", ] +[[package]] +name = "blockstore" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7679095248a6dc7555fae81154ed1baef264383c16621ef881a219576c72a9be" +dependencies = [ + "cid", + "dashmap 6.1.0", + "multihash", + "thiserror", +] + [[package]] name = "blst" version = "0.3.13" @@ -1047,9 +1271,9 @@ checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" dependencies = [ "once_cell", "proc-macro-crate 3.2.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", "syn_derive", ] @@ -1097,7 +1321,7 @@ version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -1116,9 +1340,12 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +dependencies = [ + "serde", +] [[package]] name = "bytesize" @@ -1163,7 +1390,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.23", "serde", "serde_json", ] @@ -1176,15 +1403,105 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.21" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", "shlex", ] +[[package]] +name = "celestia-proto" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6eb26c852e42015f85f3aed5c3d1472c751b143e2199d0401ebac2f4500b20d" +dependencies = [ + "celestia-tendermint-proto", + "prost 0.12.6", + "prost-build", + "prost-types", + "protox 0.6.1", + "serde", +] + +[[package]] +name = "celestia-tendermint" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8c92a01145f79a0f3ac7c44a43a9b5ee58e8a4c716b56d98833a3848db1afd" +dependencies = [ + "bytes", + "celestia-tendermint-proto", + "digest 0.10.7", + "ed25519", + "ed25519-consensus", + "flex-error", + "futures 0.3.31", + "num-traits", + "once_cell", + "prost 0.12.6", + "prost-types", + "serde", + "serde_bytes", + "serde_json", + "serde_repr", + "sha2 0.10.8", + "signature 2.2.0", + "subtle", + "subtle-encoding", + "time", + "zeroize", +] + +[[package]] +name = "celestia-tendermint-proto" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a95746c5221a74d7b913a415fdbb9e7c90e1b4d818dbbff59bddc034cfce2ec" +dependencies = [ + "bytes", + "flex-error", + "num-derive 0.3.3", + "num-traits", + "prost 0.12.6", + "prost-types", + "serde", + "serde_bytes", + "subtle-encoding", + "time", +] + +[[package]] +name = "celestia-types" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf52cc4b4cdf73fc07d9eeaea6d27bb39eed81f4bf8c89f01df86ace4e6da10" +dependencies = [ + "base64 0.22.1", + "bech32", + "blockstore", + "bytes", + "celestia-proto", + "celestia-tendermint", + "celestia-tendermint-proto", + "cid", + "const_format", + "enum_dispatch", + "leopard-codec", + "libp2p-identity", + "multiaddr", + "multihash", + "nmt-rs", + "ruint", + "serde", + "serde_repr", + "sha2 0.10.8", + "thiserror", + "time", +] + [[package]] name = "cesu8" version = "1.1.0" @@ -1278,6 +1595,18 @@ dependencies = [ "half", ] +[[package]] +name = "cid" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3147d8272e8fa0ccd29ce51194dd98f79ddfb8191ba9e3409884e751798acf3a" +dependencies = [ + "core2", + "multibase", + "multihash", + "unsigned-varint", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1473,9 +1802,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1513,9 +1842,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "combine" @@ -1584,7 +1913,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "unicode-xid 0.2.6", ] @@ -1632,6 +1961,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.14" @@ -1815,7 +2153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1848,7 +2186,7 @@ dependencies = [ "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "rustc_version", + "rustc_version 0.4.1", "subtle", "zeroize", ] @@ -1859,9 +2197,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", +] + +[[package]] +name = "curve25519-dalek-ng" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.6.4", + "subtle-ng", + "zeroize", ] [[package]] @@ -1902,7 +2253,7 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "strsim 0.10.0", "syn 1.0.109", @@ -1916,7 +2267,7 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "strsim 0.10.0", "syn 1.0.109", @@ -1930,10 +2281,10 @@ checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "strsim 0.11.1", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1966,7 +2317,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1982,6 +2333,46 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "data-encoding-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + [[package]] name = "debugid" version = "0.8.0" @@ -2029,7 +2420,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -2041,10 +2432,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "rustc_version", - "syn 2.0.77", + "rustc_version 0.4.1", + "syn 2.0.85", ] [[package]] @@ -2062,9 +2453,9 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", "unicode-xid 0.2.6", ] @@ -2176,6 +2567,19 @@ dependencies = [ "signature 2.2.0", ] +[[package]] +name = "ed25519-consensus" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b" +dependencies = [ + "curve25519-dalek-ng", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + [[package]] name = "ed25519-dalek" version = "2.1.1" @@ -2281,9 +2685,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -2295,9 +2699,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -2447,12 +2851,33 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec 0.7.6", + "auto_impl", + "bytes", +] + [[package]] name = "ff" version = "0.12.1" @@ -2525,11 +2950,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flex-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c606d892c9de11507fa0dcffc116434f94e105d0bbdc4e405b61519464c49d7b" +dependencies = [ + "eyre", + "paste", +] + [[package]] name = "flume" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", @@ -2542,6 +2977,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2619,7 +3060,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "num-bigint 0.4.6", - "num-derive", + "num-derive 0.2.5", "num-integer", "num-traits", "rand 0.4.6", @@ -2658,9 +3099,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -2673,9 +3114,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -2683,15 +3124,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -2712,9 +3153,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -2731,26 +3172,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -2764,9 +3205,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures 0.1.31", "futures-channel", @@ -2798,7 +3239,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap 4.5.20", - "futures 0.3.30", + "futures 0.3.31", "serde", "serde_json", "serde_yaml", @@ -2851,9 +3292,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -2931,7 +3372,7 @@ dependencies = [ "google-cloud-token", "home", "jsonwebtoken", - "reqwest 0.12.7", + "reqwest 0.12.9", "serde", "serde_json", "thiserror", @@ -2947,7 +3388,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f945a208886a13d07636f38fb978da371d0abc3e34bad338124b9f8c135a8f" dependencies = [ - "reqwest 0.12.7", + "reqwest 0.12.9", "thiserror", "tokio", ] @@ -2972,7 +3413,7 @@ dependencies = [ "percent-encoding", "pkcs8 0.10.2", "regex", - "reqwest 0.12.7", + "reqwest 0.12.9", "reqwest-middleware", "ring", "serde", @@ -3000,8 +3441,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19775995ee20209163239355bc3ad2f33f83da35d9ef72dea26e5af753552c87" dependencies = [ - "dashmap", - "futures 0.3.30", + "dashmap 5.5.3", + "futures 0.3.31", "futures-timer", "no-std-compat", "nonzero_ext", @@ -3045,7 +3486,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -3064,7 +3505,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -3115,6 +3556,17 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "hashlink" version = "0.9.1" @@ -3172,6 +3624,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" + [[package]] name = "hkdf" version = "0.12.4" @@ -3289,9 +3747,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -3314,7 +3772,7 @@ dependencies = [ "crossbeam-utils", "form_urlencoded", "futures-util", - "hyper 0.14.30", + "hyper 0.14.31", "lazy_static", "levenshtein", "log", @@ -3335,9 +3793,9 @@ checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" dependencies = [ "bytes", "futures-channel", @@ -3359,9 +3817,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -3386,7 +3844,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.31", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -3402,23 +3860,35 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "log", - "rustls 0.23.13", + "rustls 0.23.16", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper 0.14.31", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "hyper-timeout" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "pin-project-lite", "tokio", @@ -3432,7 +3902,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.30", + "hyper 0.14.31", "native-tls", "tokio", "tokio-native-tls", @@ -3446,7 +3916,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "native-tls", "tokio", @@ -3456,16 +3926,16 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.5.0", "pin-project-lite", "socket2", "tokio", @@ -3559,11 +4029,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.3" @@ -3576,12 +4052,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -3601,9 +4077,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6593a41c7a73841868772495db7dc1e8ecab43bb5c0b6da2059246c4b506ab60" +checksum = "a1f72d3e19488cf7d8ea52d2fc0f8754fc933398b337cd3cbdb28aaeb35159ef" dependencies = [ "console", "lazy_static", @@ -3623,9 +4099,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "ipnetwork" @@ -3715,9 +4191,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -3786,7 +4262,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core 0.23.2", "pin-project", - "rustls 0.23.13", + "rustls 0.23.16", "rustls-pki-types", "rustls-platform-verifier", "soketto 0.8.0", @@ -3810,7 +4286,7 @@ dependencies = [ "beef", "futures-timer", "futures-util", - "hyper 0.14.30", + "hyper 0.14.31", "jsonrpsee-types 0.21.0", "pin-project", "rustc-hash", @@ -3858,7 +4334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b7de9f3219d95985eb77fd03194d7c1b56c19bce1abfcc9d07462574b15572" dependencies = [ "async-trait", - "hyper 0.14.30", + "hyper 0.14.31", "hyper-rustls 0.24.2", "jsonrpsee-core 0.21.0", "jsonrpsee-types 0.21.0", @@ -3880,12 +4356,12 @@ dependencies = [ "async-trait", "base64 0.22.1", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-rustls 0.27.3", "hyper-util", "jsonrpsee-core 0.23.2", "jsonrpsee-types 0.23.2", - "rustls 0.23.13", + "rustls 0.23.16", "rustls-platform-verifier", "serde", "serde_json", @@ -3904,9 +4380,9 @@ checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.2.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -3920,7 +4396,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "jsonrpsee-core 0.23.2", "jsonrpsee-types 0.23.2", @@ -4060,7 +4536,7 @@ dependencies = [ "petgraph", "pico-args", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "string_cache", "term", "tiny-keccak 2.0.2", @@ -4074,7 +4550,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.7", + "regex-automata 0.4.8", ] [[package]] @@ -4098,6 +4574,17 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "leopard-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee58dbc414bd23885d7da915e0457618b36d1fc950a6169ef2cb29829d1b1a1d" +dependencies = [ + "bytes", + "lazy_static", + "thiserror", +] + [[package]] name = "levenshtein" version = "1.0.5" @@ -4106,9 +4593,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" @@ -4117,14 +4604,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libp2p-identity" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" +dependencies = [ + "bs58", + "hkdf", + "multihash", + "quick-protobuf", + "sha2 0.10.8", + "thiserror", + "tracing", +] [[package]] name = "libredox" @@ -4241,13 +4743,13 @@ dependencies = [ "anyhow", "async-trait", "envy", - "futures 0.3.30", + "futures 0.3.31", "hex", "num", "once_cell", "rand 0.8.5", "regex", - "reqwest 0.12.7", + "reqwest 0.12.9", "serde", "serde_json", "static_assertions", @@ -4291,7 +4793,16 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" dependencies = [ - "logos-derive", + "logos-derive 0.13.0", +] + +[[package]] +name = "logos" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6b6e02facda28ca5fb8dbe4b152496ba3b1bd5a4b40bb2b1b2d8ad74e0f39b" +dependencies = [ + "logos-derive 0.14.2", ] [[package]] @@ -4302,10 +4813,25 @@ checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" dependencies = [ "beef", "fnv", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "regex-syntax 0.6.29", - "syn 2.0.77", + "syn 2.0.85", +] + +[[package]] +name = "logos-codegen" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32eb6b5f26efacd015b000bfc562186472cd9b34bdba3f6b264e2a052676d10" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2 1.0.89", + "quote 1.0.37", + "regex-syntax 0.8.5", + "syn 2.0.85", ] [[package]] @@ -4314,16 +4840,25 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" dependencies = [ - "logos-codegen", + "logos-codegen 0.13.0", +] + +[[package]] +name = "logos-derive" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5d0c5463c911ef55624739fc353238b4e310f0144be1f875dc42fec6bfd5ec" +dependencies = [ + "logos-codegen 0.14.2", ] [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -4415,21 +4950,44 @@ version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" dependencies = [ - "miette-derive", + "miette-derive 5.10.0", "once_cell", "thiserror", "unicode-width", ] +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "cfg-if", + "miette-derive 7.2.0", + "thiserror", + "unicode-width", +] + [[package]] name = "miette-derive" version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", ] [[package]] @@ -4456,7 +5014,7 @@ checksum = "c325dfab65f261f386debee8b0969da215b3fa0037e74c8a1234db7ba986d803" dependencies = [ "crossbeam-channel", "crossbeam-utils", - "dashmap", + "dashmap 5.5.3", "skeptic", "smallvec", "tagptr", @@ -4502,15 +5060,55 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.1.0", - "httparse", - "memchr", - "mime", - "spin", - "version_check", + "bytes", + "encoding_rs", + "futures-util", + "http 1.1.0", + "httparse", + "memchr", + "mime", + "spin", + "version_check", +] + +[[package]] +name = "multiaddr" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc41f430805af9d1cf4adae4ed2149c759b877b01d909a1f40256188d09345d2" +dependencies = [ + "core2", + "unsigned-varint", ] [[package]] @@ -4554,6 +5152,18 @@ dependencies = [ "libc", ] +[[package]] +name = "nmt-rs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e408e823bdc9b4bb525a61b44e846239833a8f9bd86c03a43e4ca314a5497582" +dependencies = [ + "borsh", + "bytes", + "serde", + "sha2 0.10.8", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -4678,6 +5288,17 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 1.0.109", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -4765,9 +5386,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -4777,25 +5398,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate 3.2.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -4811,9 +5432,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -4830,9 +5451,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -4843,9 +5464,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -4889,7 +5510,7 @@ dependencies = [ "bytes", "http 1.1.0", "opentelemetry", - "reqwest 0.12.7", + "reqwest 0.12.9", ] [[package]] @@ -4906,10 +5527,10 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost 0.13.3", - "reqwest 0.12.7", + "reqwest 0.12.9", "thiserror", "tokio", - "tonic", + "tonic 0.12.3", ] [[package]] @@ -4921,7 +5542,7 @@ dependencies = [ "opentelemetry", "opentelemetry_sdk", "prost 0.13.3", - "tonic", + "tonic 0.12.3", ] [[package]] @@ -5016,7 +5637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ "proc-macro-crate 3.2.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -5056,6 +5677,43 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbjson" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" +dependencies = [ + "heck 0.4.1", + "itertools 0.11.0", + "prost 0.12.6", + "prost-types", +] + +[[package]] +name = "pbjson-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +dependencies = [ + "bytes", + "chrono", + "pbjson", + "pbjson-build", + "prost 0.12.6", + "prost-build", + "serde", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -5098,9 +5756,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -5109,9 +5767,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" +checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" dependencies = [ "pest", "pest_generator", @@ -5119,22 +5777,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" +checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "pest_meta" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" +checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" dependencies = [ "once_cell", "pest", @@ -5148,7 +5806,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.6.0", ] [[package]] @@ -5168,29 +5826,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -5345,12 +6003,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ - "proc-macro2 1.0.86", - "syn 2.0.77", + "proc-macro2 1.0.89", + "syn 2.0.85", ] [[package]] @@ -5402,7 +6060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", "version_check", @@ -5414,7 +6072,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "version_check", ] @@ -5436,9 +6094,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -5461,9 +6119,25 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bitflags 2.6.0", + "lazy_static", + "num-traits", + "rand 0.8.5", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.8.5", + "unarray", ] [[package]] @@ -5493,7 +6167,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes", - "heck 0.4.1", + "heck 0.5.0", "itertools 0.12.1", "log", "multimap", @@ -5503,7 +6177,7 @@ dependencies = [ "prost 0.12.6", "prost-types", "regex", - "syn 2.0.77", + "syn 2.0.85", "tempfile", ] @@ -5515,9 +6189,9 @@ checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", "itertools 0.12.1", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5528,9 +6202,9 @@ checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", "itertools 0.13.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5540,8 +6214,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3" dependencies = [ "base64 0.21.7", - "logos", - "miette", + "logos 0.13.0", + "miette 5.10.0", "once_cell", "prost 0.12.6", "prost-types", @@ -5549,6 +6223,19 @@ dependencies = [ "serde-value", ] +[[package]] +name = "prost-reflect" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f5eec97d5d34bdd17ad2db2219aabf46b054c6c41bd5529767c9ce55be5898f" +dependencies = [ + "logos 0.14.2", + "miette 7.2.0", + "once_cell", + "prost 0.12.6", + "prost-types", +] + [[package]] name = "prost-types" version = "0.12.6" @@ -5565,11 +6252,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00bb76c5f6221de491fe2c8f39b106330bbd9762c6511119c07940e10eb9ff11" dependencies = [ "bytes", - "miette", + "miette 5.10.0", + "prost 0.12.6", + "prost-reflect 0.12.0", + "prost-types", + "protox-parse 0.5.0", + "thiserror", +] + +[[package]] +name = "protox" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac532509cee918d40f38c3e12f8ef9230f215f017d54de7dd975015538a42ce7" +dependencies = [ + "bytes", + "miette 7.2.0", "prost 0.12.6", - "prost-reflect", + "prost-reflect 0.13.1", "prost-types", - "protox-parse", + "protox-parse 0.6.1", "thiserror", ] @@ -5579,8 +6281,20 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4581f441c58863525a3e6bec7b8de98188cf75239a56c725a3e7288450a33f" dependencies = [ - "logos", - "miette", + "logos 0.13.0", + "miette 5.10.0", + "prost-types", + "thiserror", +] + +[[package]] +name = "protox-parse" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6c33f43516fe397e2f930779d720ca12cd057f7da4cd6326a0ef78d69dee96" +dependencies = [ + "logos 0.14.2", + "miette 7.2.0", "prost-types", "thiserror", ] @@ -5600,7 +6314,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -5662,7 +6376,7 @@ version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", ] [[package]] @@ -5729,6 +6443,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "rand_xoshiro" version = "0.6.0" @@ -5778,9 +6501,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -5798,14 +6521,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -5819,13 +6542,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -5836,9 +6559,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rend" @@ -5863,7 +6586,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.30", + "hyper 0.14.31", "hyper-tls 0.5.0", "ipnet", "js-sys", @@ -5891,9 +6614,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "async-compression", "base64 0.22.1", @@ -5906,7 +6629,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-rustls 0.27.3", "hyper-tls 0.6.0", "hyper-util", @@ -5919,7 +6642,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "serde", "serde_json", "serde_urlencoded", @@ -5946,7 +6669,7 @@ dependencies = [ "anyhow", "async-trait", "http 1.1.0", - "reqwest 0.12.7", + "reqwest 0.12.9", "serde", "thiserror", "tower-service", @@ -6013,6 +6736,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rkyv" version = "0.7.45" @@ -6037,7 +6769,7 @@ version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -6088,6 +6820,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint 0.4.6", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rust_decimal" version = "1.36.0" @@ -6122,20 +6884,29 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.23", ] [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags 2.6.0", "errno", @@ -6172,9 +6943,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "aws-lc-rs", "log", @@ -6205,7 +6976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", "security-framework", @@ -6222,19 +6993,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-platform-verifier" @@ -6247,7 +7017,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.13", + "rustls 0.23.16", "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki 0.102.8", @@ -6287,9 +7057,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ruzstd" @@ -6351,7 +7121,7 @@ checksum = "d3475108a1b62c7efd1b5c65974f30109a598b2f45f23c9ae030acb9686966db" dependencies = [ "darling 0.14.4", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -6379,20 +7149,20 @@ checksum = "995491f110efdc6bea96d6a746140e32bfceb4ea47510750a5467295a4707a25" dependencies = [ "darling 0.14.4", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" dependencies = [ "bitvec", "cfg-if", - "derive_more 0.99.18", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", "serde", @@ -6400,14 +7170,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "46385cc24172cf615450267463f937c10072516359b3ff1cb24228a4a08bf951" dependencies = [ "proc-macro-crate 3.2.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 1.0.109", + "syn 2.0.85", ] [[package]] @@ -6416,10 +7186,10 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00860983481ac590ac87972062909bef0d6a658013b592ccc0f2feb272feab11" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "scale-info", - "syn 2.0.77", + "syn 2.0.85", "thiserror", ] @@ -6445,9 +7215,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -6587,6 +7357,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.23" @@ -6596,6 +7375,15 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "send_wrapper" version = "0.4.0" @@ -6642,7 +7430,7 @@ dependencies = [ "hostname", "libc", "os_info", - "rustc_version", + "rustc_version 0.4.1", "sentry-core", "uname", ] @@ -6718,9 +7506,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -6746,20 +7534,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", @@ -6787,6 +7575,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "serde_spanned" version = "0.6.8" @@ -6827,7 +7626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -6838,7 +7637,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "ryu", "serde", @@ -7172,7 +7971,7 @@ name = "snapshots_creator" version = "0.1.0" dependencies = [ "anyhow", - "futures 0.3.30", + "futures 0.3.31", "rand 0.8.5", "structopt", "test-casing", @@ -7199,7 +7998,7 @@ dependencies = [ "chacha20poly1305", "curve25519-dalek", "rand_core 0.6.4", - "rustc_version", + "rustc_version 0.4.1", "sha2 0.10.8", "subtle", ] @@ -7222,7 +8021,7 @@ checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ "base64 0.13.1", "bytes", - "futures 0.3.30", + "futures 0.3.31", "httparse", "log", "rand 0.8.5", @@ -7237,7 +8036,7 @@ checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" dependencies = [ "base64 0.22.1", "bytes", - "futures 0.3.30", + "futures 0.3.31", "http 1.1.0", "httparse", "log", @@ -7340,7 +8139,7 @@ dependencies = [ "hashbrown 0.14.5", "hashlink", "hex", - "indexmap 2.5.0", + "indexmap 2.6.0", "ipnetwork", "log", "memchr", @@ -7367,11 +8166,11 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "sqlx-core", "sqlx-macros-core", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -7385,7 +8184,7 @@ dependencies = [ "heck 0.5.0", "hex", "once_cell", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "serde", "serde_json", @@ -7394,7 +8193,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.77", + "syn 2.0.85", "tempfile", "tokio", "url", @@ -7585,7 +8384,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -7606,10 +8405,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "rustversion", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -7618,6 +8417,21 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "subtle-encoding" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcb1ed7b8330c5eed5441052651dd7a12c75e2ed88f2ec024ae1fa3a5e59945" +dependencies = [ + "zeroize", +] + +[[package]] +name = "subtle-ng" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" + [[package]] name = "subxt" version = "0.34.0" @@ -7630,7 +8444,7 @@ dependencies = [ "derivative", "either", "frame-metadata 16.0.0", - "futures 0.3.30", + "futures 0.3.31", "hex", "impl-serde", "instant", @@ -7665,12 +8479,12 @@ dependencies = [ "hex", "jsonrpsee 0.21.0", "parity-scale-codec", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.77", + "syn 2.0.85", "thiserror", "tokio", ] @@ -7681,7 +8495,7 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecec7066ba7bc0c3608fcd1d0c7d9584390990cd06095b6ae4f114f74c4b8550" dependencies = [ - "futures 0.3.30", + "futures 0.3.31", "futures-util", "serde", "serde_json", @@ -7704,7 +8518,7 @@ dependencies = [ "quote 1.0.37", "scale-typegen", "subxt-codegen", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -7758,18 +8572,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.77" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "unicode-ident", ] @@ -7781,9 +8595,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -7869,11 +8683,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -7927,9 +8747,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9b53c7124dd88026d5d98a1eb1fd062a578b7d783017c9298825526c7fb6427" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -7949,9 +8769,9 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -7971,22 +8791,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -8117,9 +8937,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -8133,15 +8953,25 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-macros" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -8181,7 +9011,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.13", + "rustls 0.23.16", "rustls-pki-types", "tokio", ] @@ -8239,7 +9069,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "toml_datetime", "winnow 0.5.40", ] @@ -8250,13 +9080,44 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", "winnow 0.6.20", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum 0.6.20", + "base64 0.21.7", + "bytes", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.31", + "hyper-timeout 0.4.1", + "percent-encoding", + "pin-project", + "prost 0.12.6", + "rustls-native-certs 0.7.3", + "rustls-pemfile 2.2.0", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tonic" version = "0.12.3" @@ -8265,15 +9126,15 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.7.7", "base64 0.22.1", "bytes", "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", - "hyper-timeout", + "hyper 1.5.0", + "hyper-timeout 0.5.1", "hyper-util", "percent-encoding", "pin-project", @@ -8373,9 +9234,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -8451,9 +9312,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" [[package]] name = "try-lock" @@ -8463,14 +9324,15 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.99" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207aa50d36c4be8d8c6ea829478be44a372c6a77669937bb39c698e52f1491e8" +checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" dependencies = [ "glob", "serde", "serde_derive", "serde_json", + "target-triple", "termcolor", "toml", ] @@ -8500,9 +9362,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -8525,20 +9387,23 @@ dependencies = [ "libc", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -8557,9 +9422,9 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-segmentation" @@ -8617,6 +9482,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" + [[package]] name = "untrusted" version = "0.9.0" @@ -8662,9 +9533,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "serde", ] @@ -8677,9 +9548,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" [[package]] name = "vcpkg" @@ -8732,7 +9603,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "671d3b894d5d0849f0a597f56bf071f42d4f2a1cbcf2f78ca21f870ab7c0cc2b" dependencies = [ - "hyper 0.14.30", + "hyper 0.14.31", "once_cell", "tokio", "tracing", @@ -8745,9 +9616,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a511871dc5de990a3b2a0e715facfbc5da848c0c0395597a1415029fb7c250a" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -8808,9 +9679,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -8819,24 +9690,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -8846,9 +9717,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote 1.0.37", "wasm-bindgen-macro-support", @@ -8856,28 +9727,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -8928,9 +9799,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", @@ -9284,9 +10155,9 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -9304,9 +10175,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -9607,7 +10478,7 @@ dependencies = [ "byteorder", "cfg-if", "crossbeam", - "futures 0.3.30", + "futures 0.3.31", "hex", "lazy_static", "num_cpus", @@ -9625,7 +10496,7 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "futures 0.3.30", + "futures 0.3.31", "serde", "tempfile", "test-casing", @@ -9664,7 +10535,7 @@ dependencies = [ "circuit_sequencer_api 0.140.3", "circuit_sequencer_api 0.141.2", "circuit_sequencer_api 0.150.7", - "futures 0.3.30", + "futures 0.3.31", "itertools 0.10.5", "num_cpus", "rand 0.8.5", @@ -9783,7 +10654,7 @@ dependencies = [ "anyhow", "async-trait", "rand 0.8.5", - "semver", + "semver 1.0.23", "tracing", "vise", "zksync_concurrency", @@ -9809,14 +10680,14 @@ dependencies = [ "bytesize", "http-body-util", "human-repr", - "hyper 1.4.1", + "hyper 1.5.0", "hyper-util", "im", "once_cell", "pin-project", "prost 0.12.6", "rand 0.8.5", - "semver", + "semver 1.0.23", "snow", "thiserror", "tls-listener", @@ -9917,7 +10788,7 @@ name = "zksync_contract_verification_server" version = "0.1.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.7", "serde", "serde_json", "tokio", @@ -9935,7 +10806,7 @@ version = "0.1.0" dependencies = [ "anyhow", "ctrlc", - "futures 0.3.30", + "futures 0.3.31", "structopt", "tokio", "tracing", @@ -9959,7 +10830,7 @@ dependencies = [ "hex", "lazy_static", "regex", - "semver", + "semver 1.0.23", "serde", "serde_json", "tempfile", @@ -10026,7 +10897,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5939e2df4288c263c706ff18ac718e984149223ad4289d6d957d767dcfc04c81" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -10048,22 +10919,34 @@ dependencies = [ "async-trait", "backon", "base58", + "bech32", + "bincode", "blake2 0.10.6", "blake2b_simd", "bytes", + "celestia-types", "flate2", - "futures 0.3.30", + "futures 0.3.31", "hex", + "http 1.1.0", "jsonrpsee 0.23.2", "parity-scale-codec", - "reqwest 0.12.7", + "pbjson-types", + "prost 0.12.6", + "reqwest 0.12.9", + "ripemd", "scale-encode", + "secp256k1", "serde", "serde_json", + "sha2 0.10.8", "subxt-metadata", "subxt-signer", "tokio", + "tokio-stream", + "tonic 0.11.0", "tracing", + "zksync_basic_types", "zksync_config", "zksync_da_client", "zksync_env_config", @@ -10077,7 +10960,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "futures 0.3.30", + "futures 0.3.31", "rand 0.8.5", "tokio", "tracing", @@ -10248,8 +11131,8 @@ dependencies = [ "async-trait", "clap 4.5.20", "envy", - "futures 0.3.30", - "rustc_version", + "futures 0.3.31", + "rustc_version 0.4.1", "serde", "serde_json", "tempfile", @@ -10306,7 +11189,7 @@ dependencies = [ "fraction", "httpmock", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest 0.12.9", "serde", "serde_json", "tokio", @@ -10322,7 +11205,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "axum", + "axum 0.7.7", "bincode", "thiserror", "tokio", @@ -10356,7 +11239,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", "serde", "syn 1.0.109", @@ -10368,7 +11251,7 @@ version = "0.1.0" dependencies = [ "assert_matches", "async-trait", - "futures 0.3.30", + "futures 0.3.31", "serde", "serde_json", "thiserror", @@ -10485,11 +11368,11 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "axum", - "futures 0.3.30", + "axum 0.7.7", + "futures 0.3.31", "itertools 0.10.5", "once_cell", - "reqwest 0.12.7", + "reqwest 0.12.9", "serde", "serde_json", "tempfile", @@ -10566,10 +11449,10 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "axum", + "axum 0.7.7", "chrono", "const-decoder", - "futures 0.3.30", + "futures 0.3.31", "governor", "hex", "http 1.1.0", @@ -10621,7 +11504,7 @@ dependencies = [ "async-trait", "rand 0.8.5", "secrecy", - "semver", + "semver 1.0.23", "tempfile", "test-casing", "thiserror", @@ -10704,9 +11587,9 @@ dependencies = [ "assert_matches", "async-trait", "ctrlc", - "futures 0.3.30", + "futures 0.3.31", "pin-project-lite", - "semver", + "semver 1.0.23", "thiserror", "tokio", "tracing", @@ -10760,9 +11643,9 @@ dependencies = [ name = "zksync_node_framework_derive" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -10817,7 +11700,7 @@ dependencies = [ "async-trait", "backon", "chrono", - "futures 0.3.30", + "futures 0.3.31", "once_cell", "serde", "serde_json", @@ -10871,7 +11754,7 @@ dependencies = [ "http 1.1.0", "prost 0.12.6", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest 0.12.9", "serde_json", "tempfile", "tokio", @@ -10900,9 +11783,9 @@ name = "zksync_proof_data_handler" version = "0.1.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.7", "chrono", - "hyper 1.4.1", + "hyper 1.5.0", "serde_json", "tokio", "tower 0.4.13", @@ -10931,7 +11814,7 @@ dependencies = [ "bit-vec", "once_cell", "prost 0.12.6", - "prost-reflect", + "prost-reflect 0.12.0", "quick-protobuf", "rand 0.8.5", "serde", @@ -10951,12 +11834,12 @@ dependencies = [ "anyhow", "heck 0.5.0", "prettyplease", - "proc-macro2 1.0.86", + "proc-macro2 1.0.89", "prost-build", - "prost-reflect", - "protox", + "prost-reflect 0.12.0", + "protox 0.5.1", "quote 1.0.37", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -11035,7 +11918,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap 4.5.20", - "futures 0.3.30", + "futures 0.3.31", "serde_json", "tikv-jemallocator", "tokio", @@ -11064,7 +11947,7 @@ dependencies = [ name = "zksync_shared_metrics" version = "0.1.0" dependencies = [ - "rustc_version", + "rustc_version 0.4.1", "tracing", "vise", "zksync_dal", @@ -11078,7 +11961,7 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "futures 0.3.30", + "futures 0.3.31", "serde", "test-casing", "thiserror", @@ -11144,7 +12027,7 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "futures 0.3.30", + "futures 0.3.31", "hex", "itertools 0.10.5", "once_cell", @@ -11205,7 +12088,7 @@ dependencies = [ "anyhow", "async-trait", "envy", - "reqwest 0.12.7", + "reqwest 0.12.9", "secp256k1", "serde", "thiserror", @@ -11299,12 +12182,12 @@ dependencies = [ "assert_matches", "bigdecimal", "bincode", - "futures 0.3.30", + "futures 0.3.31", "hex", "num", "once_cell", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest 0.12.9", "serde", "serde_json", "thiserror", @@ -11343,7 +12226,7 @@ dependencies = [ [[package]] name = "zksync_vm2" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=fd27994d1d88ea4868d9ad74746bf274b97126d1#fd27994d1d88ea4868d9ad74746bf274b97126d1" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "enum_dispatch", "primitive-types", @@ -11355,7 +12238,7 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=fd27994d1d88ea4868d9ad74746bf274b97126d1#fd27994d1d88ea4868d9ad74746bf274b97126d1" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "primitive-types", ] @@ -11405,8 +12288,8 @@ dependencies = [ "assert_matches", "async-trait", "backon", - "dashmap", - "futures 0.3.30", + "dashmap 5.5.3", + "futures 0.3.31", "once_cell", "rand 0.8.5", "serde", @@ -11439,12 +12322,12 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "futures 0.3.30", + "futures 0.3.31", "jsonrpsee 0.23.2", "pin-project-lite", "rand 0.8.5", "rlp", - "rustls 0.23.13", + "rustls 0.23.16", "serde", "serde_json", "test-casing", diff --git a/Cargo.toml b/Cargo.toml index 41c3df46c35..cdbc0d107f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -150,7 +150,7 @@ opentelemetry-semantic-conventions = "0.16.0" opentelemetry-appender-tracing = "0.5" pin-project-lite = "0.2.13" pretty_assertions = "1" -prost = "0.12.1" +prost = "0.12.6" rand = "0.8" rayon = "1.3.1" regex = "1" @@ -211,6 +211,16 @@ subxt-metadata = "0.34.0" parity-scale-codec = { version = "3.6.9", default-features = false } subxt-signer = { version = "0.34", default-features = false } +# Celestia +celestia-types = "0.6.1" +bech32 = "0.11.0" +ripemd = "0.1.3" +tonic = { version = "0.11.0", default-features = false } +pbjson-types = "0.6.0" + +# Eigen +tokio-stream = "0.1.16" + # Here and below: # We *always* pin the latest version of protocol to disallow accidental changes in the execution logic. # However, for the historical version of protocol crates, we have lax requirements. Otherwise, @@ -230,7 +240,7 @@ zk_evm_1_4_1 = { package = "zk_evm", version = "0.141" } zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.7" } # New VM; pinned to a specific commit because of instability -zksync_vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "fd27994d1d88ea4868d9ad74746bf274b97126d1" } +zksync_vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "457d8a7eea9093af9440662e33e598c13ba41633" } # Consensus dependencies. zksync_concurrency = "=0.5.0" diff --git a/contracts b/contracts index 84d5e3716f6..9fb1264fce8 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 84d5e3716f645909e8144c7d50af9dd6dd9ded62 +Subproject commit 9fb1264fce8c0ebeefe8bf1846e89876027161d2 diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index 70803a66311..0a94f993656 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -1,6 +1,7 @@ use std::{ env, ffi::OsString, + future::Future, num::{NonZeroU32, NonZeroU64, NonZeroUsize}, path::PathBuf, time::Duration, @@ -24,7 +25,7 @@ use zksync_core_leftovers::temp_config_store::read_yaml_repr; use zksync_dal::{ConnectionPool, Core}; use zksync_metadata_calculator::MetadataCalculatorRecoveryConfig; use zksync_node_api_server::{ - tx_sender::TxSenderConfig, + tx_sender::{TimestampAsserterParams, TxSenderConfig}, web3::{state::InternalApiConfig, Namespace}, }; use zksync_protobuf_config::proto; @@ -121,6 +122,7 @@ pub(crate) struct RemoteENConfig { pub l1_weth_bridge_addr: Option
, pub l2_weth_bridge_addr: Option
, pub l2_testnet_paymaster_addr: Option
, + pub l2_timestamp_asserter_addr: Option
, pub base_token_addr: Address, pub l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, pub dummy_verifier: bool, @@ -146,22 +148,19 @@ impl RemoteENConfig { .get_main_contract() .rpc_context("get_main_contract") .await?; - let base_token_addr = match client.get_base_token_l1_address().await { - Err(ClientError::Call(err)) - if [ - ErrorCode::MethodNotFound.code(), - // This what `Web3Error::NotImplemented` gets - // `casted` into in the `api` server. - ErrorCode::InternalError.code(), - ] - .contains(&(err.code())) => - { - // This is the fallback case for when the EN tries to interact - // with a node that does not implement the `zks_baseTokenL1Address` endpoint. - ETHEREUM_ADDRESS - } - response => response.context("Failed to fetch base token address")?, - }; + + let timestamp_asserter_address = handle_rpc_response_with_fallback( + client.get_timestamp_asserter(), + None, + "Failed to fetch timestamp asserter address".to_string(), + ) + .await?; + let base_token_addr = handle_rpc_response_with_fallback( + client.get_base_token_l1_address(), + ETHEREUM_ADDRESS, + "Failed to fetch base token address".to_string(), + ) + .await?; // These two config variables should always have the same value. // TODO(EVM-578): double check and potentially forbid both of them being `None`. @@ -206,6 +205,7 @@ impl RemoteENConfig { .as_ref() .map(|a| a.dummy_verifier) .unwrap_or_default(), + l2_timestamp_asserter_addr: timestamp_asserter_address, }) } @@ -227,10 +227,36 @@ impl RemoteENConfig { l2_legacy_shared_bridge_addr: Some(Address::repeat_byte(7)), l1_batch_commit_data_generator_mode: L1BatchCommitmentMode::Rollup, dummy_verifier: true, + l2_timestamp_asserter_addr: None, } } } +async fn handle_rpc_response_with_fallback( + rpc_call: F, + fallback: T, + context: String, +) -> anyhow::Result +where + F: Future>, + T: Clone, +{ + match rpc_call.await { + Err(ClientError::Call(err)) + if [ + ErrorCode::MethodNotFound.code(), + // This what `Web3Error::NotImplemented` gets + // `casted` into in the `api` server. + ErrorCode::InternalError.code(), + ] + .contains(&(err.code())) => + { + Ok(fallback) + } + response => response.context(context), + } +} + /// This part of the external node config is completely optional to provide. /// It can tweak limits of the API, delay intervals of certain components, etc. /// If any of the fields are not provided, the default values will be used. @@ -454,6 +480,9 @@ pub(crate) struct OptionalENConfig { pub gateway_url: Option, /// Interval for bridge addresses refreshing in seconds. bridge_addresses_refresh_interval_sec: Option, + /// Minimum time between current block.timestamp and the end of the asserted range for TimestampAsserter + #[serde(default = "OptionalENConfig::default_timestamp_asserter_min_time_till_end_sec")] + pub timestamp_asserter_min_time_till_end_sec: u32, } impl OptionalENConfig { @@ -685,6 +714,11 @@ impl OptionalENConfig { contracts_diamond_proxy_addr: None, gateway_url: enconfig.gateway_url.clone(), bridge_addresses_refresh_interval_sec: enconfig.bridge_addresses_refresh_interval_sec, + timestamp_asserter_min_time_till_end_sec: general_config + .timestamp_asserter_config + .as_ref() + .map(|x| x.min_time_till_end_sec) + .unwrap_or_else(Self::default_timestamp_asserter_min_time_till_end_sec), }) } @@ -819,6 +853,10 @@ impl OptionalENConfig { 3_600 * 24 * 7 // 7 days } + const fn default_timestamp_asserter_min_time_till_end_sec() -> u32 { + 60 + } + fn from_env() -> anyhow::Result { let mut result: OptionalENConfig = envy::prefixed("EN_") .from_env() @@ -1425,6 +1463,7 @@ impl From<&ExternalNodeConfig> for InternalApiConfig { filters_disabled: config.optional.filters_disabled, dummy_verifier: config.remote.dummy_verifier, l1_batch_commit_data_generator_mode: config.remote.l1_batch_commit_data_generator_mode, + timestamp_asserter_address: config.remote.l2_timestamp_asserter_addr, } } } @@ -1447,6 +1486,17 @@ impl From<&ExternalNodeConfig> for TxSenderConfig { chain_id: config.required.l2_chain_id, // Does not matter for EN. whitelisted_tokens_for_aa: Default::default(), + timestamp_asserter_params: config.remote.l2_timestamp_asserter_addr.map(|address| { + TimestampAsserterParams { + address, + min_time_till_end: Duration::from_secs( + config + .optional + .timestamp_asserter_min_time_till_end_sec + .into(), + ), + } + }), } } } diff --git a/core/bin/external_node/src/config/tests.rs b/core/bin/external_node/src/config/tests.rs index a32be3eff72..dc74d124b18 100644 --- a/core/bin/external_node/src/config/tests.rs +++ b/core/bin/external_node/src/config/tests.rs @@ -128,6 +128,7 @@ fn parsing_optional_config_from_env() { "zks_getProof=100,eth_call=2", ), ("EN_L1_BATCH_COMMIT_DATA_GENERATOR_MODE", "Validium"), + ("EN_TIMESTAMP_ASSERTER_MIN_TIME_TILL_END_SEC", "2"), ]; let env_vars = env_vars .into_iter() diff --git a/core/bin/zksync_server/src/main.rs b/core/bin/zksync_server/src/main.rs index 9e1a1b5948c..51e7b409c9a 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -7,16 +7,15 @@ use zksync_config::{ api::{HealthCheckConfig, MerkleTreeApiConfig, Web3JsonRpcConfig}, chain::{ CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, - StateKeeperConfig, + StateKeeperConfig, TimestampAsserterConfig, }, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, - secrets::DataAvailabilitySecrets, - BasicWitnessInputProducerConfig, ContractsConfig, DatabaseSecrets, ExperimentalVmConfig, - ExternalPriceApiClientConfig, FriProofCompressorConfig, FriProverConfig, - FriProverGatewayConfig, FriWitnessGeneratorConfig, FriWitnessVectorGeneratorConfig, - L1Secrets, ObservabilityConfig, PrometheusConfig, ProofDataHandlerConfig, - ProtectiveReadsWriterConfig, Secrets, + BasicWitnessInputProducerConfig, ContractsConfig, DataAvailabilitySecrets, DatabaseSecrets, + ExperimentalVmConfig, ExternalPriceApiClientConfig, FriProofCompressorConfig, + FriProverConfig, FriProverGatewayConfig, FriWitnessGeneratorConfig, + FriWitnessVectorGeneratorConfig, L1Secrets, ObservabilityConfig, PrometheusConfig, + ProofDataHandlerConfig, ProtectiveReadsWriterConfig, Secrets, }, ApiConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, DAClientConfig, DADispatcherConfig, DBConfig, EthConfig, EthWatchConfig, ExternalProofIntegrationApiConfig, GasAdjusterConfig, @@ -196,5 +195,6 @@ fn load_env_config() -> anyhow::Result { external_proof_integration_api_config: ExternalProofIntegrationApiConfig::from_env().ok(), experimental_vm_config: ExperimentalVmConfig::from_env().ok(), prover_job_monitor_config: None, + timestamp_asserter_config: TimestampAsserterConfig::from_env().ok(), }) } diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 19edef6e4ee..32478ede5bf 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -1,7 +1,9 @@ //! This module provides a "builder" for the main node, //! as well as an interface to run the node with the specified components. -use anyhow::Context; +use std::time::Duration; + +use anyhow::{bail, Context}; use zksync_config::{ configs::{ da_client::DAClientConfig, secrets::DataAvailabilitySecrets, wallets::Wallets, @@ -12,7 +14,7 @@ use zksync_config::{ use zksync_core_leftovers::Component; use zksync_metadata_calculator::MetadataCalculatorConfig; use zksync_node_api_server::{ - tx_sender::TxSenderConfig, + tx_sender::{TimestampAsserterParams, TxSenderConfig}, web3::{state::InternalApiConfig, Namespace}, }; use zksync_node_framework::{ @@ -26,8 +28,8 @@ use zksync_node_framework::{ consensus::MainNodeConsensusLayer, contract_verification_api::ContractVerificationApiLayer, da_clients::{ - avail::AvailWiringLayer, no_da::NoDAClientWiringLayer, - object_store::ObjectStorageClientWiringLayer, + avail::AvailWiringLayer, celestia::CelestiaWiringLayer, eigen::EigenWiringLayer, + no_da::NoDAClientWiringLayer, object_store::ObjectStorageClientWiringLayer, }, da_dispatcher::DataAvailabilityDispatcherLayer, eth_sender::{EthTxAggregatorLayer, EthTxManagerLayer}, @@ -303,6 +305,20 @@ impl MainNodeBuilder { fn add_tx_sender_layer(mut self) -> anyhow::Result { let sk_config = try_load_config!(self.configs.state_keeper_config); let rpc_config = try_load_config!(self.configs.api_config).web3_json_rpc; + + let timestamp_asserter_params = match self.contracts_config.l2_timestamp_asserter_addr { + Some(address) => { + let timestamp_asserter_config = + try_load_config!(self.configs.timestamp_asserter_config); + Some(TimestampAsserterParams { + address, + min_time_till_end: Duration::from_secs( + timestamp_asserter_config.min_time_till_end_sec.into(), + ), + }) + } + None => None, + }; let postgres_storage_caches_config = PostgresStorageCachesConfig { factory_deps_cache_size: rpc_config.factory_deps_cache_size() as u64, initial_writes_cache_size: rpc_config.initial_writes_cache_size() as u64, @@ -322,6 +338,7 @@ impl MainNodeBuilder { .fee_account .address(), self.genesis_config.l2_chain_id, + timestamp_asserter_params, ), postgres_storage_caches_config, rpc_config.vm_concurrency_limit(), @@ -507,16 +524,25 @@ impl MainNodeBuilder { }; let secrets = try_load_config!(self.secrets.data_availability); - match (da_client_config, secrets) { (DAClientConfig::Avail(config), DataAvailabilitySecrets::Avail(secret)) => { self.node.add_layer(AvailWiringLayer::new(config, secret)); } + (DAClientConfig::Celestia(config), DataAvailabilitySecrets::Celestia(secret)) => { + self.node + .add_layer(CelestiaWiringLayer::new(config, secret)); + } + + (DAClientConfig::Eigen(config), DataAvailabilitySecrets::Eigen(secret)) => { + self.node.add_layer(EigenWiringLayer::new(config, secret)); + } + (DAClientConfig::ObjectStore(config), _) => { self.node .add_layer(ObjectStorageClientWiringLayer::new(config)); } + _ => bail!("invalid pair of da_client and da_secrets"), } Ok(self) diff --git a/core/lib/basic_types/src/api_key.rs b/core/lib/basic_types/src/api_key.rs deleted file mode 100644 index eadf4e9051b..00000000000 --- a/core/lib/basic_types/src/api_key.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::str::FromStr; - -use secrecy::{ExposeSecret, Secret}; - -#[derive(Debug, Clone)] -pub struct APIKey(pub Secret); - -impl PartialEq for APIKey { - fn eq(&self, other: &Self) -> bool { - self.0.expose_secret().eq(other.0.expose_secret()) - } -} - -impl FromStr for APIKey { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - Ok(APIKey(s.parse()?)) - } -} diff --git a/core/lib/basic_types/src/lib.rs b/core/lib/basic_types/src/lib.rs index 7953f362fd4..1b462fdf77d 100644 --- a/core/lib/basic_types/src/lib.rs +++ b/core/lib/basic_types/src/lib.rs @@ -24,14 +24,13 @@ use serde::{de, Deserialize, Deserializer, Serialize}; #[macro_use] mod macros; -pub mod api_key; pub mod basic_fri_types; pub mod commitment; pub mod network; pub mod protocol_version; pub mod prover_dal; pub mod pubdata_da; -pub mod seed_phrase; +pub mod secrets; pub mod settlement; pub mod tee_types; pub mod url; diff --git a/core/lib/basic_types/src/prover_dal.rs b/core/lib/basic_types/src/prover_dal.rs index bec5a55ced1..d86f79ba77a 100644 --- a/core/lib/basic_types/src/prover_dal.rs +++ b/core/lib/basic_types/src/prover_dal.rs @@ -28,12 +28,6 @@ pub struct ExtendedJobCountStatistics { pub successful: usize, } -#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)] -pub struct JobCountStatistics { - pub queued: usize, - pub in_progress: usize, -} - impl Add for ExtendedJobCountStatistics { type Output = ExtendedJobCountStatistics; @@ -47,6 +41,19 @@ impl Add for ExtendedJobCountStatistics { } } +#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)] +pub struct JobCountStatistics { + pub queued: usize, + pub in_progress: usize, +} + +impl JobCountStatistics { + /// all returns sum of queued and in_progress. + pub fn all(&self) -> usize { + self.queued + self.in_progress + } +} + #[derive(Debug)] pub struct StuckJobs { pub id: u64, diff --git a/core/lib/basic_types/src/secrets.rs b/core/lib/basic_types/src/secrets.rs new file mode 100644 index 00000000000..b3627470660 --- /dev/null +++ b/core/lib/basic_types/src/secrets.rs @@ -0,0 +1,54 @@ +use std::str::FromStr; + +use secrecy::{ExposeSecret, Secret}; + +#[derive(Debug, Clone)] +pub struct SeedPhrase(pub Secret); + +impl PartialEq for SeedPhrase { + fn eq(&self, other: &Self) -> bool { + self.0.expose_secret().eq(other.0.expose_secret()) + } +} + +impl FromStr for SeedPhrase { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(SeedPhrase(s.parse()?)) + } +} + +#[derive(Debug, Clone)] +pub struct PrivateKey(pub Secret); + +impl PartialEq for PrivateKey { + fn eq(&self, other: &Self) -> bool { + self.0.expose_secret().eq(other.0.expose_secret()) + } +} + +impl FromStr for PrivateKey { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(PrivateKey(s.parse()?)) + } +} + +#[derive(Debug, Clone)] +pub struct APIKey(pub Secret); + +impl PartialEq for APIKey { + fn eq(&self, other: &Self) -> bool { + self.0.expose_secret().eq(other.0.expose_secret()) + } +} + +impl FromStr for APIKey { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(APIKey(s.parse()?)) + } +} diff --git a/core/lib/basic_types/src/seed_phrase.rs b/core/lib/basic_types/src/seed_phrase.rs deleted file mode 100644 index 332bfd58594..00000000000 --- a/core/lib/basic_types/src/seed_phrase.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::str::FromStr; - -use secrecy::{ExposeSecret, Secret}; - -#[derive(Debug, Clone)] -pub struct SeedPhrase(pub Secret); - -impl PartialEq for SeedPhrase { - fn eq(&self, other: &Self) -> bool { - self.0.expose_secret().eq(other.0.expose_secret()) - } -} - -impl FromStr for SeedPhrase { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - Ok(SeedPhrase(s.parse()?)) - } -} diff --git a/core/lib/config/src/configs/chain.rs b/core/lib/config/src/configs/chain.rs index c117064dbc4..d73dce81b13 100644 --- a/core/lib/config/src/configs/chain.rs +++ b/core/lib/config/src/configs/chain.rs @@ -1,6 +1,6 @@ use std::{str::FromStr, time::Duration}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use zksync_basic_types::{ commitment::L1BatchCommitmentMode, network::Network, Address, L2ChainId, H256, }; @@ -244,3 +244,9 @@ impl MempoolConfig { Duration::from_millis(self.delay_interval) } } + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)] +pub struct TimestampAsserterConfig { + /// Minimum time between current block.timestamp and the end of the asserted range + pub min_time_till_end_sec: u32, +} diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 0bf7aab3bca..38576833fa3 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -41,6 +41,7 @@ pub struct ContractsConfig { pub l1_weth_bridge_proxy_addr: Option
, pub l2_weth_bridge_addr: Option
, pub l2_testnet_paymaster_addr: Option
, + pub l2_timestamp_asserter_addr: Option
, pub l1_multicall3_addr: Address, pub ecosystem_contracts: Option, // Used by the RPC API and by the node builder in wiring the BaseTokenRatioProvider layer. @@ -65,6 +66,7 @@ impl ContractsConfig { l2_weth_bridge_addr: Some(Address::repeat_byte(0x0c)), l2_testnet_paymaster_addr: Some(Address::repeat_byte(0x11)), l1_multicall3_addr: Address::repeat_byte(0x12), + l2_timestamp_asserter_addr: Some(Address::repeat_byte(0x19)), governance_addr: Address::repeat_byte(0x13), base_token_addr: Some(Address::repeat_byte(0x14)), ecosystem_contracts: Some(EcosystemContracts::for_tests()), diff --git a/core/lib/config/src/configs/da_client/avail.rs b/core/lib/config/src/configs/da_client/avail.rs index b8e9db0f393..3993656d667 100644 --- a/core/lib/config/src/configs/da_client/avail.rs +++ b/core/lib/config/src/configs/da_client/avail.rs @@ -1,5 +1,5 @@ use serde::Deserialize; -use zksync_basic_types::{api_key::APIKey, seed_phrase::SeedPhrase}; +use zksync_basic_types::secrets::{APIKey, SeedPhrase}; pub const AVAIL_GAS_RELAY_CLIENT_NAME: &str = "GasRelay"; pub const AVAIL_FULL_CLIENT_NAME: &str = "FullClient"; @@ -14,7 +14,7 @@ pub enum AvailClientConfig { #[derive(Clone, Debug, PartialEq, Deserialize)] pub struct AvailConfig { pub bridge_api_url: String, - pub timeout: usize, + pub timeout_ms: usize, #[serde(flatten)] pub config: AvailClientConfig, } diff --git a/core/lib/config/src/configs/da_client/celestia.rs b/core/lib/config/src/configs/da_client/celestia.rs new file mode 100644 index 00000000000..45810e0381e --- /dev/null +++ b/core/lib/config/src/configs/da_client/celestia.rs @@ -0,0 +1,15 @@ +use serde::Deserialize; +use zksync_basic_types::secrets::PrivateKey; + +#[derive(Clone, Debug, Default, PartialEq, Deserialize)] +pub struct CelestiaConfig { + pub api_node_url: String, + pub namespace: String, + pub chain_id: String, + pub timeout_ms: u64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CelestiaSecrets { + pub private_key: PrivateKey, +} diff --git a/core/lib/config/src/configs/da_client/eigen.rs b/core/lib/config/src/configs/da_client/eigen.rs new file mode 100644 index 00000000000..f2c05a0f61e --- /dev/null +++ b/core/lib/config/src/configs/da_client/eigen.rs @@ -0,0 +1,13 @@ +use serde::Deserialize; +use zksync_basic_types::secrets::PrivateKey; + +#[derive(Clone, Debug, Default, PartialEq, Deserialize)] +pub struct EigenConfig { + pub rpc_node_url: String, + pub inclusion_polling_interval_ms: u64, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct EigenSecrets { + pub private_key: PrivateKey, +} diff --git a/core/lib/config/src/configs/da_client/mod.rs b/core/lib/config/src/configs/da_client/mod.rs index 406305a77b1..322c4a20aac 100644 --- a/core/lib/config/src/configs/da_client/mod.rs +++ b/core/lib/config/src/configs/da_client/mod.rs @@ -1,12 +1,18 @@ -use crate::{AvailConfig, ObjectStoreConfig}; +use crate::{AvailConfig, CelestiaConfig, EigenConfig, ObjectStoreConfig}; pub mod avail; +pub mod celestia; +pub mod eigen; pub const AVAIL_CLIENT_CONFIG_NAME: &str = "Avail"; +pub const CELESTIA_CLIENT_CONFIG_NAME: &str = "Celestia"; +pub const EIGEN_CLIENT_CONFIG_NAME: &str = "Eigen"; pub const OBJECT_STORE_CLIENT_CONFIG_NAME: &str = "ObjectStore"; #[derive(Debug, Clone, PartialEq)] pub enum DAClientConfig { Avail(AvailConfig), + Celestia(CelestiaConfig), + Eigen(EigenConfig), ObjectStore(ObjectStoreConfig), } diff --git a/core/lib/config/src/configs/external_price_api_client.rs b/core/lib/config/src/configs/external_price_api_client.rs index 15cc7d29d84..c1092f3a727 100644 --- a/core/lib/config/src/configs/external_price_api_client.rs +++ b/core/lib/config/src/configs/external_price_api_client.rs @@ -4,16 +4,21 @@ use serde::Deserialize; pub const DEFAULT_TIMEOUT_MS: u64 = 10_000; +pub const DEFAULT_FORCED_NEXT_VALUE_FLUCTUATION: u32 = 3; + #[derive(Debug, Clone, PartialEq, Deserialize)] pub struct ForcedPriceClientConfig { /// Forced conversion ratio pub numerator: Option, pub denominator: Option, - /// Forced fluctuation. It defines how much percent numerator / - /// denominator should fluctuate from their forced values. If it's None or 0, then ForcedPriceClient - /// will return the same quote every time it's called. Otherwise, ForcedPriceClient will return - /// forced_quote +/- forced_fluctuation % from its values. + /// Forced fluctuation. It defines how much percent the ratio should fluctuate from its forced + /// value. If it's None or 0, then the ForcedPriceClient will return the same quote every time + /// it's called. Otherwise, ForcedPriceClient will return quote with numerator +/- fluctuation %. pub fluctuation: Option, + /// In order to smooth out fluctuation, consecutive values returned by forced client will not + /// differ more than next_value_fluctuation percent. If it's None, a default of 3% will be applied. + #[serde(default = "ExternalPriceApiClientConfig::default_forced_next_value_fluctuation")] + pub next_value_fluctuation: u32, } #[derive(Debug, Clone, PartialEq, Deserialize)] @@ -31,6 +36,10 @@ impl ExternalPriceApiClientConfig { DEFAULT_TIMEOUT_MS } + fn default_forced_next_value_fluctuation() -> u32 { + DEFAULT_FORCED_NEXT_VALUE_FLUCTUATION + } + pub fn client_timeout(&self) -> Duration { Duration::from_millis(self.client_timeout_ms) } diff --git a/core/lib/config/src/configs/general.rs b/core/lib/config/src/configs/general.rs index bb733510f77..dfb81af1cf8 100644 --- a/core/lib/config/src/configs/general.rs +++ b/core/lib/config/src/configs/general.rs @@ -1,7 +1,10 @@ use crate::{ configs::{ base_token_adjuster::BaseTokenAdjusterConfig, - chain::{CircuitBreakerConfig, MempoolConfig, OperationsManagerConfig, StateKeeperConfig}, + chain::{ + CircuitBreakerConfig, MempoolConfig, OperationsManagerConfig, StateKeeperConfig, + TimestampAsserterConfig, + }, consensus::ConsensusConfig, da_client::DAClientConfig, da_dispatcher::DADispatcherConfig, @@ -56,4 +59,5 @@ pub struct GeneralConfig { pub external_proof_integration_api_config: Option, pub experimental_vm_config: Option, pub prover_job_monitor_config: Option, + pub timestamp_asserter_config: Option, } diff --git a/core/lib/config/src/configs/mod.rs b/core/lib/config/src/configs/mod.rs index b3a7c291343..2b848030d71 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -5,7 +5,7 @@ pub use self::{ commitment_generator::CommitmentGeneratorConfig, contract_verifier::ContractVerifierConfig, contracts::{ContractsConfig, EcosystemContracts}, - da_client::{avail::AvailConfig, DAClientConfig}, + da_client::{avail::AvailConfig, celestia::CelestiaConfig, eigen::EigenConfig, DAClientConfig}, da_dispatcher::DADispatcherConfig, database::{DBConfig, PostgresConfig}, eth_sender::{EthConfig, GasAdjusterConfig}, @@ -25,7 +25,7 @@ pub use self::{ proof_data_handler::{ProofDataHandlerConfig, TeeConfig}, prover_job_monitor::ProverJobMonitorConfig, pruning::PruningConfig, - secrets::{DatabaseSecrets, L1Secrets, Secrets}, + secrets::{DataAvailabilitySecrets, DatabaseSecrets, L1Secrets, Secrets}, snapshot_recovery::SnapshotRecoveryConfig, snapshots_creator::SnapshotsCreatorConfig, utils::PrometheusConfig, diff --git a/core/lib/config/src/configs/prover_autoscaler.rs b/core/lib/config/src/configs/prover_autoscaler.rs index b24a1a26651..4191208b96e 100644 --- a/core/lib/config/src/configs/prover_autoscaler.rs +++ b/core/lib/config/src/configs/prover_autoscaler.rs @@ -61,6 +61,11 @@ pub struct ProverAutoscalerScalerConfig { /// Duration after which pending pod considered long pending. #[serde(default = "ProverAutoscalerScalerConfig::default_long_pending_duration")] pub long_pending_duration: Duration, + /// List of simple autoscaler targets. + pub scaler_targets: Vec, + /// If dry-run enabled don't send any scale requests. + #[serde(default)] + pub dry_run: bool, } #[derive( @@ -93,6 +98,41 @@ pub enum Gpu { A100, } +// TODO: generate this enum by QueueReport from https://github.com/matter-labs/zksync-era/blob/main/prover/crates/bin/prover_job_monitor/src/autoscaler_queue_reporter.rs#L23 +// and remove allowing of non_camel_case_types by generating field name parser. +#[derive(Debug, Display, PartialEq, Eq, Hash, Clone, Copy, Deserialize, EnumString, Default)] +#[allow(non_camel_case_types)] +pub enum QueueReportFields { + #[strum(ascii_case_insensitive)] + basic_witness_jobs, + #[strum(ascii_case_insensitive)] + leaf_witness_jobs, + #[strum(ascii_case_insensitive)] + node_witness_jobs, + #[strum(ascii_case_insensitive)] + recursion_tip_witness_jobs, + #[strum(ascii_case_insensitive)] + scheduler_witness_jobs, + #[strum(ascii_case_insensitive)] + proof_compressor_jobs, + #[default] + #[strum(ascii_case_insensitive)] + prover_jobs, +} + +/// ScalerTarget can be configured to autoscale any of services for which queue is reported by +/// prover-job-monitor, except of provers. Provers need special treatment due to GPU requirement. +#[derive(Debug, Clone, PartialEq, Deserialize, Default)] +pub struct ScalerTarget { + pub queue_report_field: QueueReportFields, + pub deployment: String, + /// Max replicas per cluster. + pub max_replicas: HashMap, + /// The queue will be divided by the speed and rounded up to get number of replicas. + #[serde(default = "ScalerTarget::default_speed")] + pub speed: usize, +} + impl ProverAutoscalerConfig { /// Default graceful shutdown timeout -- 5 seconds pub fn default_graceful_shutdown_timeout() -> Duration { @@ -126,3 +166,9 @@ impl ProverAutoscalerScalerConfig { Duration::minutes(10) } } + +impl ScalerTarget { + pub fn default_speed() -> usize { + 1 + } +} diff --git a/core/lib/config/src/configs/secrets.rs b/core/lib/config/src/configs/secrets.rs index 779bad37065..75ff067c247 100644 --- a/core/lib/config/src/configs/secrets.rs +++ b/core/lib/config/src/configs/secrets.rs @@ -1,7 +1,10 @@ use anyhow::Context; use zksync_basic_types::url::SensitiveUrl; -use crate::configs::{consensus::ConsensusSecrets, da_client::avail::AvailSecrets}; +use crate::configs::{ + consensus::ConsensusSecrets, + da_client::{avail::AvailSecrets, celestia::CelestiaSecrets, eigen::EigenSecrets}, +}; #[derive(Debug, Clone, PartialEq)] pub struct DatabaseSecrets { @@ -18,6 +21,8 @@ pub struct L1Secrets { #[derive(Debug, Clone, PartialEq)] pub enum DataAvailabilitySecrets { Avail(AvailSecrets), + Celestia(CelestiaSecrets), + Eigen(EigenSecrets), } #[derive(Debug, Clone, PartialEq)] diff --git a/core/lib/config/src/lib.rs b/core/lib/config/src/lib.rs index 9191edc3982..f77a8ceb39a 100644 --- a/core/lib/config/src/lib.rs +++ b/core/lib/config/src/lib.rs @@ -1,10 +1,10 @@ #![allow(clippy::upper_case_acronyms, clippy::derive_partial_eq_without_eq)] pub use crate::configs::{ - ApiConfig, AvailConfig, BaseTokenAdjusterConfig, ContractVerifierConfig, ContractsConfig, - DAClientConfig, DADispatcherConfig, DBConfig, EthConfig, EthWatchConfig, - ExternalProofIntegrationApiConfig, GasAdjusterConfig, GenesisConfig, ObjectStoreConfig, - PostgresConfig, SnapshotsCreatorConfig, + ApiConfig, AvailConfig, BaseTokenAdjusterConfig, CelestiaConfig, ContractVerifierConfig, + ContractsConfig, DAClientConfig, DADispatcherConfig, DBConfig, EigenConfig, EthConfig, + EthWatchConfig, ExternalProofIntegrationApiConfig, GasAdjusterConfig, GenesisConfig, + ObjectStoreConfig, PostgresConfig, SnapshotsCreatorConfig, }; pub mod configs; diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 21ff9e2351b..72df871d7ce 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -3,13 +3,12 @@ use std::num::NonZeroUsize; use rand::{distributions::Distribution, Rng}; use secrecy::Secret; use zksync_basic_types::{ - api_key::APIKey, basic_fri_types::CircuitIdRoundTuple, commitment::L1BatchCommitmentMode, network::Network, protocol_version::{ProtocolSemanticVersion, ProtocolVersionId, VersionPatch}, pubdata_da::PubdataSendingMode, - seed_phrase::SeedPhrase, + secrets::{APIKey, SeedPhrase}, vm::FastVmMode, L1BatchNumber, L1ChainId, L2ChainId, }; @@ -19,6 +18,7 @@ use zksync_crypto_primitives::K256PrivateKey; use crate::{ configs::{ self, + chain::TimestampAsserterConfig, da_client::{ avail::{AvailClientConfig, AvailDefaultConfig}, DAClientConfig::Avail, @@ -266,6 +266,7 @@ impl Distribution for EncodeDist { l1_weth_bridge_proxy_addr: self.sample_opt(|| rng.gen()), l2_weth_bridge_addr: self.sample_opt(|| rng.gen()), l2_testnet_paymaster_addr: self.sample_opt(|| rng.gen()), + l2_timestamp_asserter_addr: self.sample_opt(|| rng.gen()), l1_multicall3_addr: rng.gen(), ecosystem_contracts: self.sample(rng), base_token_addr: self.sample_opt(|| rng.gen()), @@ -949,7 +950,7 @@ impl Distribution for EncodeDist { fn sample(&self, rng: &mut R) -> configs::da_client::DAClientConfig { Avail(AvailConfig { bridge_api_url: self.sample(rng), - timeout: self.sample(rng), + timeout_ms: self.sample(rng), config: AvailClientConfig::FullClient(AvailDefaultConfig { api_node_url: self.sample(rng), app_id: self.sample(rng), @@ -1114,6 +1115,7 @@ impl Distribution for EncodeDist { external_proof_integration_api_config: self.sample(rng), experimental_vm_config: self.sample(rng), prover_job_monitor_config: self.sample(rng), + timestamp_asserter_config: self.sample(rng), + } + } +} + +impl Distribution for EncodeDist { + fn sample(&self, rng: &mut R) -> TimestampAsserterConfig { + TimestampAsserterConfig { + min_time_till_end_sec: self.sample(rng), } } } diff --git a/core/lib/dal/.sqlx/query-1689c212d411ebd99a22210519ea2d505a1aabf52ff4136d2ed1b39c70dd1632.json b/core/lib/dal/.sqlx/query-1689c212d411ebd99a22210519ea2d505a1aabf52ff4136d2ed1b39c70dd1632.json index 7b939d137db..b84cd1bcba8 100644 --- a/core/lib/dal/.sqlx/query-1689c212d411ebd99a22210519ea2d505a1aabf52ff4136d2ed1b39c70dd1632.json +++ b/core/lib/dal/.sqlx/query-1689c212d411ebd99a22210519ea2d505a1aabf52ff4136d2ed1b39c70dd1632.json @@ -182,6 +182,16 @@ "ordinal": 35, "name": "upgrade_id", "type_info": "Int4" + }, + { + "ordinal": 36, + "name": "timestamp_asserter_range_start", + "type_info": "Timestamp" + }, + { + "ordinal": 37, + "name": "timestamp_asserter_range_end", + "type_info": "Timestamp" } ], "parameters": { @@ -223,6 +233,8 @@ false, true, true, + true, + true, true ] }, diff --git a/core/lib/dal/.sqlx/query-72a4f50355324cce85ebaef9fa32826095e9290f0c1157094bd0c44e06012e42.json b/core/lib/dal/.sqlx/query-72a4f50355324cce85ebaef9fa32826095e9290f0c1157094bd0c44e06012e42.json index 707b7ce9e75..2f4203aaa32 100644 --- a/core/lib/dal/.sqlx/query-72a4f50355324cce85ebaef9fa32826095e9290f0c1157094bd0c44e06012e42.json +++ b/core/lib/dal/.sqlx/query-72a4f50355324cce85ebaef9fa32826095e9290f0c1157094bd0c44e06012e42.json @@ -182,6 +182,16 @@ "ordinal": 35, "name": "upgrade_id", "type_info": "Int4" + }, + { + "ordinal": 36, + "name": "timestamp_asserter_range_start", + "type_info": "Timestamp" + }, + { + "ordinal": 37, + "name": "timestamp_asserter_range_end", + "type_info": "Timestamp" } ], "parameters": { @@ -225,6 +235,8 @@ false, true, true, + true, + true, true ] }, diff --git a/core/lib/dal/.sqlx/query-a36135b5908992324c4308f549ea77a428820fdcea9969aff3b29ca16727357b.json b/core/lib/dal/.sqlx/query-a36135b5908992324c4308f549ea77a428820fdcea9969aff3b29ca16727357b.json index 1d27af2bbc1..50d3ce5188d 100644 --- a/core/lib/dal/.sqlx/query-a36135b5908992324c4308f549ea77a428820fdcea9969aff3b29ca16727357b.json +++ b/core/lib/dal/.sqlx/query-a36135b5908992324c4308f549ea77a428820fdcea9969aff3b29ca16727357b.json @@ -182,6 +182,16 @@ "ordinal": 35, "name": "upgrade_id", "type_info": "Int4" + }, + { + "ordinal": 36, + "name": "timestamp_asserter_range_start", + "type_info": "Timestamp" + }, + { + "ordinal": 37, + "name": "timestamp_asserter_range_end", + "type_info": "Timestamp" } ], "parameters": { @@ -228,6 +238,8 @@ false, true, true, + true, + true, true ] }, diff --git a/core/lib/dal/.sqlx/query-ab3f97cf96ef769346703e0c132802690bdd139505ba22be3655e306773abc77.json b/core/lib/dal/.sqlx/query-ab3f97cf96ef769346703e0c132802690bdd139505ba22be3655e306773abc77.json new file mode 100644 index 00000000000..2edb0822ac6 --- /dev/null +++ b/core/lib/dal/.sqlx/query-ab3f97cf96ef769346703e0c132802690bdd139505ba22be3655e306773abc77.json @@ -0,0 +1,42 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO\n transactions (\n hash,\n is_priority,\n initiator_address,\n nonce,\n signature,\n gas_limit,\n max_fee_per_gas,\n max_priority_fee_per_gas,\n gas_per_pubdata_limit,\n input,\n data,\n tx_format,\n contract_address,\n value,\n paymaster,\n paymaster_input,\n execution_info,\n received_at,\n timestamp_asserter_range_start,\n timestamp_asserter_range_end,\n created_at,\n updated_at\n )\n VALUES\n (\n $1,\n FALSE,\n $2,\n $3,\n $4,\n $5,\n $6,\n $7,\n $8,\n $9,\n $10,\n $11,\n $12,\n $13,\n $14,\n $15,\n JSONB_BUILD_OBJECT(\n 'gas_used',\n $16::BIGINT,\n 'storage_writes',\n $17::INT,\n 'contracts_used',\n $18::INT\n ),\n $19,\n $20,\n $21,\n NOW(),\n NOW()\n )\n ON CONFLICT (initiator_address, nonce) DO\n UPDATE\n SET\n hash = $1,\n signature = $4,\n gas_limit = $5,\n max_fee_per_gas = $6,\n max_priority_fee_per_gas = $7,\n gas_per_pubdata_limit = $8,\n input = $9,\n data = $10,\n tx_format = $11,\n contract_address = $12,\n value = $13,\n paymaster = $14,\n paymaster_input = $15,\n execution_info\n = JSONB_BUILD_OBJECT(\n 'gas_used',\n $16::BIGINT,\n 'storage_writes',\n $17::INT,\n 'contracts_used',\n $18::INT\n ),\n in_mempool = FALSE,\n received_at = $19,\n timestamp_asserter_range_start = $20,\n timestamp_asserter_range_end = $21,\n created_at = NOW(),\n updated_at = NOW(),\n error = NULL\n WHERE\n transactions.is_priority = FALSE\n AND transactions.miniblock_number IS NULL\n RETURNING\n (\n SELECT\n hash\n FROM\n transactions\n WHERE\n transactions.initiator_address = $2\n AND transactions.nonce = $3\n ) IS NOT NULL AS \"is_replaced!\"\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "is_replaced!", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Bytea", + "Bytea", + "Int8", + "Bytea", + "Numeric", + "Numeric", + "Numeric", + "Numeric", + "Bytea", + "Jsonb", + "Int4", + "Bytea", + "Numeric", + "Bytea", + "Bytea", + "Int8", + "Int4", + "Int4", + "Timestamp", + "Timestamp", + "Timestamp" + ] + }, + "nullable": [ + null + ] + }, + "hash": "ab3f97cf96ef769346703e0c132802690bdd139505ba22be3655e306773abc77" +} diff --git a/core/lib/dal/.sqlx/query-ca428423f278feea2942fd2c78fc5223c9d5e2e42d89bb456d24c601edc06a05.json b/core/lib/dal/.sqlx/query-ca428423f278feea2942fd2c78fc5223c9d5e2e42d89bb456d24c601edc06a05.json deleted file mode 100644 index c234cbe4235..00000000000 --- a/core/lib/dal/.sqlx/query-ca428423f278feea2942fd2c78fc5223c9d5e2e42d89bb456d24c601edc06a05.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO\n transactions (\n hash,\n is_priority,\n initiator_address,\n nonce,\n signature,\n gas_limit,\n max_fee_per_gas,\n max_priority_fee_per_gas,\n gas_per_pubdata_limit,\n input,\n data,\n tx_format,\n contract_address,\n value,\n paymaster,\n paymaster_input,\n execution_info,\n received_at,\n created_at,\n updated_at\n )\n VALUES\n (\n $1,\n FALSE,\n $2,\n $3,\n $4,\n $5,\n $6,\n $7,\n $8,\n $9,\n $10,\n $11,\n $12,\n $13,\n $14,\n $15,\n JSONB_BUILD_OBJECT(\n 'gas_used',\n $16::BIGINT,\n 'storage_writes',\n $17::INT,\n 'contracts_used',\n $18::INT\n ),\n $19,\n NOW(),\n NOW()\n )\n ON CONFLICT (initiator_address, nonce) DO\n UPDATE\n SET\n hash = $1,\n signature = $4,\n gas_limit = $5,\n max_fee_per_gas = $6,\n max_priority_fee_per_gas = $7,\n gas_per_pubdata_limit = $8,\n input = $9,\n data = $10,\n tx_format = $11,\n contract_address = $12,\n value = $13,\n paymaster = $14,\n paymaster_input = $15,\n execution_info\n = JSONB_BUILD_OBJECT(\n 'gas_used',\n $16::BIGINT,\n 'storage_writes',\n $17::INT,\n 'contracts_used',\n $18::INT\n ),\n in_mempool = FALSE,\n received_at = $19,\n created_at = NOW(),\n updated_at = NOW(),\n error = NULL\n WHERE\n transactions.is_priority = FALSE\n AND transactions.miniblock_number IS NULL\n RETURNING\n (\n SELECT\n hash\n FROM\n transactions\n WHERE\n transactions.initiator_address = $2\n AND transactions.nonce = $3\n ) IS NOT NULL AS \"is_replaced!\"\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "is_replaced!", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Bytea", - "Bytea", - "Int8", - "Bytea", - "Numeric", - "Numeric", - "Numeric", - "Numeric", - "Bytea", - "Jsonb", - "Int4", - "Bytea", - "Numeric", - "Bytea", - "Bytea", - "Int8", - "Int4", - "Int4", - "Timestamp" - ] - }, - "nullable": [ - null - ] - }, - "hash": "ca428423f278feea2942fd2c78fc5223c9d5e2e42d89bb456d24c601edc06a05" -} diff --git a/core/lib/dal/.sqlx/query-eb27e1b82b8ecbb9711c417888564a8e245ecee4866264d38146938595b07f37.json b/core/lib/dal/.sqlx/query-eb27e1b82b8ecbb9711c417888564a8e245ecee4866264d38146938595b07f37.json index 2419082dcc2..079ce55bd56 100644 --- a/core/lib/dal/.sqlx/query-eb27e1b82b8ecbb9711c417888564a8e245ecee4866264d38146938595b07f37.json +++ b/core/lib/dal/.sqlx/query-eb27e1b82b8ecbb9711c417888564a8e245ecee4866264d38146938595b07f37.json @@ -182,6 +182,16 @@ "ordinal": 35, "name": "upgrade_id", "type_info": "Int4" + }, + { + "ordinal": 36, + "name": "timestamp_asserter_range_start", + "type_info": "Timestamp" + }, + { + "ordinal": 37, + "name": "timestamp_asserter_range_end", + "type_info": "Timestamp" } ], "parameters": { @@ -226,6 +236,8 @@ false, true, true, + true, + true, true ] }, diff --git a/core/lib/dal/.sqlx/query-f023e5fa599b279acd6ac02dffb7a33a8fea8ab7fdefb7d9210673245a2a6f6c.json b/core/lib/dal/.sqlx/query-f023e5fa599b279acd6ac02dffb7a33a8fea8ab7fdefb7d9210673245a2a6f6c.json index 2cd001b274d..8c43f8865ac 100644 --- a/core/lib/dal/.sqlx/query-f023e5fa599b279acd6ac02dffb7a33a8fea8ab7fdefb7d9210673245a2a6f6c.json +++ b/core/lib/dal/.sqlx/query-f023e5fa599b279acd6ac02dffb7a33a8fea8ab7fdefb7d9210673245a2a6f6c.json @@ -182,6 +182,16 @@ "ordinal": 35, "name": "upgrade_id", "type_info": "Int4" + }, + { + "ordinal": 36, + "name": "timestamp_asserter_range_start", + "type_info": "Timestamp" + }, + { + "ordinal": 37, + "name": "timestamp_asserter_range_end", + "type_info": "Timestamp" } ], "parameters": { @@ -225,6 +235,8 @@ false, true, true, + true, + true, true ] }, diff --git a/core/lib/dal/migrations/20241008073940_add_block_timestamp_asserter.down.sql b/core/lib/dal/migrations/20241008073940_add_block_timestamp_asserter.down.sql new file mode 100644 index 00000000000..87f6a8cb75a --- /dev/null +++ b/core/lib/dal/migrations/20241008073940_add_block_timestamp_asserter.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE transactions +DROP COLUMN timestamp_asserter_range_start, +DROP COLUMN timestamp_asserter_range_end; diff --git a/core/lib/dal/migrations/20241008073940_add_block_timestamp_asserter.up.sql b/core/lib/dal/migrations/20241008073940_add_block_timestamp_asserter.up.sql new file mode 100644 index 00000000000..103a22cb8e3 --- /dev/null +++ b/core/lib/dal/migrations/20241008073940_add_block_timestamp_asserter.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE transactions +ADD COLUMN timestamp_asserter_range_start TIMESTAMP DEFAULT NULL, +ADD COLUMN timestamp_asserter_range_end TIMESTAMP DEFAULT NULL; diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 4cb57798638..ba843bbf92f 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -803,7 +803,7 @@ mod tests { block::{L2BlockHasher, L2BlockHeader}, Address, L2BlockNumber, ProtocolVersion, ProtocolVersionId, }; - use zksync_vm_interface::TransactionExecutionMetrics; + use zksync_vm_interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use super::*; use crate::{ @@ -1090,7 +1090,11 @@ mod tests { let mut tx_results = vec![]; for (i, tx) in transactions.into_iter().enumerate() { conn.transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); let mut tx_result = mock_execution_result(tx); diff --git a/core/lib/dal/src/models/storage_transaction.rs b/core/lib/dal/src/models/storage_transaction.rs index 78daaebb335..459a3ec0c0f 100644 --- a/core/lib/dal/src/models/storage_transaction.rs +++ b/core/lib/dal/src/models/storage_transaction.rs @@ -12,9 +12,9 @@ use zksync_types::{ transaction_request::PaymasterParams, web3::Bytes, Address, Execute, ExecuteTransactionCommon, L1TxCommonData, L2ChainId, L2TxCommonData, Nonce, - PackedEthSignature, PriorityOpId, ProtocolVersionId, Transaction, EIP_1559_TX_TYPE, - EIP_2930_TX_TYPE, EIP_712_TX_TYPE, H160, H256, PRIORITY_OPERATION_L2_TX_TYPE, - PROTOCOL_UPGRADE_TX_TYPE, U256, U64, + PackedEthSignature, PriorityOpId, ProtocolVersionId, Transaction, + TransactionTimeRangeConstraint, EIP_1559_TX_TYPE, EIP_2930_TX_TYPE, EIP_712_TX_TYPE, H160, + H256, PRIORITY_OPERATION_L2_TX_TYPE, PROTOCOL_UPGRADE_TX_TYPE, U256, U64, }; use zksync_utils::{bigdecimal_to_u256, h256_to_account_address}; use zksync_vm_interface::Call; @@ -68,6 +68,9 @@ pub struct StorageTransaction { pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, + pub timestamp_asserter_range_start: Option, + pub timestamp_asserter_range_end: Option, + // DEPRECATED. pub l1_block_number: Option, } @@ -321,6 +324,18 @@ impl From for Transaction { } } +impl From<&StorageTransaction> for TransactionTimeRangeConstraint { + fn from(tx: &StorageTransaction) -> Self { + Self { + timestamp_asserter_range: tx.timestamp_asserter_range_start.and_then(|start| { + tx.timestamp_asserter_range_end.map(|end| { + (start.and_utc().timestamp() as u64)..(end.and_utc().timestamp() as u64) + }) + }), + } + } +} + #[derive(sqlx::FromRow)] pub(crate) struct StorageTransactionReceipt { pub error: Option, @@ -392,7 +407,6 @@ impl From for TransactionReceipt { logs: vec![], l2_to_l1_logs: vec![], status, - root: block_hash, logs_bloom: Default::default(), // Even though the Rust SDK recommends us to supply "None" for legacy transactions // we always supply some number anyway to have the same behavior as most popular RPCs diff --git a/core/lib/dal/src/pruning_dal/tests.rs b/core/lib/dal/src/pruning_dal/tests.rs index 4f94ff7f63d..70dda48d8c8 100644 --- a/core/lib/dal/src/pruning_dal/tests.rs +++ b/core/lib/dal/src/pruning_dal/tests.rs @@ -5,7 +5,7 @@ use zksync_types::{ tx::IncludedTxLocation, AccountTreeId, Address, L1BatchNumber, L2BlockNumber, L2ChainId, ProtocolVersion, ProtocolVersionId, StorageKey, StorageLog, H256, }; -use zksync_vm_interface::TransactionExecutionMetrics; +use zksync_vm_interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use super::*; use crate::{ @@ -457,7 +457,11 @@ async fn transactions_are_handled_correctly_after_pruning() { let tx = mock_l2_transaction(); let tx_hash = tx.hash(); conn.transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); conn.blocks_dal() diff --git a/core/lib/dal/src/sync_dal.rs b/core/lib/dal/src/sync_dal.rs index 55e6543c028..4372a83f1fe 100644 --- a/core/lib/dal/src/sync_dal.rs +++ b/core/lib/dal/src/sync_dal.rs @@ -113,7 +113,7 @@ mod tests { block::{L1BatchHeader, L2BlockHeader}, Address, L1BatchNumber, ProtocolVersion, ProtocolVersionId, Transaction, }; - use zksync_vm_interface::TransactionExecutionMetrics; + use zksync_vm_interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use super::*; use crate::{ @@ -168,7 +168,11 @@ mod tests { }; let tx = mock_l2_transaction(); conn.transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); conn.blocks_dal() diff --git a/core/lib/dal/src/tests/mod.rs b/core/lib/dal/src/tests/mod.rs index baa2ee58485..11d4e55a55a 100644 --- a/core/lib/dal/src/tests/mod.rs +++ b/core/lib/dal/src/tests/mod.rs @@ -17,8 +17,8 @@ use zksync_types::{ L2ChainId, PriorityOpId, ProtocolVersion, ProtocolVersionId, H160, H256, U256, }; use zksync_vm_interface::{ - TransactionExecutionMetrics, TransactionExecutionResult, TxExecutionStatus, VmEvent, - VmExecutionMetrics, + tracer::ValidationTraces, TransactionExecutionMetrics, TransactionExecutionResult, + TxExecutionStatus, VmEvent, VmExecutionMetrics, }; use crate::{ @@ -210,14 +210,22 @@ async fn workflow_with_submit_tx_equal_hashes() { let tx = mock_l2_transaction(); let result = transactions_dal - .insert_transaction_l2(&tx, mock_tx_execution_metrics()) + .insert_transaction_l2( + &tx, + mock_tx_execution_metrics(), + ValidationTraces::default(), + ) .await .unwrap(); assert_eq!(result, L2TxSubmissionResult::Added); let result = transactions_dal - .insert_transaction_l2(&tx, mock_tx_execution_metrics()) + .insert_transaction_l2( + &tx, + mock_tx_execution_metrics(), + ValidationTraces::default(), + ) .await .unwrap(); @@ -236,7 +244,11 @@ async fn workflow_with_submit_tx_diff_hashes() { let initiator_address = tx.common_data.initiator_address; let result = transactions_dal - .insert_transaction_l2(&tx, mock_tx_execution_metrics()) + .insert_transaction_l2( + &tx, + mock_tx_execution_metrics(), + ValidationTraces::default(), + ) .await .unwrap(); @@ -246,7 +258,11 @@ async fn workflow_with_submit_tx_diff_hashes() { tx.common_data.nonce = nonce; tx.common_data.initiator_address = initiator_address; let result = transactions_dal - .insert_transaction_l2(&tx, mock_tx_execution_metrics()) + .insert_transaction_l2( + &tx, + mock_tx_execution_metrics(), + ValidationTraces::default(), + ) .await .unwrap(); @@ -270,13 +286,21 @@ async fn remove_stuck_txs() { let mut tx = mock_l2_transaction(); tx.received_timestamp_ms = unix_timestamp_ms() - Duration::new(1000, 0).as_millis() as u64; transactions_dal - .insert_transaction_l2(&tx, mock_tx_execution_metrics()) + .insert_transaction_l2( + &tx, + mock_tx_execution_metrics(), + ValidationTraces::default(), + ) .await .unwrap(); // Tx in mempool let tx = mock_l2_transaction(); transactions_dal - .insert_transaction_l2(&tx, mock_tx_execution_metrics()) + .insert_transaction_l2( + &tx, + mock_tx_execution_metrics(), + ValidationTraces::default(), + ) .await .unwrap(); @@ -293,7 +317,11 @@ async fn remove_stuck_txs() { executed_tx.received_timestamp_ms = unix_timestamp_ms() - Duration::new(1000, 0).as_millis() as u64; transactions_dal - .insert_transaction_l2(&executed_tx, mock_tx_execution_metrics()) + .insert_transaction_l2( + &executed_tx, + mock_tx_execution_metrics(), + ValidationTraces::default(), + ) .await .unwrap(); diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index 5314e9799b3..9c0889ebfc7 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt, time::Duration}; +use std::{cmp::min, collections::HashMap, fmt, time::Duration}; use bigdecimal::BigDecimal; use itertools::Itertools; @@ -12,12 +12,13 @@ use zksync_db_connection::{ use zksync_types::{ block::L2BlockExecutionData, debug_flat_call::CallTraceMeta, l1::L1Tx, l2::L2Tx, protocol_upgrade::ProtocolUpgradeTx, Address, ExecuteTransactionCommon, L1BatchNumber, - L1BlockNumber, L2BlockNumber, PriorityOpId, ProtocolVersionId, Transaction, H256, - PROTOCOL_UPGRADE_TX_TYPE, U256, + L1BlockNumber, L2BlockNumber, PriorityOpId, ProtocolVersionId, Transaction, + TransactionTimeRangeConstraint, H256, PROTOCOL_UPGRADE_TX_TYPE, U256, }; use zksync_utils::u256_to_big_decimal; use zksync_vm_interface::{ - Call, TransactionExecutionMetrics, TransactionExecutionResult, TxExecutionStatus, + tracer::ValidationTraces, Call, TransactionExecutionMetrics, TransactionExecutionResult, + TxExecutionStatus, }; use crate::{ @@ -264,6 +265,7 @@ impl TransactionsDal<'_, '_> { &mut self, tx: &L2Tx, exec_info: TransactionExecutionMetrics, + validation_traces: ValidationTraces, ) -> DalResult { let tx_hash = tx.hash(); let is_duplicate = sqlx::query!( @@ -314,6 +316,16 @@ impl TransactionsDal<'_, '_> { let nanosecs = ((tx.received_timestamp_ms % 1000) * 1_000_000) as u32; #[allow(deprecated)] let received_at = NaiveDateTime::from_timestamp_opt(secs, nanosecs).unwrap(); + let max_timestamp = NaiveDateTime::MAX.and_utc().timestamp() as u64; + #[allow(deprecated)] + let timestamp_asserter_range_start = + validation_traces.timestamp_asserter_range.clone().map(|x| { + NaiveDateTime::from_timestamp_opt(min(x.start, max_timestamp) as i64, 0).unwrap() + }); + #[allow(deprecated)] + let timestamp_asserter_range_end = validation_traces.timestamp_asserter_range.map(|x| { + NaiveDateTime::from_timestamp_opt(min(x.end, max_timestamp) as i64, 0).unwrap() + }); // Besides just adding or updating(on conflict) the record, we want to extract some info // from the query below, to indicate what actually happened: // 1) transaction is added @@ -346,6 +358,8 @@ impl TransactionsDal<'_, '_> { paymaster_input, execution_info, received_at, + timestamp_asserter_range_start, + timestamp_asserter_range_end, created_at, updated_at ) @@ -376,6 +390,8 @@ impl TransactionsDal<'_, '_> { $18::INT ), $19, + $20, + $21, NOW(), NOW() ) @@ -406,6 +422,8 @@ impl TransactionsDal<'_, '_> { ), in_mempool = FALSE, received_at = $19, + timestamp_asserter_range_start = $20, + timestamp_asserter_range_end = $21, created_at = NOW(), updated_at = NOW(), error = NULL @@ -441,7 +459,9 @@ impl TransactionsDal<'_, '_> { exec_info.gas_used as i64, (exec_info.initial_storage_writes + exec_info.repeated_storage_writes) as i32, exec_info.contracts_used as i32, - received_at + received_at, + timestamp_asserter_range_start, + timestamp_asserter_range_end, ) .instrument("insert_transaction_l2") .with_arg("tx_hash", &tx_hash) @@ -1728,7 +1748,7 @@ impl TransactionsDal<'_, '_> { gas_per_pubdata: u32, fee_per_gas: u64, limit: usize, - ) -> DalResult> { + ) -> DalResult> { let stashed_addresses: Vec<_> = stashed_accounts.iter().map(Address::as_bytes).collect(); sqlx::query!( r#" @@ -1819,8 +1839,14 @@ impl TransactionsDal<'_, '_> { .fetch_all(self.storage) .await?; - let transactions = transactions.into_iter().map(|tx| tx.into()).collect(); - Ok(transactions) + let transactions_with_constraints = transactions + .into_iter() + .map(|tx| { + let constraint = TransactionTimeRangeConstraint::from(&tx); + (tx.into(), constraint) + }) + .collect(); + Ok(transactions_with_constraints) } pub async fn reset_mempool(&mut self) -> DalResult<()> { @@ -2212,6 +2238,29 @@ impl TransactionsDal<'_, '_> { .fetch_optional(self.storage) .await } + + pub async fn get_storage_tx_by_hash( + &mut self, + hash: H256, + ) -> DalResult> { + sqlx::query_as!( + StorageTransaction, + r#" + SELECT + * + FROM + transactions + WHERE + hash = $1 + "#, + hash.as_bytes() + ) + .map(Into::into) + .instrument("get_storage_tx_by_hash") + .with_arg("hash", &hash) + .fetch_optional(self.storage) + .await + } } #[cfg(test)] @@ -2240,7 +2289,11 @@ mod tests { let tx = mock_l2_transaction(); let tx_hash = tx.hash(); conn.transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); let mut tx_result = mock_execution_result(tx); diff --git a/core/lib/dal/src/transactions_web3_dal.rs b/core/lib/dal/src/transactions_web3_dal.rs index c2209bb9c93..44d7ed89c47 100644 --- a/core/lib/dal/src/transactions_web3_dal.rs +++ b/core/lib/dal/src/transactions_web3_dal.rs @@ -493,7 +493,7 @@ mod tests { use std::collections::HashMap; use zksync_types::{l2::L2Tx, Nonce, ProtocolVersion, ProtocolVersionId}; - use zksync_vm_interface::TransactionExecutionMetrics; + use zksync_vm_interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use super::*; use crate::{ @@ -509,7 +509,11 @@ mod tests { for tx in &txs { conn.transactions_dal() - .insert_transaction_l2(tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); } @@ -747,7 +751,11 @@ mod tests { tx.common_data.initiator_address = initiator; tx_by_nonce.insert(nonce, tx.clone()); conn.transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); } @@ -816,7 +824,11 @@ mod tests { tx.common_data.nonce = Nonce(1); tx.common_data.initiator_address = initiator; conn.transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index 3792f356be4..ae4e1d1d5b4 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -74,6 +74,7 @@ mod tests { base_token_addr: Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS), chain_admin_addr: Some(addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), l2_da_validator_addr: Some(addr("0xed6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), + l2_timestamp_asserter_addr: Some(addr("0x0000000000000000000000000000000000000002")), } } @@ -102,6 +103,7 @@ CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347 CONTRACTS_BASE_TOKEN_ADDR="0x0000000000000000000000000000000000000001" CONTRACTS_CHAIN_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff" CONTRACTS_L2_DA_VALIDATOR_ADDR="0xed6fa5c14e7550b4caf2aa2818d24c69cbc347ff" +CONTRACTS_L2_TIMESTAMP_ASSERTER_ADDR="0x0000000000000000000000000000000000000002" "#; lock.set_env(config); diff --git a/core/lib/env_config/src/da_client.rs b/core/lib/env_config/src/da_client.rs index 1043786fc1e..8ceeb215faf 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -5,7 +5,10 @@ use zksync_config::configs::{ avail::{ AvailClientConfig, AvailSecrets, AVAIL_FULL_CLIENT_NAME, AVAIL_GAS_RELAY_CLIENT_NAME, }, - DAClientConfig, AVAIL_CLIENT_CONFIG_NAME, OBJECT_STORE_CLIENT_CONFIG_NAME, + celestia::CelestiaSecrets, + eigen::EigenSecrets, + DAClientConfig, AVAIL_CLIENT_CONFIG_NAME, CELESTIA_CLIENT_CONFIG_NAME, + EIGEN_CLIENT_CONFIG_NAME, OBJECT_STORE_CLIENT_CONFIG_NAME, }, secrets::DataAvailabilitySecrets, AvailConfig, @@ -19,7 +22,7 @@ impl FromEnv for DAClientConfig { let config = match client_tag.as_str() { AVAIL_CLIENT_CONFIG_NAME => Self::Avail(AvailConfig { bridge_api_url: env::var("DA_BRIDGE_API_URL").ok().unwrap(), - timeout: env::var("DA_TIMEOUT")?.parse()?, + timeout_ms: env::var("DA_TIMEOUT_MS")?.parse()?, config: match env::var("DA_AVAIL_CLIENT_TYPE")?.as_str() { AVAIL_FULL_CLIENT_NAME => { AvailClientConfig::FullClient(envy_load("da_avail_full_client", "DA_")?) @@ -30,6 +33,8 @@ impl FromEnv for DAClientConfig { _ => anyhow::bail!("Unknown Avail DA client type"), }, }), + CELESTIA_CLIENT_CONFIG_NAME => Self::Celestia(envy_load("da_celestia_config", "DA_")?), + EIGEN_CLIENT_CONFIG_NAME => Self::Eigen(envy_load("da_eigen_config", "DA_")?), OBJECT_STORE_CLIENT_CONFIG_NAME => { Self::ObjectStore(envy_load("da_object_store", "DA_")?) } @@ -45,11 +50,11 @@ impl FromEnv for DataAvailabilitySecrets { let client_tag = std::env::var("DA_CLIENT")?; let secrets = match client_tag.as_str() { AVAIL_CLIENT_CONFIG_NAME => { - let seed_phrase: Option = + let seed_phrase: Option = env::var("DA_SECRETS_SEED_PHRASE") .ok() .map(|s| s.parse().unwrap()); - let gas_relay_api_key: Option = + let gas_relay_api_key: Option = env::var("DA_SECRETS_GAS_RELAY_API_KEY") .ok() .map(|s| s.parse().unwrap()); @@ -61,6 +66,21 @@ impl FromEnv for DataAvailabilitySecrets { gas_relay_api_key, }) } + CELESTIA_CLIENT_CONFIG_NAME => { + let private_key = env::var("DA_SECRETS_PRIVATE_KEY") + .map_err(|e| anyhow::format_err!("Celestia private key not found: {}", e))? + .parse() + .map_err(|e| anyhow::format_err!("failed to parse the private key: {}", e))?; + Self::Celestia(CelestiaSecrets { private_key }) + } + EIGEN_CLIENT_CONFIG_NAME => { + let private_key = env::var("DA_SECRETS_PRIVATE_KEY") + .map_err(|e| anyhow::format_err!("Eigen private key not found: {}", e))? + .parse() + .map_err(|e| anyhow::format_err!("failed to parse the private key: {}", e))?; + Self::Eigen(EigenSecrets { private_key }) + } + _ => anyhow::bail!("Unknown DA client name: {}", client_tag), }; @@ -78,7 +98,7 @@ mod tests { }, object_store::ObjectStoreMode::GCS, }, - AvailConfig, ObjectStoreConfig, + AvailConfig, CelestiaConfig, EigenConfig, ObjectStoreConfig, }; use super::*; @@ -118,11 +138,11 @@ mod tests { api_node_url: &str, bridge_api_url: &str, app_id: u32, - timeout: usize, + timeout_ms: usize, ) -> DAClientConfig { DAClientConfig::Avail(AvailConfig { bridge_api_url: bridge_api_url.to_string(), - timeout, + timeout_ms, config: AvailClientConfig::FullClient(AvailDefaultConfig { api_node_url: api_node_url.to_string(), app_id, @@ -138,7 +158,7 @@ mod tests { DA_AVAIL_CLIENT_TYPE="FullClient" DA_BRIDGE_API_URL="localhost:54321" - DA_TIMEOUT="2" + DA_TIMEOUT_MS="2000" DA_API_NODE_URL="localhost:12345" DA_APP_ID="1" @@ -153,7 +173,7 @@ mod tests { "localhost:12345", "localhost:54321", "1".parse::().unwrap(), - "2".parse::().unwrap(), + "2000".parse::().unwrap(), ) ); } @@ -170,8 +190,10 @@ mod tests { let (actual_seed, actual_key) = match DataAvailabilitySecrets::from_env().unwrap() { DataAvailabilitySecrets::Avail(avail) => (avail.seed_phrase, avail.gas_relay_api_key), + _ => { + panic!("Avail config expected") + } }; - assert_eq!( (actual_seed.unwrap(), actual_key), ( @@ -182,4 +204,85 @@ mod tests { ) ); } + + fn expected_celestia_da_layer_config( + api_node_url: &str, + namespace: &str, + chain_id: &str, + timeout_ms: u64, + ) -> DAClientConfig { + DAClientConfig::Celestia(CelestiaConfig { + api_node_url: api_node_url.to_string(), + namespace: namespace.to_string(), + chain_id: chain_id.to_string(), + timeout_ms, + }) + } + + #[test] + fn from_env_celestia_client() { + let mut lock = MUTEX.lock(); + let config = r#" + DA_CLIENT="Celestia" + DA_API_NODE_URL="localhost:12345" + DA_NAMESPACE="0x1234567890abcdef" + DA_CHAIN_ID="mocha-4" + DA_TIMEOUT_MS="7000" + "#; + lock.set_env(config); + + let actual = DAClientConfig::from_env().unwrap(); + assert_eq!( + actual, + expected_celestia_da_layer_config( + "localhost:12345", + "0x1234567890abcdef", + "mocha-4", + 7000 + ) + ); + } + + #[test] + fn from_env_eigen_client() { + let mut lock = MUTEX.lock(); + let config = r#" + DA_CLIENT="Eigen" + DA_RPC_NODE_URL="localhost:12345" + DA_INCLUSION_POLLING_INTERVAL_MS="1000" + "#; + lock.set_env(config); + + let actual = DAClientConfig::from_env().unwrap(); + assert_eq!( + actual, + DAClientConfig::Eigen(EigenConfig { + rpc_node_url: "localhost:12345".to_string(), + inclusion_polling_interval_ms: 1000, + }) + ); + } + + #[test] + fn from_env_celestia_secrets() { + let mut lock = MUTEX.lock(); + let config = r#" + DA_CLIENT="Celestia" + DA_SECRETS_PRIVATE_KEY="f55baf7c0e4e33b1d78fbf52f069c426bc36cff1aceb9bc8f45d14c07f034d73" + "#; + + lock.set_env(config); + + let DataAvailabilitySecrets::Celestia(actual) = + DataAvailabilitySecrets::from_env().unwrap() + else { + panic!("expected Celestia config") + }; + assert_eq!( + actual.private_key, + "f55baf7c0e4e33b1d78fbf52f069c426bc36cff1aceb9bc8f45d14c07f034d73" + .parse() + .unwrap() + ); + } } diff --git a/core/lib/env_config/src/database.rs b/core/lib/env_config/src/database.rs index c067c96de73..119d64b7738 100644 --- a/core/lib/env_config/src/database.rs +++ b/core/lib/env_config/src/database.rs @@ -1,23 +1,8 @@ -use std::{env, error, str::FromStr}; +use std::env; -use anyhow::Context as _; use zksync_config::{configs::DatabaseSecrets, DBConfig, PostgresConfig}; -use crate::{envy_load, FromEnv}; - -fn parse_optional_var(name: &str) -> anyhow::Result> -where - T: FromStr, - T::Err: 'static + error::Error + Send + Sync, -{ - env::var(name) - .ok() - .map(|val| { - val.parse() - .with_context(|| format!("failed to parse env variable {name}")) - }) - .transpose() -} +use crate::{envy_load, utils::parse_optional_var, FromEnv}; impl FromEnv for DBConfig { fn from_env() -> anyhow::Result { diff --git a/core/lib/env_config/src/external_price_api_client.rs b/core/lib/env_config/src/external_price_api_client.rs index 60ddeea8315..07ab40e059d 100644 --- a/core/lib/env_config/src/external_price_api_client.rs +++ b/core/lib/env_config/src/external_price_api_client.rs @@ -43,6 +43,7 @@ mod tests { numerator: Some(100), denominator: Some(1), fluctuation: Some(10), + next_value_fluctuation: 1, }), } } @@ -57,6 +58,7 @@ mod tests { EXTERNAL_PRICE_API_CLIENT_FORCED_NUMERATOR=100 EXTERNAL_PRICE_API_CLIENT_FORCED_DENOMINATOR=1 EXTERNAL_PRICE_API_CLIENT_FORCED_FLUCTUATION=10 + EXTERNAL_PRICE_API_CLIENT_FORCED_NEXT_VALUE_FLUCTUATION=1 "#; lock.set_env(config); diff --git a/core/lib/env_config/src/lib.rs b/core/lib/env_config/src/lib.rs index b72c2c5d5b9..325288056b3 100644 --- a/core/lib/env_config/src/lib.rs +++ b/core/lib/env_config/src/lib.rs @@ -33,6 +33,7 @@ mod vm_runner; mod wallets; mod da_client; +mod timestamp_asserter; pub trait FromEnv: Sized { fn from_env() -> anyhow::Result; diff --git a/core/lib/env_config/src/timestamp_asserter.rs b/core/lib/env_config/src/timestamp_asserter.rs new file mode 100644 index 00000000000..df586f5925e --- /dev/null +++ b/core/lib/env_config/src/timestamp_asserter.rs @@ -0,0 +1,34 @@ +use zksync_config::configs::chain::TimestampAsserterConfig; + +use crate::{envy_load, FromEnv}; + +impl FromEnv for TimestampAsserterConfig { + fn from_env() -> anyhow::Result { + envy_load("timestamp_asserter", "TIMESTAMP_ASSERTER_") + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::EnvMutex; + + static MUTEX: EnvMutex = EnvMutex::new(); + + #[test] + fn from_env_timestamp_asserter() { + let mut lock = MUTEX.lock(); + let config = r#" + TIMESTAMP_ASSERTER_MIN_TIME_TILL_END_SEC=2 + "#; + lock.set_env(config); + + let actual = TimestampAsserterConfig::from_env().unwrap(); + assert_eq!( + actual, + TimestampAsserterConfig { + min_time_till_end_sec: 2, + } + ); + } +} diff --git a/core/lib/env_config/src/utils.rs b/core/lib/env_config/src/utils.rs index 211e73ae2b1..9f363777bf6 100644 --- a/core/lib/env_config/src/utils.rs +++ b/core/lib/env_config/src/utils.rs @@ -1,3 +1,6 @@ +use std::{env, error, str::FromStr}; + +use anyhow::Context; use zksync_config::configs::PrometheusConfig; use crate::{envy_load, FromEnv}; @@ -7,3 +10,17 @@ impl FromEnv for PrometheusConfig { envy_load("prometheus", "API_PROMETHEUS_") } } + +pub fn parse_optional_var(name: &str) -> anyhow::Result> +where + T: FromStr, + T::Err: 'static + error::Error + Send + Sync, +{ + env::var(name) + .ok() + .map(|val| { + val.parse() + .with_context(|| format!("failed to parse env variable {name}")) + }) + .transpose() +} diff --git a/core/lib/external_price_api/src/forced_price_client.rs b/core/lib/external_price_api/src/forced_price_client.rs index a18c03fd8ca..cca2e7cce2a 100644 --- a/core/lib/external_price_api/src/forced_price_client.rs +++ b/core/lib/external_price_api/src/forced_price_client.rs @@ -1,17 +1,23 @@ -use std::num::NonZeroU64; +use std::{ + cmp::{max, min}, + num::NonZeroU64, +}; use async_trait::async_trait; use rand::Rng; +use tokio::sync::Mutex; use zksync_config::configs::ExternalPriceApiClientConfig; use zksync_types::{base_token_ratio::BaseTokenAPIRatio, Address}; use crate::PriceAPIClient; // Struct for a forced price "client" (conversion ratio is always a configured "forced" ratio). -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ForcedPriceClient { ratio: BaseTokenAPIRatio, + previous_numerator: Mutex, fluctuation: Option, + next_value_fluctuation: u32, } impl ForcedPriceClient { @@ -29,42 +35,70 @@ impl ForcedPriceClient { let fluctuation = forced_price_client_config .fluctuation .map(|x| x.clamp(0, 100)); + let next_value_fluctuation = forced_price_client_config + .next_value_fluctuation + .clamp(0, 100); - Self { - ratio: BaseTokenAPIRatio { + let ratio = if numerator < 100 && fluctuation.is_some_and(|f| f > 0) { + // If numerator is too small we need to multiply by 100 to make sure fluctuations can be applied + BaseTokenAPIRatio { + numerator: NonZeroU64::new(numerator * 100).unwrap(), + denominator: NonZeroU64::new(denominator * 100).unwrap(), + ratio_timestamp: chrono::Utc::now(), + } + } else { + BaseTokenAPIRatio { numerator: NonZeroU64::new(numerator).unwrap(), denominator: NonZeroU64::new(denominator).unwrap(), ratio_timestamp: chrono::Utc::now(), - }, + } + }; + + Self { + ratio, + previous_numerator: Mutex::new(NonZeroU64::new(numerator).unwrap()), fluctuation, + next_value_fluctuation, } } } #[async_trait] impl PriceAPIClient for ForcedPriceClient { - // Returns a ratio which is 10% higher or lower than the configured forced ratio. + /// Returns the configured ratio with fluctuation applied if enabled async fn fetch_ratio(&self, _token_address: Address) -> anyhow::Result { - if let Some(x) = self.fluctuation { - if x != 0 { - let mut rng = rand::thread_rng(); - - let mut adjust_range = |value: NonZeroU64| { - let value_f64 = value.get() as f64; - let min = (value_f64 * (1.0 - x as f64 / 100.0)).round() as u64; - let max = (value_f64 * (1.0 + x as f64 / 100.0)).round() as u64; - rng.gen_range(min..=max) - }; - let new_numerator = adjust_range(self.ratio.numerator); - let new_denominator = adjust_range(self.ratio.denominator); + if let Some(fluctation) = self.fluctuation { + let mut previous_numerator = self.previous_numerator.lock().await; + let mut rng = rand::thread_rng(); + let numerator_range = ( + max( + (self.ratio.numerator.get() as f64 * (1.0 - (fluctation as f64 / 100.0))) + .round() as u64, + (previous_numerator.get() as f64 + * (1.0 - (self.next_value_fluctuation as f64 / 100.0))) + .round() as u64, + ), + min( + (self.ratio.numerator.get() as f64 * (1.0 + (fluctation as f64 / 100.0))) + .round() as u64, + (previous_numerator.get() as f64 + * (1.0 + (self.next_value_fluctuation as f64 / 100.0))) + .round() as u64, + ), + ); - return Ok(BaseTokenAPIRatio { - numerator: NonZeroU64::new(new_numerator).unwrap_or(self.ratio.numerator), - denominator: NonZeroU64::new(new_denominator).unwrap_or(self.ratio.denominator), - ratio_timestamp: chrono::Utc::now(), - }); - } + let new_numerator = + NonZeroU64::new(rng.gen_range(numerator_range.0..=numerator_range.1)) + .unwrap_or(self.ratio.numerator); + let adjusted_ratio = BaseTokenAPIRatio { + numerator: new_numerator, + denominator: self.ratio.denominator, + ratio_timestamp: chrono::Utc::now(), + }; + *previous_numerator = new_numerator; + Ok(adjusted_ratio) + } else { + Ok(self.ratio) } - Ok(self.ratio) } } diff --git a/core/lib/mempool/src/mempool_store.rs b/core/lib/mempool/src/mempool_store.rs index f6f9b72f9b6..70176b456dd 100644 --- a/core/lib/mempool/src/mempool_store.rs +++ b/core/lib/mempool/src/mempool_store.rs @@ -2,6 +2,7 @@ use std::collections::{hash_map, BTreeSet, HashMap}; use zksync_types::{ l1::L1Tx, l2::L2Tx, Address, ExecuteTransactionCommon, Nonce, PriorityOpId, Transaction, + TransactionTimeRangeConstraint, }; use crate::types::{AccountTransactions, L2TxFilter, MempoolScore}; @@ -54,10 +55,10 @@ impl MempoolStore { /// in other cases mempool relies on state keeper and its internal state to keep that info up to date pub fn insert( &mut self, - transactions: Vec, + transactions: Vec<(Transaction, TransactionTimeRangeConstraint)>, initial_nonces: HashMap, ) { - for transaction in transactions { + for (transaction, constraint) in transactions { let Transaction { common_data, execute, @@ -85,6 +86,7 @@ impl MempoolStore { received_timestamp_ms, raw_bytes, }, + constraint, &initial_nonces, ); } @@ -95,20 +97,36 @@ impl MempoolStore { } } + #[cfg(test)] + pub fn insert_without_constraints( + &mut self, + transactions: Vec, + initial_nonces: HashMap, + ) { + self.insert( + transactions + .into_iter() + .map(|x| (x, TransactionTimeRangeConstraint::default())) + .collect(), + initial_nonces, + ); + } + fn insert_l2_transaction( &mut self, transaction: L2Tx, + constraint: TransactionTimeRangeConstraint, initial_nonces: &HashMap, ) { let account = transaction.initiator_account(); let metadata = match self.l2_transactions_per_account.entry(account) { - hash_map::Entry::Occupied(mut txs) => txs.get_mut().insert(transaction), + hash_map::Entry::Occupied(mut txs) => txs.get_mut().insert(transaction, constraint), hash_map::Entry::Vacant(entry) => { let account_nonce = initial_nonces.get(&account).cloned().unwrap_or(Nonce(0)); entry .insert(AccountTransactions::new(account_nonce)) - .insert(transaction) + .insert(transaction, constraint) } }; if let Some(score) = metadata.previous_score { @@ -133,10 +151,17 @@ impl MempoolStore { } /// Returns next transaction for execution from mempool - pub fn next_transaction(&mut self, filter: &L2TxFilter) -> Option { + pub fn next_transaction( + &mut self, + filter: &L2TxFilter, + ) -> Option<(Transaction, TransactionTimeRangeConstraint)> { if let Some(transaction) = self.l1_transactions.remove(&self.next_priority_id) { self.next_priority_id += 1; - return Some(transaction.into()); + // L1 transactions can't use block.timestamp in AA and hence do not need to have a constraint + return Some(( + transaction.into(), + TransactionTimeRangeConstraint::default(), + )); } let mut removed = 0; @@ -163,7 +188,7 @@ impl MempoolStore { self.stashed_accounts.push(stashed_pointer.account); } // insert pointer to the next transaction if it exists - let (transaction, score) = self + let (transaction, constraint, score) = self .l2_transactions_per_account .get_mut(&tx_pointer.account) .expect("mempool: dangling pointer in priority queue") @@ -176,28 +201,31 @@ impl MempoolStore { .size .checked_sub((removed + 1) as u64) .expect("mempool size can't be negative"); - Some(transaction.into()) + Some((transaction.into(), constraint)) } /// When a state_keeper starts the block over after a rejected transaction, /// we have to rollback the nonces/ids in the mempool and /// reinsert the transactions from the block back into mempool. - pub fn rollback(&mut self, tx: &Transaction) { + pub fn rollback(&mut self, tx: &Transaction) -> TransactionTimeRangeConstraint { // rolling back the nonces and priority ids match &tx.common_data { ExecuteTransactionCommon::L1(data) => { // reset next priority id self.next_priority_id = self.next_priority_id.min(data.serial_id); + TransactionTimeRangeConstraint::default() } ExecuteTransactionCommon::L2(_) => { - if let Some(score) = self + if let Some((score, constraint)) = self .l2_transactions_per_account .get_mut(&tx.initiator_account()) .expect("account is not available in mempool") .reset(tx) { self.l2_priority_queue.remove(&score); + return constraint; } + TransactionTimeRangeConstraint::default() } ExecuteTransactionCommon::ProtocolUpgrade(_) => { panic!("Protocol upgrade tx is not supposed to be in mempool"); diff --git a/core/lib/mempool/src/tests.rs b/core/lib/mempool/src/tests.rs index b84ab7d5765..d40158ae955 100644 --- a/core/lib/mempool/src/tests.rs +++ b/core/lib/mempool/src/tests.rs @@ -9,7 +9,7 @@ use zksync_types::{ l1::{OpProcessingType, PriorityQueueType}, l2::L2Tx, Address, Execute, ExecuteTransactionCommon, L1TxCommonData, Nonce, PriorityOpId, Transaction, - H256, U256, + TransactionTimeRangeConstraint, H256, U256, }; use crate::{mempool_store::MempoolStore, types::L2TxFilter}; @@ -27,7 +27,7 @@ fn basic_flow() { gen_l2_tx(account1, Nonce(1)), ]; assert_eq!(mempool.next_transaction(&L2TxFilter::default()), None); - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); assert_eq!( view(mempool.next_transaction(&L2TxFilter::default())), (account0, 0) @@ -46,7 +46,7 @@ fn basic_flow() { ); assert_eq!(mempool.next_transaction(&L2TxFilter::default()), None); // unclog second account and insert more transactions - mempool.insert( + mempool.insert_without_constraints( vec![gen_l2_tx(account1, Nonce(0)), gen_l2_tx(account0, Nonce(3))], HashMap::new(), ); @@ -72,10 +72,10 @@ fn missing_txns() { ]; let mut nonces = HashMap::new(); nonces.insert(account, Nonce(5)); - mempool.insert(transactions, nonces); + mempool.insert_without_constraints(transactions, nonces); assert_eq!(mempool.next_transaction(&L2TxFilter::default()), None); // missing transaction unclogs mempool - mempool.insert(vec![gen_l2_tx(account, Nonce(5))], HashMap::new()); + mempool.insert_without_constraints(vec![gen_l2_tx(account, Nonce(5))], HashMap::new()); assert_eq!( view(mempool.next_transaction(&L2TxFilter::default())), (account, 5) @@ -90,7 +90,7 @@ fn missing_txns() { ); // filling remaining gap - mempool.insert(vec![gen_l2_tx(account, Nonce(8))], HashMap::new()); + mempool.insert_without_constraints(vec![gen_l2_tx(account, Nonce(8))], HashMap::new()); assert_eq!( view(mempool.next_transaction(&L2TxFilter::default())), (account, 8) @@ -110,10 +110,11 @@ fn prioritize_l1_txns() { gen_l2_tx(account, Nonce(1)), gen_l1_tx(PriorityOpId(0)), ]; - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); assert!(mempool .next_transaction(&L2TxFilter::default()) .unwrap() + .0 .is_l1()) } @@ -125,13 +126,14 @@ fn l1_txns_priority_id() { gen_l1_tx(PriorityOpId(2)), gen_l1_tx(PriorityOpId(3)), ]; - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); assert!(mempool.next_transaction(&L2TxFilter::default()).is_none()); - mempool.insert(vec![gen_l1_tx(PriorityOpId(0))], HashMap::new()); + mempool.insert_without_constraints(vec![gen_l1_tx(PriorityOpId(0))], HashMap::new()); for idx in 0..4 { let data = mempool .next_transaction(&L2TxFilter::default()) .unwrap() + .0 .common_data; match data { ExecuteTransactionCommon::L1(data) => { @@ -153,7 +155,7 @@ fn rejected_tx() { gen_l2_tx(account, Nonce(3)), gen_l2_tx(account, Nonce(5)), ]; - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); assert_eq!( view(mempool.next_transaction(&L2TxFilter::default())), (account, 0) @@ -167,7 +169,7 @@ fn rejected_tx() { assert!(mempool.next_transaction(&L2TxFilter::default()).is_none()); // replace transaction and unblock account - mempool.insert(vec![gen_l2_tx(account, Nonce(1))], HashMap::new()); + mempool.insert_without_constraints(vec![gen_l2_tx(account, Nonce(1))], HashMap::new()); assert_eq!( view(mempool.next_transaction(&L2TxFilter::default())), (account, 1) @@ -186,9 +188,9 @@ fn rejected_tx() { fn replace_tx() { let mut mempool = MempoolStore::new(PriorityOpId(0), 100); let account = Address::random(); - mempool.insert(vec![gen_l2_tx(account, Nonce(0))], HashMap::new()); + mempool.insert_without_constraints(vec![gen_l2_tx(account, Nonce(0))], HashMap::new()); // replace it - mempool.insert( + mempool.insert_without_constraints( vec![gen_l2_tx_with_timestamp( account, Nonce(0), @@ -206,7 +208,7 @@ fn two_ready_txs() { let account0 = Address::random(); let account1 = Address::random(); let transactions = vec![gen_l2_tx(account0, Nonce(0)), gen_l2_tx(account1, Nonce(0))]; - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); assert_eq!( HashSet::<(_, _)>::from_iter(vec![ view(mempool.next_transaction(&L2TxFilter::default())), @@ -228,10 +230,10 @@ fn mempool_size() { gen_l2_tx(account0, Nonce(3)), gen_l2_tx(account1, Nonce(1)), ]; - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); assert_eq!(mempool.stats().l2_transaction_count, 5); // replacement - mempool.insert(vec![gen_l2_tx(account0, Nonce(2))], HashMap::new()); + mempool.insert_without_constraints(vec![gen_l2_tx(account0, Nonce(2))], HashMap::new()); assert_eq!(mempool.stats().l2_transaction_count, 5); // load next mempool.next_transaction(&L2TxFilter::default()); @@ -261,7 +263,7 @@ fn filtering() { // First account will have two transactions: one with too low pubdata price and one with the right value. // Second account will have just one transaction with the right value. - mempool.insert( + mempool.insert_without_constraints( gen_transactions_for_filtering(vec![ (account0, Nonce(0), unix_timestamp_ms(), 0), (account0, Nonce(1), unix_timestamp_ms(), 1), @@ -302,7 +304,7 @@ fn stashed_accounts() { let account0 = Address::random(); let account1 = Address::random(); - mempool.insert( + mempool.insert_without_constraints( gen_transactions_for_filtering(vec![ (account0, Nonce(0), unix_timestamp_ms(), 0), (account0, Nonce(1), unix_timestamp_ms(), 1), @@ -334,7 +336,7 @@ fn mempool_capacity() { gen_l2_tx_with_timestamp(account2, Nonce(0), unix_timestamp_ms() + 2), gen_l2_tx(account3, Nonce(1)), ]; - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); // Mempool is full. Accounts with non-sequential nonces and some accounts with lowest score should be purged. assert_eq!( HashSet::<_>::from_iter(mempool.get_mempool_info().purged_accounts), @@ -346,6 +348,7 @@ fn mempool_capacity() { mempool .next_transaction(&L2TxFilter::default()) .unwrap() + .0 .initiator_account(), account0 ); @@ -354,6 +357,7 @@ fn mempool_capacity() { mempool .next_transaction(&L2TxFilter::default()) .unwrap() + .0 .initiator_account(), account1 ); @@ -370,7 +374,7 @@ fn mempool_does_not_purge_all_accounts() { gen_l2_tx(account0, Nonce(1)), gen_l2_tx(account1, Nonce(1)), ]; - mempool.insert(transactions, HashMap::new()); + mempool.insert_without_constraints(transactions, HashMap::new()); // Mempool is full. Account 1 has tx with non-sequential nonce so it should be purged. // Txs from account 0 have sequential nonces but their number is greater than capacity; they should be kept. assert_eq!(mempool.get_mempool_info().purged_accounts, vec![account1]); @@ -380,6 +384,7 @@ fn mempool_does_not_purge_all_accounts() { mempool .next_transaction(&L2TxFilter::default()) .unwrap() + .0 .initiator_account(), account0 ); @@ -437,8 +442,8 @@ fn gen_l1_tx(priority_id: PriorityOpId) -> Transaction { } } -fn view(transaction: Option) -> (Address, u32) { - let tx = transaction.unwrap(); +fn view(transaction: Option<(Transaction, TransactionTimeRangeConstraint)>) -> (Address, u32) { + let tx = transaction.unwrap().0; (tx.initiator_account(), tx.nonce().unwrap().0) } diff --git a/core/lib/mempool/src/types.rs b/core/lib/mempool/src/types.rs index 99a63ffd08e..7c2694dff5e 100644 --- a/core/lib/mempool/src/types.rs +++ b/core/lib/mempool/src/types.rs @@ -1,14 +1,15 @@ use std::{cmp::Ordering, collections::HashMap}; use zksync_types::{ - fee::Fee, fee_model::BatchFeeInput, l2::L2Tx, Address, Nonce, Transaction, U256, + fee::Fee, fee_model::BatchFeeInput, l2::L2Tx, Address, Nonce, Transaction, + TransactionTimeRangeConstraint, U256, }; /// Pending mempool transactions of account #[derive(Debug)] pub(crate) struct AccountTransactions { /// transactions that belong to given account keyed by transaction nonce - transactions: HashMap, + transactions: HashMap, /// account nonce in mempool /// equals to committed nonce in db + number of transactions sent to state keeper nonce: Nonce, @@ -23,7 +24,11 @@ impl AccountTransactions { } /// Inserts new transaction for given account. Returns insertion metadata - pub fn insert(&mut self, transaction: L2Tx) -> InsertionMetadata { + pub fn insert( + &mut self, + transaction: L2Tx, + constraint: TransactionTimeRangeConstraint, + ) -> InsertionMetadata { let mut metadata = InsertionMetadata::default(); let nonce = transaction.common_data.nonce; // skip insertion if transaction is old @@ -33,8 +38,8 @@ impl AccountTransactions { let new_score = Self::score_for_transaction(&transaction); let previous_score = self .transactions - .insert(nonce, transaction) - .map(|tx| Self::score_for_transaction(&tx)); + .insert(nonce, (transaction, constraint)) + .map(|x| Self::score_for_transaction(&x.0)); metadata.is_new = previous_score.is_none(); if nonce == self.nonce { metadata.new_score = Some(new_score); @@ -43,9 +48,9 @@ impl AccountTransactions { metadata } - /// Returns next transaction to be included in block and optional score of its successor - /// Panics if no such transaction exists - pub fn next(&mut self) -> (L2Tx, Option) { + /// Returns next transaction to be included in block, its time range constraint and optional + /// score of its successor. Panics if no such transaction exists + pub fn next(&mut self) -> (L2Tx, TransactionTimeRangeConstraint, Option) { let transaction = self .transactions .remove(&self.nonce) @@ -54,12 +59,16 @@ impl AccountTransactions { let score = self .transactions .get(&self.nonce) - .map(Self::score_for_transaction); - (transaction, score) + .map(|(tx, _c)| Self::score_for_transaction(tx)); + (transaction.0, transaction.1, score) } - /// Handles transaction rejection. Returns optional score of its successor - pub fn reset(&mut self, transaction: &Transaction) -> Option { + /// Handles transaction rejection. Returns optional score of its successor and time range + /// constraint that the transaction has been added to the mempool with + pub fn reset( + &mut self, + transaction: &Transaction, + ) -> Option<(MempoolScore, TransactionTimeRangeConstraint)> { // current nonce for the group needs to be reset let tx_nonce = transaction .nonce() @@ -67,7 +76,7 @@ impl AccountTransactions { self.nonce = self.nonce.min(tx_nonce); self.transactions .get(&(tx_nonce + 1)) - .map(Self::score_for_transaction) + .map(|(tx, c)| (Self::score_for_transaction(tx), c.clone())) } pub fn len(&self) -> usize { diff --git a/core/lib/merkle_tree/src/domain.rs b/core/lib/merkle_tree/src/domain.rs index bb69bda209c..5064c791ed5 100644 --- a/core/lib/merkle_tree/src/domain.rs +++ b/core/lib/merkle_tree/src/domain.rs @@ -360,7 +360,10 @@ impl ZkSyncTree { pub fn roll_back_logs(&mut self, last_l1_batch_to_keep: L1BatchNumber) -> anyhow::Result<()> { self.tree.db.reset(); let retained_version_count = u64::from(last_l1_batch_to_keep.0 + 1); - self.tree.truncate_recent_versions(retained_version_count) + // Since `Patched<_>` doesn't implement `PruneDatabase`, we borrow the underlying DB, which is safe + // because the in-memory patch was reset above. + MerkleTree::new_unchecked(self.tree.db.inner_mut()) + .truncate_recent_versions(retained_version_count) } /// Saves the accumulated changes in the tree to RocksDB. diff --git a/core/lib/merkle_tree/src/lib.rs b/core/lib/merkle_tree/src/lib.rs index 824f23eaf52..5e97d6d77c6 100644 --- a/core/lib/merkle_tree/src/lib.rs +++ b/core/lib/merkle_tree/src/lib.rs @@ -200,24 +200,6 @@ impl MerkleTree { root.unwrap_or(Root::Empty) } - /// Removes the most recent versions from the database. - /// - /// The current implementation does not actually remove node data for the removed versions - /// since it's likely to be reused in the future (especially upper-level internal nodes). - /// - /// # Errors - /// - /// Proxies database I/O errors. - pub fn truncate_recent_versions(&mut self, retained_version_count: u64) -> anyhow::Result<()> { - let mut manifest = self.db.manifest().unwrap_or_default(); - if manifest.version_count > retained_version_count { - manifest.version_count = retained_version_count; - let patch = PatchSet::from_manifest(manifest); - self.db.apply_patch(patch)?; - } - Ok(()) - } - /// Extends this tree by creating its new version. /// /// # Return value @@ -259,6 +241,26 @@ impl MerkleTree { } impl MerkleTree { + /// Removes the most recent versions from the database. + /// + /// The current implementation does not actually remove node data for the removed versions + /// since it's likely to be reused in the future (especially upper-level internal nodes). + /// + /// # Errors + /// + /// Proxies database I/O errors. + pub fn truncate_recent_versions(&mut self, retained_version_count: u64) -> anyhow::Result<()> { + let mut manifest = self.db.manifest().unwrap_or_default(); + let current_version_count = manifest.version_count; + if current_version_count > retained_version_count { + // It is necessary to remove "future" stale keys since otherwise they may be used in future pruning and lead + // to non-obsolete tree nodes getting removed. + manifest.version_count = retained_version_count; + self.db.truncate(manifest, ..current_version_count)?; + } + Ok(()) + } + /// Returns the first retained version of the tree. pub fn first_retained_version(&self) -> Option { match self.db.min_stale_key_version() { diff --git a/core/lib/merkle_tree/src/pruning.rs b/core/lib/merkle_tree/src/pruning.rs index a74db40ef5e..2e328d0a2bb 100644 --- a/core/lib/merkle_tree/src/pruning.rs +++ b/core/lib/merkle_tree/src/pruning.rs @@ -250,7 +250,7 @@ mod tests { use super::*; use crate::{ types::{Node, NodeKey}, - Database, Key, MerkleTree, PatchSet, TreeEntry, ValueHash, + Database, Key, MerkleTree, PatchSet, RocksDBWrapper, TreeEntry, ValueHash, }; fn create_db() -> PatchSet { @@ -506,4 +506,69 @@ mod tests { println!("Keys are pruned after each update"); test_keys_are_removed_by_pruning_when_overwritten_in_multiple_batches(true); } + + fn test_pruning_with_truncation(db: impl PruneDatabase) { + let mut tree = MerkleTree::new(db).unwrap(); + let kvs: Vec<_> = (0_u64..100) + .map(|i| TreeEntry::new(Key::from(i), i + 1, ValueHash::zero())) + .collect(); + tree.extend(kvs).unwrap(); + + let overridden_kvs = vec![TreeEntry::new( + Key::from(0), + 1, + ValueHash::repeat_byte(0xaa), + )]; + tree.extend(overridden_kvs).unwrap(); + + let stale_keys = tree.db.stale_keys(1); + assert!( + stale_keys.iter().any(|key| !key.is_empty()), + "{stale_keys:?}" + ); + + // Revert `overridden_kvs`. + tree.truncate_recent_versions(1).unwrap(); + assert_eq!(tree.latest_version(), Some(0)); + let future_stale_keys = tree.db.stale_keys(1); + assert!(future_stale_keys.is_empty()); + + // Add a new version without the key. To make the matter more egregious, the inserted key + // differs from all existing keys, starting from the first nibble. + let new_key = Key::from_big_endian(&[0xaa; 32]); + let new_kvs = vec![TreeEntry::new(new_key, 101, ValueHash::repeat_byte(0xaa))]; + tree.extend(new_kvs).unwrap(); + assert_eq!(tree.latest_version(), Some(1)); + + let stale_keys = tree.db.stale_keys(1); + assert_eq!(stale_keys.len(), 1); + assert!( + stale_keys[0].is_empty() && stale_keys[0].version == 0, + "{stale_keys:?}" + ); + + let (mut pruner, _) = MerkleTreePruner::new(tree.db); + let prunable_version = pruner.last_prunable_version().unwrap(); + assert_eq!(prunable_version, 1); + let stats = pruner + .prune_up_to(prunable_version) + .unwrap() + .expect("tree was not pruned"); + assert_eq!(stats.target_retained_version, 1); + assert_eq!(stats.pruned_key_count, 1); // only the root node should have been pruned + + let tree = MerkleTree::new(pruner.db).unwrap(); + tree.verify_consistency(1, false).unwrap(); + } + + #[test] + fn pruning_with_truncation() { + test_pruning_with_truncation(PatchSet::default()); + } + + #[test] + fn pruning_with_truncation_on_rocksdb() { + let temp_dir = tempfile::TempDir::new().unwrap(); + test_pruning_with_truncation(RocksDBWrapper::new(temp_dir.path()).unwrap()); + } } diff --git a/core/lib/merkle_tree/src/storage/database.rs b/core/lib/merkle_tree/src/storage/database.rs index a6e8a36c708..a18deb643ca 100644 --- a/core/lib/merkle_tree/src/storage/database.rs +++ b/core/lib/merkle_tree/src/storage/database.rs @@ -400,6 +400,17 @@ pub trait PruneDatabase: Database { /// /// Propagates database I/O errors. fn prune(&mut self, patch: PrunePatchSet) -> anyhow::Result<()>; + + /// Atomically truncates the specified range of versions and stale keys. + /// + /// # Errors + /// + /// Propagates database I/O errors. + fn truncate( + &mut self, + manifest: Manifest, + truncated_versions: ops::RangeTo, + ) -> anyhow::Result<()>; } impl PruneDatabase for &mut T { @@ -414,6 +425,14 @@ impl PruneDatabase for &mut T { fn prune(&mut self, patch: PrunePatchSet) -> anyhow::Result<()> { (**self).prune(patch) } + + fn truncate( + &mut self, + manifest: Manifest, + truncated_versions: ops::RangeTo, + ) -> anyhow::Result<()> { + (**self).truncate(manifest, truncated_versions) + } } impl PruneDatabase for PatchSet { @@ -447,6 +466,17 @@ impl PruneDatabase for PatchSet { .retain(|version, _| !patch.deleted_stale_key_versions.contains(version)); Ok(()) } + + fn truncate( + &mut self, + manifest: Manifest, + truncated_versions: ops::RangeTo, + ) -> anyhow::Result<()> { + self.manifest = manifest; + self.stale_keys_by_version + .retain(|version, _| !truncated_versions.contains(version)); + Ok(()) + } } #[cfg(test)] diff --git a/core/lib/merkle_tree/src/storage/parallel.rs b/core/lib/merkle_tree/src/storage/parallel.rs index c5368c4561d..06b147efee8 100644 --- a/core/lib/merkle_tree/src/storage/parallel.rs +++ b/core/lib/merkle_tree/src/storage/parallel.rs @@ -4,7 +4,7 @@ use std::{ any::Any, collections::{HashMap, VecDeque}, error::Error as StdError, - mem, + mem, ops, sync::{mpsc, Arc}, thread, time::Duration, @@ -375,6 +375,17 @@ impl PruneDatabase for ParallelDatabase { .context("failed synchronizing database before pruning")?; self.inner.prune(patch) } + + fn truncate( + &mut self, + manifest: Manifest, + truncated_versions: ops::RangeTo, + ) -> anyhow::Result<()> { + // Require the underlying database to be fully synced. + self.wait_sync() + .context("failed synchronizing database before truncation")?; + self.inner.truncate(manifest, truncated_versions) + } } /// Database with either sequential or parallel persistence. @@ -479,6 +490,17 @@ impl PruneDatabase for MaybeParallel { Self::Parallel(db) => db.prune(patch), } } + + fn truncate( + &mut self, + manifest: Manifest, + truncated_versions: ops::RangeTo, + ) -> anyhow::Result<()> { + match self { + Self::Sequential(db) => db.truncate(manifest, truncated_versions), + Self::Parallel(db) => db.truncate(manifest, truncated_versions), + } + } } #[cfg(test)] diff --git a/core/lib/merkle_tree/src/storage/rocksdb.rs b/core/lib/merkle_tree/src/storage/rocksdb.rs index 22335c82940..6995bbfbfc7 100644 --- a/core/lib/merkle_tree/src/storage/rocksdb.rs +++ b/core/lib/merkle_tree/src/storage/rocksdb.rs @@ -1,6 +1,6 @@ //! RocksDB implementation of [`Database`]. -use std::{any::Any, cell::RefCell, path::Path, sync::Arc}; +use std::{any::Any, cell::RefCell, ops, path::Path, sync::Arc}; use anyhow::Context as _; use rayon::prelude::*; @@ -351,6 +351,32 @@ impl PruneDatabase for RocksDBWrapper { .write(write_batch) .context("Failed writing a batch to RocksDB") } + + fn truncate( + &mut self, + manifest: Manifest, + truncated_versions: ops::RangeTo, + ) -> anyhow::Result<()> { + anyhow::ensure!( + manifest.version_count <= truncated_versions.end, + "Invalid truncate call: manifest={manifest:?}, truncated_versions={truncated_versions:?}" + ); + let mut write_batch = self.db.new_write_batch(); + + let tree_cf = MerkleTreeColumnFamily::Tree; + let mut node_bytes = Vec::with_capacity(128); + manifest.serialize(&mut node_bytes); + write_batch.put_cf(tree_cf, Self::MANIFEST_KEY, &node_bytes); + + let stale_keys_cf = MerkleTreeColumnFamily::StaleKeys; + let first_version = &manifest.version_count.to_be_bytes() as &[_]; + let last_version = &truncated_versions.end.to_be_bytes(); + write_batch.delete_range_cf(stale_keys_cf, first_version..last_version); + + self.db + .write(write_batch) + .context("Failed writing a batch to RocksDB") + } } #[cfg(test)] diff --git a/core/lib/merkle_tree/tests/integration/merkle_tree.rs b/core/lib/merkle_tree/tests/integration/merkle_tree.rs index fc26cafe9ba..789872d1873 100644 --- a/core/lib/merkle_tree/tests/integration/merkle_tree.rs +++ b/core/lib/merkle_tree/tests/integration/merkle_tree.rs @@ -6,8 +6,8 @@ use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; use test_casing::test_casing; use zksync_crypto_primitives::hasher::blake2::Blake2Hasher; use zksync_merkle_tree::{ - Database, HashTree, MerkleTree, PatchSet, Patched, TreeEntry, TreeInstruction, TreeLogEntry, - TreeRangeDigest, + Database, HashTree, MerkleTree, PatchSet, Patched, PruneDatabase, TreeEntry, TreeInstruction, + TreeLogEntry, TreeRangeDigest, }; use zksync_types::{AccountTreeId, Address, StorageKey, H256, U256}; @@ -270,7 +270,7 @@ fn accumulating_commits(chunk_size: usize) { test_accumulated_commits(PatchSet::default(), chunk_size); } -fn test_root_hash_computing_with_reverts(db: &mut impl Database) { +fn test_root_hash_computing_with_reverts(db: &mut impl PruneDatabase) { let (kvs, expected_hash) = &*ENTRIES_AND_HASH; let (initial_update, final_update) = kvs.split_at(75); let key_updates: Vec<_> = kvs diff --git a/core/lib/multivm/src/tracers/mod.rs b/core/lib/multivm/src/tracers/mod.rs index 35224d993a1..b888c373011 100644 --- a/core/lib/multivm/src/tracers/mod.rs +++ b/core/lib/multivm/src/tracers/mod.rs @@ -1,6 +1,9 @@ pub use self::{ - call_tracer::CallTracer, multivm_dispatcher::TracerDispatcher, prestate_tracer::PrestateTracer, - storage_invocation::StorageInvocations, validator::ValidationTracer, + call_tracer::CallTracer, + multivm_dispatcher::TracerDispatcher, + prestate_tracer::PrestateTracer, + storage_invocation::StorageInvocations, + validator::{ValidationTracer, TIMESTAMP_ASSERTER_FUNCTION_SELECTOR}, }; mod call_tracer; diff --git a/core/lib/multivm/src/tracers/validator/mod.rs b/core/lib/multivm/src/tracers/validator/mod.rs index 057551a9efe..97446b33fe8 100644 --- a/core/lib/multivm/src/tracers/validator/mod.rs +++ b/core/lib/multivm/src/tracers/validator/mod.rs @@ -1,10 +1,11 @@ use std::{ collections::{BTreeSet, HashSet}, marker::PhantomData, - sync::Arc, + sync::{Arc, Mutex}, }; use once_cell::sync::OnceCell; +pub use vm_latest::TIMESTAMP_ASSERTER_FUNCTION_SELECTOR; use zksync_system_constants::{ ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, CONTRACT_DEPLOYER_ADDRESS, L2_BASE_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, SYSTEM_CONTEXT_ADDRESS, @@ -13,6 +14,7 @@ use zksync_types::{ vm::VmVersion, web3::keccak256, AccountTreeId, Address, StorageKey, H256, U256, }; use zksync_utils::{address_to_u256, be_bytes_to_safe_address, u256_to_h256}; +use zksync_vm_interface::tracer::{TimestampAsserterParams, ValidationTraces}; use self::types::{NewTrustedValidationItems, ValidationTracerMode}; use crate::{ @@ -47,8 +49,11 @@ pub struct ValidationTracer { trusted_address_slots: HashSet<(Address, U256)>, computational_gas_used: u32, computational_gas_limit: u32, + timestamp_asserter_params: Option, vm_version: VmVersion, + l1_batch_timestamp: u64, pub result: Arc>, + pub traces: Arc>, _marker: PhantomData H>, } @@ -57,30 +62,34 @@ type ValidationRoundResult = Result ValidationTracer { const MAX_ALLOWED_SLOT_OFFSET: u32 = 127; - pub fn new( - params: ValidationParams, - vm_version: VmVersion, - ) -> (Self, Arc>) { - let result = Arc::new(OnceCell::new()); - ( - Self { - validation_mode: ValidationTracerMode::NoValidation, - auxilary_allowed_slots: Default::default(), - - should_stop_execution: false, - user_address: params.user_address, - paymaster_address: params.paymaster_address, - trusted_slots: params.trusted_slots, - trusted_addresses: params.trusted_addresses, - trusted_address_slots: params.trusted_address_slots, - computational_gas_used: 0, - computational_gas_limit: params.computational_gas_limit, - vm_version, - result: result.clone(), - _marker: Default::default(), - }, - result, - ) + pub fn new(params: ValidationParams, vm_version: VmVersion, l1_batch_timestamp: u64) -> Self { + Self { + validation_mode: ValidationTracerMode::NoValidation, + auxilary_allowed_slots: Default::default(), + + should_stop_execution: false, + user_address: params.user_address, + paymaster_address: params.paymaster_address, + trusted_slots: params.trusted_slots, + trusted_addresses: params.trusted_addresses, + trusted_address_slots: params.trusted_address_slots, + computational_gas_used: 0, + computational_gas_limit: params.computational_gas_limit, + timestamp_asserter_params: params.timestamp_asserter_params.clone(), + vm_version, + result: Arc::new(OnceCell::new()), + traces: Arc::new(Mutex::new(ValidationTraces::default())), + _marker: Default::default(), + l1_batch_timestamp, + } + } + + pub fn get_result(&self) -> Arc> { + self.result.clone() + } + + pub fn get_traces(&self) -> Arc> { + self.traces.clone() } fn process_validation_round_result(&mut self, result: ValidationRoundResult) { @@ -154,6 +163,11 @@ impl ValidationTracer { return true; } + // Allow to read any storage slot from the timesttamp asserter contract + if self.timestamp_asserter_params.as_ref().map(|x| x.address) == Some(msg_sender) { + return true; + } + false } @@ -201,6 +215,7 @@ impl ValidationTracer { trusted_addresses: self.trusted_addresses.clone(), trusted_address_slots: self.trusted_address_slots.clone(), computational_gas_limit: self.computational_gas_limit, + timestamp_asserter_params: self.timestamp_asserter_params.clone(), } } } diff --git a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs index c206bd6fb2a..5503a720497 100644 --- a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs @@ -3,7 +3,7 @@ use zk_evm_1_5_0::{ zkevm_opcode_defs::{ContextOpcode, FarCallABI, LogOpcode, Opcode}, }; use zksync_system_constants::KECCAK256_PRECOMPILE_ADDRESS; -use zksync_types::{get_code_key, AccountTreeId, StorageKey, H256}; +use zksync_types::{get_code_key, AccountTreeId, StorageKey, H256, U256}; use zksync_utils::{h256_to_account_address, u256_to_account_address, u256_to_h256}; use crate::{ @@ -26,6 +26,8 @@ use crate::{ HistoryMode, }; +pub const TIMESTAMP_ASSERTER_FUNCTION_SELECTOR: [u8; 4] = [0x5b, 0x1a, 0x0c, 0x91]; + impl ValidationTracer { fn check_user_restrictions_vm_latest( &mut self, @@ -81,6 +83,51 @@ impl ValidationTracer { called_address, )); } + // If this is a call to the timestamp asserter, extract the function arguments and store them in ValidationTraces. + // These arguments are used by the mempool for transaction filtering. The call data length should be 68 bytes: + // a 4-byte function selector followed by two U256 values. + if let Some(params) = &self.timestamp_asserter_params { + if called_address == params.address + && far_call_abi.memory_quasi_fat_pointer.length == 68 + { + let calldata_page = get_calldata_page_via_abi( + &far_call_abi, + state.vm_local_state.callstack.current.base_memory_page, + ); + let calldata = memory.read_unaligned_bytes( + calldata_page as usize, + far_call_abi.memory_quasi_fat_pointer.start as usize, + 68, + ); + + if calldata[..4] == TIMESTAMP_ASSERTER_FUNCTION_SELECTOR { + // start and end need to be capped to u64::MAX to avoid overflow + let start = U256::from_big_endian( + &calldata[calldata.len() - 64..calldata.len() - 32], + ) + .try_into() + .unwrap_or(u64::MAX); + let end = U256::from_big_endian(&calldata[calldata.len() - 32..]) + .try_into() + .unwrap_or(u64::MAX); + + // using self.l1_batch_env.timestamp is ok here because the tracer is always + // used in a oneshot execution mode + if end + < self.l1_batch_timestamp + params.min_time_till_end.as_secs() + { + return Err( + ViolatedValidationRule::TimestampAssertionCloseToRangeEnd, + ); + } + + self.traces + .lock() + .unwrap() + .apply_timestamp_asserter_range(start..end); + } + } + } } } Opcode::Context(context) => { diff --git a/core/lib/multivm/src/versions/shadow/tests.rs b/core/lib/multivm/src/versions/shadow/tests.rs index 6a39a28f763..e6fb05e2406 100644 --- a/core/lib/multivm/src/versions/shadow/tests.rs +++ b/core/lib/multivm/src/versions/shadow/tests.rs @@ -197,6 +197,54 @@ mod default_aa { } } +mod evm_emulator { + use test_casing::{test_casing, Product}; + + use crate::versions::testonly::evm_emulator::*; + + #[test] + fn tracing_evm_contract_deployment() { + test_tracing_evm_contract_deployment::(); + } + + #[test] + fn mock_emulator_basics() { + test_mock_emulator_basics::(); + } + + #[test_casing(2, [false, true])] + #[test] + fn mock_emulator_with_payment(deploy_emulator: bool) { + test_mock_emulator_with_payment::(deploy_emulator); + } + + #[test_casing(4, Product(([false, true], [false, true])))] + #[test] + fn mock_emulator_with_recursion(deploy_emulator: bool, is_external: bool) { + test_mock_emulator_with_recursion::(deploy_emulator, is_external); + } + + #[test] + fn calling_to_mock_emulator_from_native_contract() { + test_calling_to_mock_emulator_from_native_contract::(); + } + + #[test] + fn mock_emulator_with_deployment() { + test_mock_emulator_with_deployment::(); + } + + #[test] + fn mock_emulator_with_delegate_call() { + test_mock_emulator_with_delegate_call::(); + } + + #[test] + fn mock_emulator_with_static_call() { + test_mock_emulator_with_static_call::(); + } +} + mod gas_limit { use crate::versions::testonly::gas_limit::*; diff --git a/core/lib/multivm/src/versions/testonly/account_validation_rules.rs b/core/lib/multivm/src/versions/testonly/account_validation_rules.rs index 5fe94a4ffba..57e54bac008 100644 --- a/core/lib/multivm/src/versions/testonly/account_validation_rules.rs +++ b/core/lib/multivm/src/versions/testonly/account_validation_rules.rs @@ -95,5 +95,5 @@ fn test_rule(rule: u32) -> Option, +} + +impl EvmTestBuilder { + fn new(deploy_emulator: bool, evm_contract_address: Address) -> Self { + Self { + deploy_emulator, + storage: InMemoryStorage::with_system_contracts(hash_bytecode), + evm_contract_addresses: vec![evm_contract_address], + } + } + + fn with_mock_deployer(mut self) -> Self { + override_system_contracts(&mut self.storage); + self + } + + fn with_evm_address(mut self, address: Address) -> Self { + self.evm_contract_addresses.push(address); + self + } + + fn build(self) -> VmTester { + let mock_emulator = read_bytecode(MOCK_EMULATOR_PATH); + let mut storage = self.storage; + let mut system_env = default_system_env(); + if self.deploy_emulator { + let evm_bytecode: Vec<_> = (0..32).collect(); + let evm_bytecode_hash = hash_evm_bytecode(&evm_bytecode); + storage.set_value( + get_known_code_key(&evm_bytecode_hash), + H256::from_low_u64_be(1), + ); + for evm_address in self.evm_contract_addresses { + storage.set_value(get_code_key(&evm_address), evm_bytecode_hash); + } + + system_env.base_system_smart_contracts.evm_emulator = Some(SystemContractCode { + hash: hash_bytecode(&mock_emulator), + code: bytes_to_be_words(mock_emulator), + }); + } else { + let emulator_hash = hash_bytecode(&mock_emulator); + storage.set_value(get_known_code_key(&emulator_hash), H256::from_low_u64_be(1)); + storage.store_factory_dep(emulator_hash, mock_emulator); + + for evm_address in self.evm_contract_addresses { + storage.set_value(get_code_key(&evm_address), emulator_hash); + // Set `isUserSpace` in the emulator storage to `true`, so that it skips emulator-specific checks + storage.set_value( + StorageKey::new(AccountTreeId::new(evm_address), H256::zero()), + H256::from_low_u64_be(1), + ); + } + } + + VmTesterBuilder::new() + .with_system_env(system_env) + .with_storage(storage) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_rich_accounts(1) + .build::() + } +} + +pub(crate) fn test_tracing_evm_contract_deployment() { + let mut storage = InMemoryStorage::with_system_contracts(hash_bytecode); + override_system_contracts(&mut storage); + + let mut system_env = default_system_env(); + // The EVM emulator will not be accessed, so we set it to a dummy value. + system_env.base_system_smart_contracts.evm_emulator = + Some(system_env.base_system_smart_contracts.default_aa.clone()); + let mut vm = VmTesterBuilder::new() + .with_system_env(system_env) + .with_storage(storage) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_rich_accounts(1) + .build::(); + let account = &mut vm.rich_accounts[0]; + + let args = [Token::Bytes((0..32).collect())]; + let evm_bytecode = ethabi::encode(&args); + let expected_bytecode_hash = hash_evm_bytecode(&evm_bytecode); + let execute = Execute::for_deploy(expected_bytecode_hash, vec![0; 32], &args); + let deploy_tx = account.get_l2_tx_for_execute(execute, None); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(deploy_tx, true); + assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); + + let new_known_factory_deps = vm_result.new_known_factory_deps.unwrap(); + assert_eq!(new_known_factory_deps.len(), 2); // the deployed EraVM contract + EVM contract + assert_eq!( + new_known_factory_deps[&expected_bytecode_hash], + evm_bytecode + ); +} + +pub(crate) fn test_mock_emulator_basics() { + let called_address = Address::repeat_byte(0x23); + let mut vm = EvmTestBuilder::new(true, called_address).build::(); + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(called_address), + calldata: vec![], + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(tx, true); + assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); +} + +const RECIPIENT_ADDRESS: Address = Address::repeat_byte(0x12); + +/// `deploy_emulator = false` here and below tests the mock emulator as an ordinary contract (i.e., sanity-checks its logic). +pub(crate) fn test_mock_emulator_with_payment(deploy_emulator: bool) { + let mock_emulator_abi = load_contract(MOCK_EMULATOR_PATH); + let mut vm = EvmTestBuilder::new(deploy_emulator, RECIPIENT_ADDRESS).build::(); + + let mut current_balance = U256::zero(); + for i in 1_u64..=5 { + let transferred_value = (1_000_000_000 * i).into(); + let vm_result = test_payment( + &mut vm, + &mock_emulator_abi, + &mut current_balance, + transferred_value, + ); + + let balance_storage_logs = vm_result.logs.storage_logs.iter().filter_map(|log| { + (*log.log.key.address() == L2_BASE_TOKEN_ADDRESS) + .then_some((*log.log.key.key(), h256_to_u256(log.log.value))) + }); + let balances: HashMap<_, _> = balance_storage_logs.collect(); + assert_eq!( + balances[&key_for_eth_balance(&RECIPIENT_ADDRESS)], + current_balance + ); + } +} + +fn test_payment( + vm: &mut VmTester, + mock_emulator_abi: ðabi::Contract, + balance: &mut U256, + transferred_value: U256, +) -> VmExecutionResultAndLogs { + *balance += transferred_value; + let test_payment_fn = mock_emulator_abi.function("testPayment").unwrap(); + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(RECIPIENT_ADDRESS), + calldata: test_payment_fn + .encode_input(&[Token::Uint(transferred_value), Token::Uint(*balance)]) + .unwrap(), + value: transferred_value, + factory_deps: vec![], + }, + None, + ); + + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(tx, true); + assert!(!vm_result.result.is_failed(), "{vm_result:?}"); + vm_result +} + +pub(crate) fn test_mock_emulator_with_recursion( + deploy_emulator: bool, + is_external: bool, +) { + let mock_emulator_abi = load_contract(MOCK_EMULATOR_PATH); + let recipient_address = Address::repeat_byte(0x12); + let mut vm = EvmTestBuilder::new(deploy_emulator, recipient_address).build::(); + let account = &mut vm.rich_accounts[0]; + + let test_recursion_fn = mock_emulator_abi + .function(if is_external { + "testExternalRecursion" + } else { + "testRecursion" + }) + .unwrap(); + let mut expected_value = U256::one(); + let depth = 50_u32; + for i in 2..=depth { + expected_value *= i; + } + + let factory_deps = if is_external { + vec![read_bytecode(RECURSIVE_CONTRACT_PATH)] + } else { + vec![] + }; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(recipient_address), + calldata: test_recursion_fn + .encode_input(&[Token::Uint(depth.into()), Token::Uint(expected_value)]) + .unwrap(), + value: 0.into(), + factory_deps, + }, + None, + ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(tx, true); + assert!(!vm_result.result.is_failed(), "{vm_result:?}"); +} + +pub(crate) fn test_calling_to_mock_emulator_from_native_contract() { + let recipient_address = Address::repeat_byte(0x12); + let mut vm = EvmTestBuilder::new(true, recipient_address).build::(); + let account = &mut vm.rich_accounts[0]; + + // Deploy a native contract. + let native_contract = read_bytecode(RECURSIVE_CONTRACT_PATH); + let native_contract_abi = load_contract(RECURSIVE_CONTRACT_PATH); + let deploy_tx = account.get_deploy_tx( + &native_contract, + Some(&[Token::Address(recipient_address)]), + TxType::L2, + ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(deploy_tx.tx, true); + assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); + + // Call from the native contract to the EVM emulator. + let test_fn = native_contract_abi.function("recurse").unwrap(); + let test_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(deploy_tx.address), + calldata: test_fn.encode_input(&[Token::Uint(50.into())]).unwrap(), + value: Default::default(), + factory_deps: vec![], + }, + None, + ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(test_tx, true); + assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); +} + +pub(crate) fn test_mock_emulator_with_deployment() { + let contract_address = Address::repeat_byte(0xaa); + let mut vm = EvmTestBuilder::new(true, contract_address) + .with_mock_deployer() + .build::(); + let account = &mut vm.rich_accounts[0]; + + let mock_emulator_abi = load_contract(MOCK_EMULATOR_PATH); + let new_evm_bytecode = vec![0xfe; 96]; + let new_evm_bytecode_hash = hash_evm_bytecode(&new_evm_bytecode); + + let test_fn = mock_emulator_abi.function("testDeploymentAndCall").unwrap(); + let test_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(contract_address), + calldata: test_fn + .encode_input(&[ + Token::FixedBytes(new_evm_bytecode_hash.0.into()), + Token::Bytes(new_evm_bytecode.clone()), + ]) + .unwrap(), + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(test_tx, true); + assert!(!vm_result.result.is_failed(), "{vm_result:?}"); + + let factory_deps = vm_result.new_known_factory_deps.unwrap(); + assert_eq!( + factory_deps, + HashMap::from([(new_evm_bytecode_hash, new_evm_bytecode)]) + ); +} + +pub(crate) fn test_mock_emulator_with_delegate_call() { + let evm_contract_address = Address::repeat_byte(0xaa); + let other_evm_contract_address = Address::repeat_byte(0xbb); + let mut builder = EvmTestBuilder::new(true, evm_contract_address); + builder.storage.set_value( + storage_key_for_eth_balance(&evm_contract_address), + H256::from_low_u64_be(1_000_000), + ); + builder.storage.set_value( + storage_key_for_eth_balance(&other_evm_contract_address), + H256::from_low_u64_be(2_000_000), + ); + let mut vm = builder + .with_evm_address(other_evm_contract_address) + .build::(); + let account = &mut vm.rich_accounts[0]; + + // Deploy a native contract. + let native_contract = read_bytecode(INCREMENTING_CONTRACT_PATH); + let native_contract_abi = load_contract(INCREMENTING_CONTRACT_PATH); + let deploy_tx = account.get_deploy_tx(&native_contract, None, TxType::L2); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(deploy_tx.tx, true); + assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); + + let test_fn = native_contract_abi.function("testDelegateCall").unwrap(); + // Delegate to the native contract from EVM. + test_delegate_call(&mut vm, test_fn, evm_contract_address, deploy_tx.address); + // Delegate to EVM from the native contract. + test_delegate_call(&mut vm, test_fn, deploy_tx.address, evm_contract_address); + // Delegate to EVM from EVM. + test_delegate_call( + &mut vm, + test_fn, + evm_contract_address, + other_evm_contract_address, + ); +} + +fn test_delegate_call( + vm: &mut VmTester, + test_fn: ðabi::Function, + from: Address, + to: Address, +) { + let account = &mut vm.rich_accounts[0]; + let test_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(from), + calldata: test_fn.encode_input(&[Token::Address(to)]).unwrap(), + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(test_tx, true); + assert!(!vm_result.result.is_failed(), "{vm_result:?}"); +} + +pub(crate) fn test_mock_emulator_with_static_call() { + let evm_contract_address = Address::repeat_byte(0xaa); + let other_evm_contract_address = Address::repeat_byte(0xbb); + let mut builder = EvmTestBuilder::new(true, evm_contract_address); + builder.storage.set_value( + storage_key_for_eth_balance(&evm_contract_address), + H256::from_low_u64_be(1_000_000), + ); + builder.storage.set_value( + storage_key_for_eth_balance(&other_evm_contract_address), + H256::from_low_u64_be(2_000_000), + ); + // Set differing read values for tested contracts. The slot index is defined in the contract. + let value_slot = H256::from_low_u64_be(0x123); + builder.storage.set_value( + StorageKey::new(AccountTreeId::new(evm_contract_address), value_slot), + H256::from_low_u64_be(100), + ); + builder.storage.set_value( + StorageKey::new(AccountTreeId::new(other_evm_contract_address), value_slot), + H256::from_low_u64_be(200), + ); + let mut vm = builder + .with_evm_address(other_evm_contract_address) + .build::(); + let account = &mut vm.rich_accounts[0]; + + // Deploy a native contract. + let native_contract = read_bytecode(INCREMENTING_CONTRACT_PATH); + let native_contract_abi = load_contract(INCREMENTING_CONTRACT_PATH); + let deploy_tx = account.get_deploy_tx(&native_contract, None, TxType::L2); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(deploy_tx.tx, true); + assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); + + let test_fn = native_contract_abi.function("testStaticCall").unwrap(); + // Call to the native contract from EVM. + test_static_call(&mut vm, test_fn, evm_contract_address, deploy_tx.address, 0); + // Call to EVM from the native contract. + test_static_call( + &mut vm, + test_fn, + deploy_tx.address, + evm_contract_address, + 100, + ); + // Call to EVM from EVM. + test_static_call( + &mut vm, + test_fn, + evm_contract_address, + other_evm_contract_address, + 200, + ); +} + +fn test_static_call( + vm: &mut VmTester, + test_fn: ðabi::Function, + from: Address, + to: Address, + expected_value: u64, +) { + let account = &mut vm.rich_accounts[0]; + let test_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(from), + calldata: test_fn + .encode_input(&[Token::Address(to), Token::Uint(expected_value.into())]) + .unwrap(), + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(test_tx, true); + assert!(!vm_result.result.is_failed(), "{vm_result:?}"); +} diff --git a/core/lib/multivm/src/versions/testonly/mod.rs b/core/lib/multivm/src/versions/testonly/mod.rs index df3e8701a4a..748f7e88170 100644 --- a/core/lib/multivm/src/versions/testonly/mod.rs +++ b/core/lib/multivm/src/versions/testonly/mod.rs @@ -42,6 +42,7 @@ pub(super) mod bytecode_publishing; pub(super) mod circuits; pub(super) mod code_oracle; pub(super) mod default_aa; +pub(super) mod evm_emulator; pub(super) mod gas_limit; pub(super) mod get_used_contracts; pub(super) mod is_write_initial; diff --git a/core/lib/multivm/src/versions/testonly/tester/mod.rs b/core/lib/multivm/src/versions/testonly/tester/mod.rs index 2901b6251b9..4603d66c023 100644 --- a/core/lib/multivm/src/versions/testonly/tester/mod.rs +++ b/core/lib/multivm/src/versions/testonly/tester/mod.rs @@ -234,7 +234,7 @@ pub(crate) trait TestedVm: } pub(crate) trait TestedVmForValidation { - fn run_validation(&mut self, tx: L2Tx) -> Option; + fn run_validation(&mut self, tx: L2Tx, timestamp: u64) -> Option; } pub(crate) fn validation_params(tx: &L2Tx, system: &SystemEnv) -> ValidationParams { @@ -247,5 +247,6 @@ pub(crate) fn validation_params(tx: &L2Tx, system: &SystemEnv) -> ValidationPara trusted_addresses: Default::default(), trusted_address_slots: Default::default(), computational_gas_limit: system.default_validation_computational_gas_limit, + timestamp_asserter_params: None, } } diff --git a/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs b/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs index 3585ea876da..9c1c0b7dfb7 100644 --- a/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs @@ -1,6 +1,6 @@ use circuit_sequencer_api_1_5_0::{geometry_config::get_geometry_config, toolset::GeometryConfig}; use zksync_vm2::interface::{ - CycleStats, ExecutionStatus, GlobalStateInterface, Opcode, OpcodeType, Tracer, + CycleStats, GlobalStateInterface, Opcode, OpcodeType, ShouldStop, Tracer, }; use zksync_vm_interface::CircuitStatistic; @@ -9,7 +9,7 @@ use crate::vm_latest::tracers::circuits_capacity::*; /// VM tracer tracking [`CircuitStatistic`]s. Statistics generally depend on the number of time some opcodes were invoked, /// and, for precompiles, invocation complexity (e.g., how many hashing cycles `keccak256` required). #[derive(Debug, Default, Clone, PartialEq)] -pub struct CircuitsTracer { +pub(super) struct CircuitsTracer { main_vm_cycles: u32, ram_permutation_cycles: u32, storage_application_cycles: u32, @@ -29,7 +29,7 @@ impl Tracer for CircuitsTracer { fn after_instruction( &mut self, _: &mut S, - ) -> ExecutionStatus { + ) -> ShouldStop { self.main_vm_cycles += 1; match OP::VALUE { @@ -116,7 +116,7 @@ impl Tracer for CircuitsTracer { } } - ExecutionStatus::Running + ShouldStop::Continue } fn on_extra_prover_cycles(&mut self, stats: CycleStats) { diff --git a/core/lib/multivm/src/versions/vm_fast/evm_deploy_tracer.rs b/core/lib/multivm/src/versions/vm_fast/evm_deploy_tracer.rs new file mode 100644 index 00000000000..0a0527dbaed --- /dev/null +++ b/core/lib/multivm/src/versions/vm_fast/evm_deploy_tracer.rs @@ -0,0 +1,98 @@ +//! Tracer tracking deployment of EVM bytecodes during VM execution. + +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +use zksync_system_constants::{CONTRACT_DEPLOYER_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS}; +use zksync_types::U256; +use zksync_utils::{bytecode::hash_evm_bytecode, h256_to_u256}; +use zksync_vm2::interface::{ + CallframeInterface, CallingMode, GlobalStateInterface, Opcode, OpcodeType, ShouldStop, Tracer, +}; + +use super::utils::read_fat_pointer; + +/// Container for dynamic bytecodes added by [`EvmDeployTracer`]. +#[derive(Debug, Clone, Default)] +pub(super) struct DynamicBytecodes(Rc>>>); + +impl DynamicBytecodes { + pub(super) fn take(&self, hash: U256) -> Option> { + self.0.borrow_mut().remove(&hash) + } + + fn insert(&self, hash: U256, bytecode: Vec) { + self.0.borrow_mut().insert(hash, bytecode); + } +} + +/// Tracer that tracks EVM bytecode deployments. +/// +/// Unlike EraVM bytecodes, EVM bytecodes are *dynamic*; they are not necessarily known before transaction execution. +/// (EraVM bytecodes must be present in the storage or be mentioned in the `factory_deps` field of a transaction.) +/// Hence, it's necessary to track which EVM bytecodes were deployed so that they are persisted after VM execution. +#[derive(Debug)] +pub(super) struct EvmDeployTracer { + tracked_signature: [u8; 4], + bytecodes: Option, +} + +impl Default for EvmDeployTracer { + fn default() -> Self { + Self { + tracked_signature: ethabi::short_signature( + "publishEVMBytecode", + &[ethabi::ParamType::Bytes], + ), + bytecodes: None, + } + } +} + +impl EvmDeployTracer { + fn handle_far_call(&self, state: &mut impl GlobalStateInterface) { + let from = state.current_frame().caller(); + let to = state.current_frame().code_address(); + if from != CONTRACT_DEPLOYER_ADDRESS || to != KNOWN_CODES_STORAGE_ADDRESS { + return; + } + + let data = read_fat_pointer(state, state.read_register(1).0); + if data.len() < 4 { + return; + } + let (signature, data) = data.split_at(4); + if signature != self.tracked_signature { + return; + } + + match ethabi::decode(&[ethabi::ParamType::Bytes], data) { + Ok(decoded) => { + // `unwrap`s should be safe since the function signature is checked above. + let published_bytecode = decoded.into_iter().next().unwrap().into_bytes().unwrap(); + let bytecode_hash = h256_to_u256(hash_evm_bytecode(&published_bytecode)); + self.bytecodes + .as_ref() + .expect("not initialized") + .insert(bytecode_hash, published_bytecode); + } + Err(err) => tracing::error!("Unable to decode `publishEVMBytecode` call: {err}"), + } + } + + pub(super) fn insert_dynamic_bytecodes_handle(&mut self, bytecodes: DynamicBytecodes) { + self.bytecodes = Some(bytecodes); + } +} + +impl Tracer for EvmDeployTracer { + #[inline(always)] + fn after_instruction( + &mut self, + state: &mut S, + ) -> ShouldStop { + if matches!(OP::VALUE, Opcode::FarCall(CallingMode::Normal)) { + self.handle_far_call(state); + } + ShouldStop::Continue + } +} diff --git a/core/lib/multivm/src/versions/vm_fast/mod.rs b/core/lib/multivm/src/versions/vm_fast/mod.rs index 0df64e1cdc6..88f8463eeec 100644 --- a/core/lib/multivm/src/versions/vm_fast/mod.rs +++ b/core/lib/multivm/src/versions/vm_fast/mod.rs @@ -12,6 +12,7 @@ mod bootloader_state; mod bytecode; mod circuits_tracer; mod events; +mod evm_deploy_tracer; mod glue; mod hook; mod initial_bootloader_memory; diff --git a/core/lib/multivm/src/versions/vm_fast/tests/evm_emulator.rs b/core/lib/multivm/src/versions/vm_fast/tests/evm_emulator.rs new file mode 100644 index 00000000000..cb7d54dba29 --- /dev/null +++ b/core/lib/multivm/src/versions/vm_fast/tests/evm_emulator.rs @@ -0,0 +1,53 @@ +use test_casing::{test_casing, Product}; + +use crate::{ + versions::testonly::evm_emulator::{ + test_calling_to_mock_emulator_from_native_contract, test_mock_emulator_basics, + test_mock_emulator_with_delegate_call, test_mock_emulator_with_deployment, + test_mock_emulator_with_payment, test_mock_emulator_with_recursion, + test_mock_emulator_with_static_call, test_tracing_evm_contract_deployment, + }, + vm_fast::Vm, +}; + +#[test] +fn tracing_evm_contract_deployment() { + test_tracing_evm_contract_deployment::>(); +} + +#[test] +fn mock_emulator_basics() { + test_mock_emulator_basics::>(); +} + +#[test_casing(2, [false, true])] +#[test] +fn mock_emulator_with_payment(deploy_emulator: bool) { + test_mock_emulator_with_payment::>(deploy_emulator); +} + +#[test_casing(4, Product(([false, true], [false, true])))] +#[test] +fn mock_emulator_with_recursion(deploy_emulator: bool, is_external: bool) { + test_mock_emulator_with_recursion::>(deploy_emulator, is_external); +} + +#[test] +fn calling_to_mock_emulator_from_native_contract() { + test_calling_to_mock_emulator_from_native_contract::>(); +} + +#[test] +fn mock_emulator_with_deployment() { + test_mock_emulator_with_deployment::>(); +} + +#[test] +fn mock_emulator_with_delegate_call() { + test_mock_emulator_with_delegate_call::>(); +} + +#[test] +fn mock_emulator_with_static_call() { + test_mock_emulator_with_static_call::>(); +} diff --git a/core/lib/multivm/src/versions/vm_fast/tests/mod.rs b/core/lib/multivm/src/versions/vm_fast/tests/mod.rs index e4b3d1d103e..7757d66ed78 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/mod.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/mod.rs @@ -24,6 +24,7 @@ mod bytecode_publishing; mod circuits; mod code_oracle; mod default_aa; +mod evm_emulator; mod gas_limit; mod get_used_contracts; mod is_write_initial; @@ -173,13 +174,13 @@ where impl TestedVmForValidation for Vm, WithBuiltinTracersForValidation<()>> { - fn run_validation(&mut self, tx: L2Tx) -> Option { + fn run_validation(&mut self, tx: L2Tx, timestamp: u64) -> Option { let validation_params = validation_params(&tx, &self.system_env); self.push_transaction(tx.into()); - let mut tracer = WithBuiltinTracers::for_validation((), validation_params); + let mut tracer = WithBuiltinTracers::for_validation((), validation_params, timestamp); self.inspect(&mut tracer, InspectExecutionMode::OneTx); - tracer.validation_error() + tracer.validation().validation_error() } } diff --git a/core/lib/multivm/src/versions/vm_fast/utils.rs b/core/lib/multivm/src/versions/vm_fast/utils.rs index f9e0d48a21a..20a6545d338 100644 --- a/core/lib/multivm/src/versions/vm_fast/utils.rs +++ b/core/lib/multivm/src/versions/vm_fast/utils.rs @@ -1,7 +1,7 @@ use zksync_types::U256; use zksync_vm2::{interface::StateInterface, FatPointer}; -pub(crate) fn read_fat_pointer(state: &S, raw: U256) -> Vec { +pub(super) fn read_fat_pointer(state: &S, raw: U256) -> Vec { let pointer = FatPointer::from(raw); let length = pointer.length - pointer.offset; let start = pointer.start + pointer.offset; diff --git a/core/lib/multivm/src/versions/vm_fast/validation_tracer.rs b/core/lib/multivm/src/versions/vm_fast/validation_tracer.rs index ef9038a4d5e..0a204dd3b8f 100644 --- a/core/lib/multivm/src/versions/vm_fast/validation_tracer.rs +++ b/core/lib/multivm/src/versions/vm_fast/validation_tracer.rs @@ -6,16 +6,16 @@ use zksync_types::{ KECCAK256_PRECOMPILE_ADDRESS, L2_BASE_TOKEN_ADDRESS, MSG_VALUE_SIMULATOR_ADDRESS, SYSTEM_CONTEXT_ADDRESS, U256, }; -use zksync_vm2::{ - interface::{ - CallframeInterface, ExecutionStatus, GlobalStateInterface, Opcode::*, OpcodeType, - ReturnType::*, Tracer, - }, - ExecutionEnd, +use zksync_vm2::interface::{ + CallframeInterface, GlobalStateInterface, Opcode::*, OpcodeType, ReturnType::*, ShouldStop, + Tracer, +}; +use zksync_vm_interface::tracer::{ + TimestampAsserterParams, ValidationParams, ValidationTraces, ViolatedValidationRule, }; -use zksync_vm_interface::tracer::{ValidationParams, ViolatedValidationRule}; use super::utils::read_fat_pointer; +use crate::tracers::TIMESTAMP_ASSERTER_FUNCTION_SELECTOR; pub trait ValidationMode: Tracer + Default { const STOP_AFTER_VALIDATION: bool; @@ -68,8 +68,11 @@ pub struct ValidationTracer { trusted_storage: HashSet<(Address, U256)>, /// These location's values are added to [Self::trusted_addresses] to support upgradeable proxies. storage_containing_trusted_addresses: HashSet<(Address, U256)>, + timestamp_asserter_params: Option, + l1_batch_timestamp: u64, validation_error: Option, + traces: ValidationTraces, } impl ValidationMode for ValidationTracer { @@ -134,13 +137,13 @@ impl Tracer for ValidationTracer { fn after_instruction( &mut self, state: &mut S, - ) -> ExecutionStatus { + ) -> ShouldStop { if !self.in_validation { - return ExecutionStatus::Running; + return ShouldStop::Continue; } if self.validation_error.is_some() { - return ExecutionStatus::Stopped(ExecutionEnd::Panicked); + return ShouldStop::Stop; } match OP::VALUE { @@ -150,7 +153,7 @@ impl Tracer for ValidationTracer { if code_address == KECCAK256_PRECOMPILE_ADDRESS { let calldata = read_fat_pointer(state, state.read_register(1).0); if calldata.len() != 64 { - return ExecutionStatus::Running; + return ShouldStop::Continue; } // Solidity mappings store values at the keccak256 hash of `key ++ slot_of_mapping` @@ -171,7 +174,37 @@ impl Tracer for ValidationTracer { self.set_error(ViolatedValidationRule::CalledContractWithNoCode( code_address, )); - return ExecutionStatus::Stopped(ExecutionEnd::Panicked); + return ShouldStop::Stop; + } + + if let Some(ref params) = self.timestamp_asserter_params { + if code_address == params.address { + let calldata = read_fat_pointer(state, state.read_register(1).0); + if calldata.len() == 68 + && calldata[..4] == TIMESTAMP_ASSERTER_FUNCTION_SELECTOR + { + // start and end need to be capped to u64::MAX to avoid overflow + let start = U256::from_big_endian( + &calldata[calldata.len() - 64..calldata.len() - 32], + ) + .try_into() + .unwrap_or(u64::MAX); + let end = U256::from_big_endian(&calldata[calldata.len() - 32..]) + .try_into() + .unwrap_or(u64::MAX); + + // using self.l1_batch_env.timestamp is ok here because the tracer is always + // used in a oneshot execution mode + if end < self.l1_batch_timestamp + params.min_time_till_end.as_secs() { + self.set_error( + ViolatedValidationRule::TimestampAssertionCloseToRangeEnd, + ); + return ShouldStop::Stop; + } + + self.traces.apply_timestamp_asserter_range(start..end); + } + } } } Ret(kind) => { @@ -185,17 +218,18 @@ impl Tracer for ValidationTracer { _ => {} } - ExecutionStatus::Running + ShouldStop::Continue } } impl ValidationTracer { - pub fn new(params: ValidationParams) -> Self { + pub fn new(params: ValidationParams, l1_batch_timestamp: u64) -> Self { let ValidationParams { user_address, trusted_slots, trusted_addresses, trusted_address_slots, + timestamp_asserter_params, .. } = params; Self { @@ -203,6 +237,8 @@ impl ValidationTracer { trusted_storage: trusted_slots, trusted_addresses, storage_containing_trusted_addresses: trusted_address_slots, + l1_batch_timestamp, + timestamp_asserter_params, ..Self::default() } @@ -243,4 +279,8 @@ impl ValidationTracer { pub fn validation_error(&self) -> Option { self.validation_error.clone() } + + pub fn traces(&self) -> ValidationTraces { + self.traces.clone() + } } diff --git a/core/lib/multivm/src/versions/vm_fast/vm.rs b/core/lib/multivm/src/versions/vm_fast/vm.rs index 45a1e831a66..56c476b1dd7 100644 --- a/core/lib/multivm/src/versions/vm_fast/vm.rs +++ b/core/lib/multivm/src/versions/vm_fast/vm.rs @@ -26,6 +26,7 @@ use zksync_vm_interface::{pubdata::PubdataBuilder, InspectExecutionMode}; use super::{ bootloader_state::{BootloaderState, BootloaderStateSnapshot}, bytecode::compress_bytecodes, + evm_deploy_tracer::DynamicBytecodes, hook::Hook, initial_bootloader_memory::bootloader_initial_memory, transaction_data::TransactionData, @@ -55,6 +56,7 @@ use crate::{ get_result_success_first_slot, get_vm_hook_params_start_position, get_vm_hook_position, OPERATOR_REFUNDS_OFFSET, TX_GAS_LIMIT_OFFSET, VM_HOOK_PARAMS_COUNT, }, + utils::extract_bytecodes_marked_as_known, MultiVMSubversion, }, }; @@ -110,16 +112,22 @@ impl Vm { system_env.version ); - let default_aa_code_hash = system_env + let default_aa_code_hash = system_env.base_system_smart_contracts.default_aa.hash; + let evm_emulator_hash = system_env .base_system_smart_contracts - .default_aa - .hash - .into(); + .evm_emulator + .as_ref() + .map(|evm| evm.hash) + .unwrap_or(system_env.base_system_smart_contracts.default_aa.hash); - let program_cache = HashMap::from([World::convert_system_contract_code( + let mut program_cache = HashMap::from([World::convert_system_contract_code( &system_env.base_system_smart_contracts.default_aa, false, )]); + if let Some(evm_emulator) = &system_env.base_system_smart_contracts.evm_emulator { + let (bytecode_hash, program) = World::convert_system_contract_code(evm_emulator, false); + program_cache.insert(bytecode_hash, program); + } let (_, bootloader) = World::convert_system_contract_code( &system_env.base_system_smart_contracts.bootloader, @@ -134,9 +142,8 @@ impl Vm { &[], system_env.bootloader_gas_limit, Settings { - default_aa_code_hash, - // this will change after 1.5 - evm_interpreter_code_hash: default_aa_code_hash, + default_aa_code_hash: default_aa_code_hash.into(), + evm_interpreter_code_hash: evm_emulator_hash.into(), hook_address: get_vm_hook_position(VM_VERSION) * 32, }, ); @@ -404,6 +411,16 @@ where }; break (ExecutionResult::Halt { reason }, true); } + ExecutionEnd::StoppedByTracer => { + break ( + ExecutionResult::Halt { + reason: Halt::TracerCustom( + "Unexpectedly stopped by tracer".to_string(), + ), + }, + false, + ); + } }; let hook = Hook::from_u32(hook); @@ -618,6 +635,8 @@ where let start = self.inner.world_diff().snapshot(); let gas_before = self.gas_remaining(); + tracer.insert_dynamic_bytecodes_handle(self.world.dynamic_bytecodes.clone()); + let result = self.run(execution_mode, tracer, track_refunds); let ignore_world_diff = @@ -675,6 +694,11 @@ where let gas_remaining = self.gas_remaining(); let gas_used = gas_before - gas_remaining; + // We need to filter out bytecodes the deployment of which may have been reverted; the tracer is not aware of reverts. + // To do this, we check bytecodes against deployer events. + let factory_deps_marked_as_known = extract_bytecodes_marked_as_known(&logs.events); + let new_known_factory_deps = self.world.decommit_bytecodes(&factory_deps_marked_as_known); + VmExecutionResultAndLogs { result: result.execution_result, logs, @@ -690,7 +714,7 @@ where total_log_queries: 0, }, refunds: result.refunds, - new_known_factory_deps: None, + new_known_factory_deps: Some(new_known_factory_deps), } } } @@ -841,6 +865,7 @@ impl fmt::Debug for Vm { #[derive(Debug)] pub(crate) struct World { pub(crate) storage: S, + dynamic_bytecodes: DynamicBytecodes, program_cache: HashMap>, pub(crate) bytecode_cache: HashMap>, } @@ -849,8 +874,9 @@ impl World { fn new(storage: S, program_cache: HashMap>) -> Self { Self { storage, + dynamic_bytecodes: DynamicBytecodes::default(), program_cache, - bytecode_cache: Default::default(), + bytecode_cache: HashMap::default(), } } @@ -863,6 +889,20 @@ impl World { Program::from_words(code.code.clone(), is_bootloader), ) } + + fn decommit_bytecodes(&self, hashes: &[H256]) -> HashMap> { + let bytecodes = hashes.iter().map(|&hash| { + let int_hash = h256_to_u256(hash); + let bytecode = self + .bytecode_cache + .get(&int_hash) + .cloned() + .or_else(|| self.dynamic_bytecodes.take(int_hash)) + .unwrap_or_else(|| panic!("Bytecode with hash {hash:?} not found")); + (hash, bytecode) + }); + bytecodes.collect() + } } impl zksync_vm2::StorageInterface for World { @@ -916,15 +956,34 @@ impl zksync_vm2::StorageInterface for World { } } +/// It may look like that an append-only cache for EVM bytecodes / `Program`s can lead to the following scenario: +/// +/// 1. A transaction deploys an EVM bytecode with hash `H`, then reverts. +/// 2. A following transaction in the same VM run queries a bytecode with hash `H` and gets it. +/// +/// This would be incorrect behavior because bytecode deployments must be reverted along with transactions. +/// +/// In reality, this cannot happen because both `decommit()` and `decommit_code()` calls perform storage-based checks +/// before a decommit: +/// +/// - `decommit_code()` is called from the `CodeOracle` system contract, which checks that the decommitted bytecode is known. +/// - `decommit()` is called during far calls, which obtains address -> bytecode hash mapping beforehand. +/// +/// Thus, if storage is reverted correctly, additional EVM bytecodes occupy the cache, but are unreachable. impl zksync_vm2::World for World { fn decommit(&mut self, hash: U256) -> Program { self.program_cache .entry(hash) .or_insert_with(|| { let bytecode = self.bytecode_cache.entry(hash).or_insert_with(|| { - self.storage - .load_factory_dep(u256_to_h256(hash)) - .expect("vm tried to decommit nonexistent bytecode") + // Since we put the bytecode in the cache anyway, it's safe to *take* it out from `dynamic_bytecodes` + // and put it in `bytecode_cache`. + self.dynamic_bytecodes + .take(hash) + .or_else(|| self.storage.load_factory_dep(u256_to_h256(hash))) + .unwrap_or_else(|| { + panic!("VM tried to decommit nonexistent bytecode: {hash:?}"); + }) }); Program::new(bytecode, false) }) diff --git a/core/lib/multivm/src/versions/vm_fast/with_builtin_tracers.rs b/core/lib/multivm/src/versions/vm_fast/with_builtin_tracers.rs index 5f55d478779..1d58b7ac16c 100644 --- a/core/lib/multivm/src/versions/vm_fast/with_builtin_tracers.rs +++ b/core/lib/multivm/src/versions/vm_fast/with_builtin_tracers.rs @@ -1,32 +1,35 @@ use zksync_vm2::interface::Tracer; -use zksync_vm_interface::tracer::{ValidationParams, ViolatedValidationRule}; +use zksync_vm_interface::tracer::ValidationParams; use super::{ circuits_tracer::CircuitsTracer, + evm_deploy_tracer::{DynamicBytecodes, EvmDeployTracer}, validation_tracer::{ValidationGasLimitOnly, ValidationMode, ValidationTracer}, }; -#[derive(Default, Debug)] -pub struct WithBuiltinTracers((External, (CircuitsTracer, Validation))); +#[derive(Debug, Default)] +pub struct WithBuiltinTracers( + (External, (Validation, (CircuitsTracer, EvmDeployTracer))), +); pub type DefaultTracers = WithBuiltinTracersForSequencer<()>; pub type WithBuiltinTracersForValidation = WithBuiltinTracers; impl WithBuiltinTracersForValidation { - pub fn for_validation(external: External, validation_params: ValidationParams) -> Self { + pub fn for_validation( + external: External, + validation_params: ValidationParams, + timestamp: u64, + ) -> Self { Self(( external, ( - CircuitsTracer::default(), - ValidationTracer::new(validation_params), + ValidationTracer::new(validation_params, timestamp), + Default::default(), ), )) } - - pub fn validation_error(&self) -> Option { - self.0 .1 .1.validation_error() - } } pub type WithBuiltinTracersForApi = WithBuiltinTracers; @@ -51,11 +54,19 @@ impl WithBuiltinTracers { } pub fn validation(&mut self) -> &mut Validation { - &mut self.0 .1 .1 + &mut self.0 .1 .0 } - pub fn circuit(&mut self) -> &mut CircuitsTracer { - &mut self.0 .1 .0 + pub(super) fn circuit(&mut self) -> &mut CircuitsTracer { + &mut self.0 .1 .1 .0 + } + + pub(super) fn insert_dynamic_bytecodes_handle(&mut self, dynamic_bytecodes: DynamicBytecodes) { + self.0 + .1 + .1 + .1 + .insert_dynamic_bytecodes_handle(dynamic_bytecodes) } } @@ -78,7 +89,7 @@ impl Tracer >( &mut self, state: &mut S, - ) -> zksync_vm2::interface::ExecutionStatus { + ) -> zksync_vm2::interface::ShouldStop { self.0.after_instruction::(state) } diff --git a/core/lib/multivm/src/versions/vm_latest/tests/evm_emulator.rs b/core/lib/multivm/src/versions/vm_latest/tests/evm_emulator.rs index 4d6e77aed51..b9b96c67098 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/evm_emulator.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/evm_emulator.rs @@ -1,507 +1,53 @@ -use std::collections::HashMap; - -use ethabi::Token; use test_casing::{test_casing, Product}; -use zksync_contracts::{load_contract, read_bytecode, SystemContractCode}; -use zksync_system_constants::{ - CONTRACT_DEPLOYER_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, L2_BASE_TOKEN_ADDRESS, -}; -use zksync_test_account::TxType; -use zksync_types::{ - get_code_key, get_known_code_key, - utils::{key_for_eth_balance, storage_key_for_eth_balance}, - AccountTreeId, Address, Execute, StorageKey, H256, U256, -}; -use zksync_utils::{ - be_words_to_bytes, - bytecode::{hash_bytecode, hash_evm_bytecode}, - bytes_to_be_words, h256_to_u256, -}; -use super::TestedLatestVm; use crate::{ - interface::{ - storage::InMemoryStorage, TxExecutionMode, VmExecutionResultAndLogs, VmInterfaceExt, + versions::testonly::evm_emulator::{ + test_calling_to_mock_emulator_from_native_contract, test_mock_emulator_basics, + test_mock_emulator_with_delegate_call, test_mock_emulator_with_deployment, + test_mock_emulator_with_payment, test_mock_emulator_with_recursion, + test_mock_emulator_with_static_call, test_tracing_evm_contract_deployment, }, - versions::testonly::{default_system_env, VmTester, VmTesterBuilder}, + vm_latest::{HistoryEnabled, Vm}, }; -const MOCK_DEPLOYER_PATH: &str = "etc/contracts-test-data/artifacts-zk/contracts/mock-evm/mock-evm.sol/MockContractDeployer.json"; -const MOCK_KNOWN_CODE_STORAGE_PATH: &str = "etc/contracts-test-data/artifacts-zk/contracts/mock-evm/mock-evm.sol/MockKnownCodeStorage.json"; -const MOCK_EMULATOR_PATH: &str = - "etc/contracts-test-data/artifacts-zk/contracts/mock-evm/mock-evm.sol/MockEvmEmulator.json"; -const RECURSIVE_CONTRACT_PATH: &str = "etc/contracts-test-data/artifacts-zk/contracts/mock-evm/mock-evm.sol/NativeRecursiveContract.json"; -const INCREMENTING_CONTRACT_PATH: &str = "etc/contracts-test-data/artifacts-zk/contracts/mock-evm/mock-evm.sol/IncrementingContract.json"; - -fn override_system_contracts(storage: &mut InMemoryStorage) { - let mock_deployer = read_bytecode(MOCK_DEPLOYER_PATH); - let mock_deployer_hash = hash_bytecode(&mock_deployer); - let mock_known_code_storage = read_bytecode(MOCK_KNOWN_CODE_STORAGE_PATH); - let mock_known_code_storage_hash = hash_bytecode(&mock_known_code_storage); - - storage.set_value(get_code_key(&CONTRACT_DEPLOYER_ADDRESS), mock_deployer_hash); - storage.set_value( - get_known_code_key(&mock_deployer_hash), - H256::from_low_u64_be(1), - ); - storage.set_value( - get_code_key(&KNOWN_CODES_STORAGE_ADDRESS), - mock_known_code_storage_hash, - ); - storage.set_value( - get_known_code_key(&mock_known_code_storage_hash), - H256::from_low_u64_be(1), - ); - storage.store_factory_dep(mock_deployer_hash, mock_deployer); - storage.store_factory_dep(mock_known_code_storage_hash, mock_known_code_storage); -} - -#[derive(Debug)] -struct EvmTestBuilder { - deploy_emulator: bool, - storage: InMemoryStorage, - evm_contract_addresses: Vec
, -} - -impl EvmTestBuilder { - fn new(deploy_emulator: bool, evm_contract_address: Address) -> Self { - Self { - deploy_emulator, - storage: InMemoryStorage::with_system_contracts(hash_bytecode), - evm_contract_addresses: vec![evm_contract_address], - } - } - - fn with_mock_deployer(mut self) -> Self { - override_system_contracts(&mut self.storage); - self - } - - fn with_evm_address(mut self, address: Address) -> Self { - self.evm_contract_addresses.push(address); - self - } - - fn build(self) -> VmTester { - let mock_emulator = read_bytecode(MOCK_EMULATOR_PATH); - let mut storage = self.storage; - let mut system_env = default_system_env(); - if self.deploy_emulator { - let evm_bytecode: Vec<_> = (0..32).collect(); - let evm_bytecode_hash = hash_evm_bytecode(&evm_bytecode); - storage.set_value( - get_known_code_key(&evm_bytecode_hash), - H256::from_low_u64_be(1), - ); - for evm_address in self.evm_contract_addresses { - storage.set_value(get_code_key(&evm_address), evm_bytecode_hash); - } - - system_env.base_system_smart_contracts.evm_emulator = Some(SystemContractCode { - hash: hash_bytecode(&mock_emulator), - code: bytes_to_be_words(mock_emulator), - }); - } else { - let emulator_hash = hash_bytecode(&mock_emulator); - storage.set_value(get_known_code_key(&emulator_hash), H256::from_low_u64_be(1)); - storage.store_factory_dep(emulator_hash, mock_emulator); - - for evm_address in self.evm_contract_addresses { - storage.set_value(get_code_key(&evm_address), emulator_hash); - // Set `isUserSpace` in the emulator storage to `true`, so that it skips emulator-specific checks - storage.set_value( - StorageKey::new(AccountTreeId::new(evm_address), H256::zero()), - H256::from_low_u64_be(1), - ); - } - } - - VmTesterBuilder::new() - .with_system_env(system_env) - .with_storage(storage) - .with_execution_mode(TxExecutionMode::VerifyExecute) - .with_rich_accounts(1) - .build() - } -} - #[test] fn tracing_evm_contract_deployment() { - let mut storage = InMemoryStorage::with_system_contracts(hash_bytecode); - override_system_contracts(&mut storage); - - let mut system_env = default_system_env(); - // The EVM emulator will not be accessed, so we set it to a dummy value. - system_env.base_system_smart_contracts.evm_emulator = - Some(system_env.base_system_smart_contracts.default_aa.clone()); - let mut vm = VmTesterBuilder::new() - .with_system_env(system_env) - .with_storage(storage) - .with_execution_mode(TxExecutionMode::VerifyExecute) - .with_rich_accounts(1) - .build::(); - let account = &mut vm.rich_accounts[0]; - - let args = [Token::Bytes((0..32).collect())]; - let evm_bytecode = ethabi::encode(&args); - let expected_bytecode_hash = hash_evm_bytecode(&evm_bytecode); - let execute = Execute::for_deploy(expected_bytecode_hash, vec![0; 32], &args); - let deploy_tx = account.get_l2_tx_for_execute(execute, None); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(deploy_tx, true); - assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); - - // Check that the surrogate EVM bytecode was added to the decommitter. - let known_bytecodes = vm.vm.state.decommittment_processor.known_bytecodes.inner(); - let known_evm_bytecode = - be_words_to_bytes(&known_bytecodes[&h256_to_u256(expected_bytecode_hash)]); - assert_eq!(known_evm_bytecode, evm_bytecode); - - let new_known_factory_deps = vm_result.new_known_factory_deps.unwrap(); - assert_eq!(new_known_factory_deps.len(), 2); // the deployed EraVM contract + EVM contract - assert_eq!( - new_known_factory_deps[&expected_bytecode_hash], - evm_bytecode - ); + test_tracing_evm_contract_deployment::>(); } #[test] fn mock_emulator_basics() { - let called_address = Address::repeat_byte(0x23); - let mut vm = EvmTestBuilder::new(true, called_address).build(); - let account = &mut vm.rich_accounts[0]; - let tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(called_address), - calldata: vec![], - value: 0.into(), - factory_deps: vec![], - }, - None, - ); - - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(tx, true); - assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); + test_mock_emulator_basics::>(); } -const RECIPIENT_ADDRESS: Address = Address::repeat_byte(0x12); - -/// `deploy_emulator = false` here and below tests the mock emulator as an ordinary contract (i.e., sanity-checks its logic). #[test_casing(2, [false, true])] #[test] fn mock_emulator_with_payment(deploy_emulator: bool) { - let mock_emulator_abi = load_contract(MOCK_EMULATOR_PATH); - let mut vm = EvmTestBuilder::new(deploy_emulator, RECIPIENT_ADDRESS).build(); - - let mut current_balance = U256::zero(); - for i in 1_u64..=5 { - let transferred_value = (1_000_000_000 * i).into(); - let vm_result = test_payment( - &mut vm, - &mock_emulator_abi, - &mut current_balance, - transferred_value, - ); - - let balance_storage_logs = vm_result.logs.storage_logs.iter().filter_map(|log| { - (*log.log.key.address() == L2_BASE_TOKEN_ADDRESS) - .then_some((*log.log.key.key(), h256_to_u256(log.log.value))) - }); - let balances: HashMap<_, _> = balance_storage_logs.collect(); - assert_eq!( - balances[&key_for_eth_balance(&RECIPIENT_ADDRESS)], - current_balance - ); - } -} - -fn test_payment( - vm: &mut VmTester, - mock_emulator_abi: ðabi::Contract, - balance: &mut U256, - transferred_value: U256, -) -> VmExecutionResultAndLogs { - *balance += transferred_value; - let test_payment_fn = mock_emulator_abi.function("testPayment").unwrap(); - let account = &mut vm.rich_accounts[0]; - let tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(RECIPIENT_ADDRESS), - calldata: test_payment_fn - .encode_input(&[Token::Uint(transferred_value), Token::Uint(*balance)]) - .unwrap(), - value: transferred_value, - factory_deps: vec![], - }, - None, - ); - - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(tx, true); - assert!(!vm_result.result.is_failed(), "{vm_result:?}"); - vm_result + test_mock_emulator_with_payment::>(deploy_emulator); } #[test_casing(4, Product(([false, true], [false, true])))] #[test] fn mock_emulator_with_recursion(deploy_emulator: bool, is_external: bool) { - let mock_emulator_abi = load_contract(MOCK_EMULATOR_PATH); - let recipient_address = Address::repeat_byte(0x12); - let mut vm = EvmTestBuilder::new(deploy_emulator, recipient_address).build(); - let account = &mut vm.rich_accounts[0]; - - let test_recursion_fn = mock_emulator_abi - .function(if is_external { - "testExternalRecursion" - } else { - "testRecursion" - }) - .unwrap(); - let mut expected_value = U256::one(); - let depth = 50_u32; - for i in 2..=depth { - expected_value *= i; - } - - let factory_deps = if is_external { - vec![read_bytecode(RECURSIVE_CONTRACT_PATH)] - } else { - vec![] - }; - let tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(recipient_address), - calldata: test_recursion_fn - .encode_input(&[Token::Uint(depth.into()), Token::Uint(expected_value)]) - .unwrap(), - value: 0.into(), - factory_deps, - }, - None, - ); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(tx, true); - assert!(!vm_result.result.is_failed(), "{vm_result:?}"); + test_mock_emulator_with_recursion::>(deploy_emulator, is_external); } #[test] fn calling_to_mock_emulator_from_native_contract() { - let recipient_address = Address::repeat_byte(0x12); - let mut vm = EvmTestBuilder::new(true, recipient_address).build(); - let account = &mut vm.rich_accounts[0]; - - // Deploy a native contract. - let native_contract = read_bytecode(RECURSIVE_CONTRACT_PATH); - let native_contract_abi = load_contract(RECURSIVE_CONTRACT_PATH); - let deploy_tx = account.get_deploy_tx( - &native_contract, - Some(&[Token::Address(recipient_address)]), - TxType::L2, - ); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(deploy_tx.tx, true); - assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); - - // Call from the native contract to the EVM emulator. - let test_fn = native_contract_abi.function("recurse").unwrap(); - let test_tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(deploy_tx.address), - calldata: test_fn.encode_input(&[Token::Uint(50.into())]).unwrap(), - value: Default::default(), - factory_deps: vec![], - }, - None, - ); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(test_tx, true); - assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); + test_calling_to_mock_emulator_from_native_contract::>(); } #[test] fn mock_emulator_with_deployment() { - let contract_address = Address::repeat_byte(0xaa); - let mut vm = EvmTestBuilder::new(true, contract_address) - .with_mock_deployer() - .build(); - let account = &mut vm.rich_accounts[0]; - - let mock_emulator_abi = load_contract(MOCK_EMULATOR_PATH); - let new_evm_bytecode = vec![0xfe; 96]; - let new_evm_bytecode_hash = hash_evm_bytecode(&new_evm_bytecode); - - let test_fn = mock_emulator_abi.function("testDeploymentAndCall").unwrap(); - let test_tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(contract_address), - calldata: test_fn - .encode_input(&[ - Token::FixedBytes(new_evm_bytecode_hash.0.into()), - Token::Bytes(new_evm_bytecode.clone()), - ]) - .unwrap(), - value: 0.into(), - factory_deps: vec![], - }, - None, - ); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(test_tx, true); - assert!(!vm_result.result.is_failed(), "{vm_result:?}"); - - let factory_deps = vm_result.new_known_factory_deps.unwrap(); - assert_eq!( - factory_deps, - HashMap::from([(new_evm_bytecode_hash, new_evm_bytecode)]) - ); + test_mock_emulator_with_deployment::>(); } #[test] fn mock_emulator_with_delegate_call() { - let evm_contract_address = Address::repeat_byte(0xaa); - let other_evm_contract_address = Address::repeat_byte(0xbb); - let mut builder = EvmTestBuilder::new(true, evm_contract_address); - builder.storage.set_value( - storage_key_for_eth_balance(&evm_contract_address), - H256::from_low_u64_be(1_000_000), - ); - builder.storage.set_value( - storage_key_for_eth_balance(&other_evm_contract_address), - H256::from_low_u64_be(2_000_000), - ); - let mut vm = builder.with_evm_address(other_evm_contract_address).build(); - let account = &mut vm.rich_accounts[0]; - - // Deploy a native contract. - let native_contract = read_bytecode(INCREMENTING_CONTRACT_PATH); - let native_contract_abi = load_contract(INCREMENTING_CONTRACT_PATH); - let deploy_tx = account.get_deploy_tx(&native_contract, None, TxType::L2); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(deploy_tx.tx, true); - assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); - - let test_fn = native_contract_abi.function("testDelegateCall").unwrap(); - // Delegate to the native contract from EVM. - test_delegate_call(&mut vm, test_fn, evm_contract_address, deploy_tx.address); - // Delegate to EVM from the native contract. - test_delegate_call(&mut vm, test_fn, deploy_tx.address, evm_contract_address); - // Delegate to EVM from EVM. - test_delegate_call( - &mut vm, - test_fn, - evm_contract_address, - other_evm_contract_address, - ); -} - -fn test_delegate_call( - vm: &mut VmTester, - test_fn: ðabi::Function, - from: Address, - to: Address, -) { - let account = &mut vm.rich_accounts[0]; - let test_tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(from), - calldata: test_fn.encode_input(&[Token::Address(to)]).unwrap(), - value: 0.into(), - factory_deps: vec![], - }, - None, - ); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(test_tx, true); - assert!(!vm_result.result.is_failed(), "{vm_result:?}"); + test_mock_emulator_with_delegate_call::>(); } #[test] fn mock_emulator_with_static_call() { - let evm_contract_address = Address::repeat_byte(0xaa); - let other_evm_contract_address = Address::repeat_byte(0xbb); - let mut builder = EvmTestBuilder::new(true, evm_contract_address); - builder.storage.set_value( - storage_key_for_eth_balance(&evm_contract_address), - H256::from_low_u64_be(1_000_000), - ); - builder.storage.set_value( - storage_key_for_eth_balance(&other_evm_contract_address), - H256::from_low_u64_be(2_000_000), - ); - // Set differing read values for tested contracts. The slot index is defined in the contract. - let value_slot = H256::from_low_u64_be(0x123); - builder.storage.set_value( - StorageKey::new(AccountTreeId::new(evm_contract_address), value_slot), - H256::from_low_u64_be(100), - ); - builder.storage.set_value( - StorageKey::new(AccountTreeId::new(other_evm_contract_address), value_slot), - H256::from_low_u64_be(200), - ); - let mut vm = builder.with_evm_address(other_evm_contract_address).build(); - let account = &mut vm.rich_accounts[0]; - - // Deploy a native contract. - let native_contract = read_bytecode(INCREMENTING_CONTRACT_PATH); - let native_contract_abi = load_contract(INCREMENTING_CONTRACT_PATH); - let deploy_tx = account.get_deploy_tx(&native_contract, None, TxType::L2); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(deploy_tx.tx, true); - assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); - - let test_fn = native_contract_abi.function("testStaticCall").unwrap(); - // Call to the native contract from EVM. - test_static_call(&mut vm, test_fn, evm_contract_address, deploy_tx.address, 0); - // Call to EVM from the native contract. - test_static_call( - &mut vm, - test_fn, - deploy_tx.address, - evm_contract_address, - 100, - ); - // Call to EVM from EVM. - test_static_call( - &mut vm, - test_fn, - evm_contract_address, - other_evm_contract_address, - 200, - ); -} - -fn test_static_call( - vm: &mut VmTester, - test_fn: ðabi::Function, - from: Address, - to: Address, - expected_value: u64, -) { - let account = &mut vm.rich_accounts[0]; - let test_tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(from), - calldata: test_fn - .encode_input(&[Token::Address(to), Token::Uint(expected_value.into())]) - .unwrap(), - value: 0.into(), - factory_deps: vec![], - }, - None, - ); - let (_, vm_result) = vm - .vm - .execute_transaction_with_bytecode_compression(test_tx, true); - assert!(!vm_result.result.is_failed(), "{vm_result:?}"); + test_mock_emulator_with_static_call::>(); } diff --git a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs index 5f42fcbe70b..9478bc1f85b 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs @@ -196,14 +196,17 @@ impl TestedVm for TestedLatestVm { } impl TestedVmForValidation for TestedLatestVm { - fn run_validation(&mut self, tx: L2Tx) -> Option { + fn run_validation(&mut self, tx: L2Tx, timestamp: u64) -> Option { let validation_params = validation_params(&tx, &self.system_env); self.push_transaction(tx.into()); - let (tracer, mut failures) = ValidationTracer::::new( + let tracer = ValidationTracer::::new( validation_params, VmVersion::Vm1_5_0IncreasedBootloaderMemory, + timestamp, ); + let mut failures = tracer.get_result(); + self.inspect_inner( &mut tracer.into_tracer_pointer().into(), VmExecutionMode::OneTx, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs index 2ae5e81a328..7156acce152 100755 --- a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs @@ -228,7 +228,10 @@ impl Tracer for DefaultExecutionTracer { ); match hook { - VmHook::TxHasEnded => self.tx_has_been_processed = true, + VmHook::TxHasEnded if matches!(self.execution_mode, VmExecutionMode::OneTx) => { + self.result_tracer.tx_finished_in_one_tx_mode = true; + self.tx_has_been_processed = true; + } VmHook::NoValidationEntered => self.in_account_validation = false, VmHook::AccountValidationEntered => self.in_account_validation = true, VmHook::FinalBatchInfo => self.final_batch_info_requested = true, diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs index 6ba00f4a099..0687c8393c6 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs @@ -104,6 +104,8 @@ pub(crate) struct ResultTracer { far_call_tracker: FarCallTracker, subversion: MultiVMSubversion, + pub(crate) tx_finished_in_one_tx_mode: bool, + _phantom: PhantomData, } @@ -115,6 +117,7 @@ impl ResultTracer { execution_mode, far_call_tracker: Default::default(), subversion, + tx_finished_in_one_tx_mode: false, _phantom: PhantomData, } } @@ -297,7 +300,7 @@ impl ResultTracer { let has_failed = tx_has_failed(state, bootloader_state.current_tx() as u32, self.subversion); - if has_failed { + if self.tx_finished_in_one_tx_mode && has_failed { self.result = Some(Result::Error { error_reason: VmRevertReason::General { msg: "Transaction reverted with empty reason. Possibly out of gas" @@ -306,9 +309,9 @@ impl ResultTracer { }, }); } else { - self.result = Some(self.result.clone().unwrap_or(Result::Success { + self.result = Some(Result::Success { return_data: vec![], - })); + }); } } } diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index 84f03c5afe3..660246928ed 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -98,6 +98,12 @@ impl ProtoRepr for proto::Contracts { .map(|x| parse_h160(x)) .transpose() .context("l2_testnet_paymaster_addr")?, + l2_timestamp_asserter_addr: l2 + .timestamp_asserter_addr + .as_ref() + .map(|x| parse_h160(x)) + .transpose() + .context("l2_timestamp_asserter_addr")?, l1_multicall3_addr: required(&l1.multicall3_addr) .and_then(|x| parse_h160(x)) .context("l1_multicall3_addr")?, @@ -158,6 +164,9 @@ impl ProtoRepr for proto::Contracts { legacy_shared_bridge_addr: this .l2_legacy_shared_bridge_addr .map(|a| format!("{:?}", a)), + timestamp_asserter_addr: this + .l2_timestamp_asserter_addr + .map(|a| format!("{:?}", a)), }), bridges: Some(proto::Bridges { shared: Some(proto::Bridge { diff --git a/core/lib/protobuf_config/src/da_client.rs b/core/lib/protobuf_config/src/da_client.rs index a17a8711a27..341a6a9e4f4 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -3,7 +3,9 @@ use zksync_config::configs::{ self, da_client::{ avail::{AvailClientConfig, AvailConfig, AvailDefaultConfig, AvailGasRelayConfig}, - DAClientConfig::{Avail, ObjectStore}, + celestia::CelestiaConfig, + eigen::EigenConfig, + DAClientConfig::{Avail, Celestia, Eigen, ObjectStore}, }, }; use zksync_protobuf::{required, ProtoRepr}; @@ -21,7 +23,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { bridge_api_url: required(&conf.bridge_api_url) .context("bridge_api_url")? .clone(), - timeout: *required(&conf.timeout).context("timeout")? as usize, + timeout_ms: *required(&conf.timeout_ms).context("timeout_ms")? as usize, config: match conf.config.as_ref() { Some(proto::avail_config::Config::FullClient(full_client_conf)) => { AvailClientConfig::FullClient(AvailDefaultConfig { @@ -44,6 +46,19 @@ impl ProtoRepr for proto::DataAvailabilityClient { None => return Err(anyhow::anyhow!("Invalid Avail DA configuration")), }, }), + proto::data_availability_client::Config::Celestia(conf) => Celestia(CelestiaConfig { + api_node_url: required(&conf.api_node_url).context("namespace")?.clone(), + namespace: required(&conf.namespace).context("namespace")?.clone(), + chain_id: required(&conf.chain_id).context("chain_id")?.clone(), + timeout_ms: *required(&conf.timeout_ms).context("timeout_ms")?, + }), + proto::data_availability_client::Config::Eigen(conf) => Eigen(EigenConfig { + rpc_node_url: required(&conf.rpc_node_url) + .context("rpc_node_url")? + .clone(), + inclusion_polling_interval_ms: *required(&conf.inclusion_polling_interval_ms) + .context("inclusion_polling_interval_ms")?, + }), proto::data_availability_client::Config::ObjectStore(conf) => { ObjectStore(object_store_proto::ObjectStore::read(conf)?) } @@ -53,34 +68,44 @@ impl ProtoRepr for proto::DataAvailabilityClient { } fn build(this: &Self::Type) -> Self { - match &this { - Avail(config) => Self { - config: Some(proto::data_availability_client::Config::Avail( - proto::AvailConfig { - bridge_api_url: Some(config.bridge_api_url.clone()), - timeout: Some(config.timeout as u64), - config: match &config.config { - AvailClientConfig::FullClient(conf) => Some( - proto::avail_config::Config::FullClient(proto::AvailClientConfig { - api_node_url: Some(conf.api_node_url.clone()), - app_id: Some(conf.app_id), - }), - ), - AvailClientConfig::GasRelay(conf) => Some( - proto::avail_config::Config::GasRelay(proto::AvailGasRelayConfig { - gas_relay_api_url: Some(conf.gas_relay_api_url.clone()), - max_retries: Some(conf.max_retries as u64), - }), - ), - }, - }, - )), - }, - ObjectStore(config) => Self { - config: Some(proto::data_availability_client::Config::ObjectStore( - object_store_proto::ObjectStore::build(config), - )), - }, + let config = match &this { + Avail(config) => proto::data_availability_client::Config::Avail(proto::AvailConfig { + bridge_api_url: Some(config.bridge_api_url.clone()), + timeout_ms: Some(config.timeout_ms as u64), + config: match &config.config { + AvailClientConfig::FullClient(conf) => Some( + proto::avail_config::Config::FullClient(proto::AvailClientConfig { + api_node_url: Some(conf.api_node_url.clone()), + app_id: Some(conf.app_id), + }), + ), + AvailClientConfig::GasRelay(conf) => Some( + proto::avail_config::Config::GasRelay(proto::AvailGasRelayConfig { + gas_relay_api_url: Some(conf.gas_relay_api_url.clone()), + max_retries: Some(conf.max_retries as u64), + }), + ), + }, + }), + Celestia(config) => { + proto::data_availability_client::Config::Celestia(proto::CelestiaConfig { + api_node_url: Some(config.api_node_url.clone()), + namespace: Some(config.namespace.clone()), + chain_id: Some(config.chain_id.clone()), + timeout_ms: Some(config.timeout_ms), + }) + } + Eigen(config) => proto::data_availability_client::Config::Eigen(proto::EigenConfig { + rpc_node_url: Some(config.rpc_node_url.clone()), + inclusion_polling_interval_ms: Some(config.inclusion_polling_interval_ms), + }), + ObjectStore(config) => proto::data_availability_client::Config::ObjectStore( + object_store_proto::ObjectStore::build(config), + ), + }; + + Self { + config: Some(config), } } } diff --git a/core/lib/protobuf_config/src/external_price_api_client.rs b/core/lib/protobuf_config/src/external_price_api_client.rs index e5ed809a128..dbc341c1865 100644 --- a/core/lib/protobuf_config/src/external_price_api_client.rs +++ b/core/lib/protobuf_config/src/external_price_api_client.rs @@ -17,6 +17,9 @@ impl ProtoRepr for proto::ExternalPriceApiClient { numerator: self.forced_numerator, denominator: self.forced_denominator, fluctuation: self.forced_fluctuation, + next_value_fluctuation: self.forced_next_value_fluctuation.unwrap_or( + configs::external_price_api_client::DEFAULT_FORCED_NEXT_VALUE_FLUCTUATION, + ), }), }, ) @@ -26,6 +29,7 @@ impl ProtoRepr for proto::ExternalPriceApiClient { let numerator = this.forced.as_ref().and_then(|x| x.numerator); let denominator = this.forced.as_ref().and_then(|x| x.denominator); let fluctuation = this.forced.as_ref().and_then(|x| x.fluctuation); + let next_value_fluctuation = this.forced.as_ref().map(|x| x.next_value_fluctuation); Self { source: Some(this.source.clone()), @@ -35,6 +39,7 @@ impl ProtoRepr for proto::ExternalPriceApiClient { forced_numerator: numerator, forced_denominator: denominator, forced_fluctuation: fluctuation, + forced_next_value_fluctuation: next_value_fluctuation, } } } diff --git a/core/lib/protobuf_config/src/general.rs b/core/lib/protobuf_config/src/general.rs index b73539a0897..83b4c84f20b 100644 --- a/core/lib/protobuf_config/src/general.rs +++ b/core/lib/protobuf_config/src/general.rs @@ -46,6 +46,7 @@ impl ProtoRepr for proto::GeneralConfig { ), experimental_vm_config: read_optional_repr(&self.experimental_vm), prover_job_monitor_config: read_optional_repr(&self.prover_job_monitor), + timestamp_asserter_config: read_optional_repr(&self.timestamp_asserter), }) } @@ -106,6 +107,10 @@ impl ProtoRepr for proto::GeneralConfig { .prover_job_monitor_config .as_ref() .map(ProtoRepr::build), + timestamp_asserter: this + .timestamp_asserter_config + .as_ref() + .map(ProtoRepr::build), } } } diff --git a/core/lib/protobuf_config/src/lib.rs b/core/lib/protobuf_config/src/lib.rs index 68f7f699de2..09f42422c51 100644 --- a/core/lib/protobuf_config/src/lib.rs +++ b/core/lib/protobuf_config/src/lib.rs @@ -36,6 +36,7 @@ mod snapshot_recovery; mod snapshots_creator; #[cfg(test)] mod tests; +mod timestamp_asserter; mod utils; mod vm_runner; mod wallets; diff --git a/core/lib/protobuf_config/src/proto/config/contracts.proto b/core/lib/protobuf_config/src/proto/config/contracts.proto index 6ab03e6aa11..4ae0ee1614f 100644 --- a/core/lib/protobuf_config/src/proto/config/contracts.proto +++ b/core/lib/protobuf_config/src/proto/config/contracts.proto @@ -23,6 +23,7 @@ message L2 { optional string testnet_paymaster_addr = 1; // optional; H160 optional string da_validator_addr = 2; // optional; H160 optional string legacy_shared_bridge_addr = 3; // optional; H160 + optional string timestamp_asserter_addr = 4; // optional; H160 } message Bridge { diff --git a/core/lib/protobuf_config/src/proto/config/da_client.proto b/core/lib/protobuf_config/src/proto/config/da_client.proto index 73fa2435996..0a302120d77 100644 --- a/core/lib/protobuf_config/src/proto/config/da_client.proto +++ b/core/lib/protobuf_config/src/proto/config/da_client.proto @@ -6,14 +6,16 @@ import "zksync/config/object_store.proto"; message AvailConfig { optional string bridge_api_url = 2; - optional uint64 timeout = 5; oneof config { AvailClientConfig full_client = 7; AvailGasRelayConfig gas_relay = 8; } + optional uint64 timeout_ms = 9; + reserved 1; reserved "api_node_url"; reserved 3; reserved "seed"; reserved 4; reserved "app_id"; + reserved 5; reserved "timeout"; reserved 6; reserved "max_retries"; } @@ -27,10 +29,24 @@ message AvailGasRelayConfig { optional uint64 max_retries = 2; } +message CelestiaConfig { + optional string api_node_url = 1; + optional string namespace = 2; + optional string chain_id = 3; + optional uint64 timeout_ms = 4; +} + +message EigenConfig { + optional string rpc_node_url = 1; + optional uint64 inclusion_polling_interval_ms = 2; +} + message DataAvailabilityClient { // oneof in protobuf allows for None oneof config { AvailConfig avail = 1; object_store.ObjectStore object_store = 2; + CelestiaConfig celestia = 3; + EigenConfig eigen = 4; } } diff --git a/core/lib/protobuf_config/src/proto/config/external_price_api_client.proto b/core/lib/protobuf_config/src/proto/config/external_price_api_client.proto index 646bcfbd764..63f3233c575 100644 --- a/core/lib/protobuf_config/src/proto/config/external_price_api_client.proto +++ b/core/lib/protobuf_config/src/proto/config/external_price_api_client.proto @@ -10,4 +10,5 @@ message ExternalPriceApiClient { optional uint64 forced_numerator = 5; optional uint64 forced_denominator = 6; optional uint32 forced_fluctuation = 7; + optional uint32 forced_next_value_fluctuation = 8; } diff --git a/core/lib/protobuf_config/src/proto/config/general.proto b/core/lib/protobuf_config/src/proto/config/general.proto index ee70b61b18b..216272f3f9a 100644 --- a/core/lib/protobuf_config/src/proto/config/general.proto +++ b/core/lib/protobuf_config/src/proto/config/general.proto @@ -26,6 +26,7 @@ import "zksync/config/external_proof_integration_api.proto"; import "zksync/core/consensus.proto"; import "zksync/config/prover_job_monitor.proto"; import "zksync/config/da_client.proto"; +import "zksync/config/timestamp_asserter.proto"; message GeneralConfig { optional database.Postgres postgres = 1; @@ -62,4 +63,5 @@ message GeneralConfig { optional experimental.Vm experimental_vm = 44; optional prover_job_monitor.ProverJobMonitor prover_job_monitor = 45; optional da_client.DataAvailabilityClient da_client = 46; + optional timestamp_asserter.TimestampAsserter timestamp_asserter = 47; } diff --git a/core/lib/protobuf_config/src/proto/config/prover_autoscaler.proto b/core/lib/protobuf_config/src/proto/config/prover_autoscaler.proto index 9b7f201e9b7..74218165386 100644 --- a/core/lib/protobuf_config/src/proto/config/prover_autoscaler.proto +++ b/core/lib/protobuf_config/src/proto/config/prover_autoscaler.proto @@ -45,15 +45,30 @@ message MinProver { optional uint32 min = 2; // required } +message MaxReplica { + optional string cluster = 1; // required + optional uint64 max = 2; // required +} + +message ScalerTarget { + optional string queue_report_field = 1; // required + optional string deployment = 5; // required + repeated MaxReplica max_replicas = 3; // required at least one + optional uint64 speed = 4; // optional + reserved 2; reserved "pod_name_prefix"; +} + message ProverAutoscalerScalerConfig { optional uint32 prometheus_port = 1; // required optional std.Duration scaler_run_interval = 2; // optional optional string prover_job_monitor_url = 3; // required repeated string agents = 4; // required at least one - repeated ProtocolVersion protocol_versions = 5; // repeated at least one + repeated ProtocolVersion protocol_versions = 5; // required at least one repeated ClusterPriority cluster_priorities = 6; // optional repeated ProverSpeed prover_speed = 7; // optional optional uint32 long_pending_duration_s = 8; // optional repeated MaxProver max_provers = 9; // optional repeated MinProver min_provers = 10; // optional + repeated ScalerTarget scaler_targets = 11; // optional + optional bool dry_run = 12; // optional } diff --git a/core/lib/protobuf_config/src/proto/config/secrets.proto b/core/lib/protobuf_config/src/proto/config/secrets.proto index 43c4542783c..7c9d0f92823 100644 --- a/core/lib/protobuf_config/src/proto/config/secrets.proto +++ b/core/lib/protobuf_config/src/proto/config/secrets.proto @@ -24,9 +24,19 @@ message AvailSecret { optional string gas_relay_api_key = 2; } +message CelestiaSecret { + optional string private_key = 1; +} + +message EigenSecret { + optional string private_key = 1; +} + message DataAvailabilitySecrets { oneof da_secrets { AvailSecret avail = 1; + CelestiaSecret celestia = 2; + EigenSecret eigen = 3; } } diff --git a/core/lib/protobuf_config/src/proto/config/timestamp_asserter.proto b/core/lib/protobuf_config/src/proto/config/timestamp_asserter.proto new file mode 100644 index 00000000000..c8d0b9d1fec --- /dev/null +++ b/core/lib/protobuf_config/src/proto/config/timestamp_asserter.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package zksync.config.timestamp_asserter; + +message TimestampAsserter { + optional uint32 min_time_till_end_sec = 1; // required; u32 +} diff --git a/core/lib/protobuf_config/src/prover_autoscaler.rs b/core/lib/protobuf_config/src/prover_autoscaler.rs index 51f1b162d4c..6b67d9f620f 100644 --- a/core/lib/protobuf_config/src/prover_autoscaler.rs +++ b/core/lib/protobuf_config/src/prover_autoscaler.rs @@ -112,6 +112,13 @@ impl ProtoRepr for proto::ProverAutoscalerScalerConfig { .map(|(i, e)| e.read().context(i)) .collect::>() .context("min_provers")?, + scaler_targets: self + .scaler_targets + .iter() + .enumerate() + .map(|(i, x)| x.read().context(i).unwrap()) + .collect::>(), + dry_run: self.dry_run.unwrap_or_default(), }) } @@ -151,6 +158,8 @@ impl ProtoRepr for proto::ProverAutoscalerScalerConfig { .iter() .map(|(k, v)| proto::MinProver::build(&(k.clone(), *v))) .collect(), + scaler_targets: this.scaler_targets.iter().map(ProtoRepr::build).collect(), + dry_run: Some(this.dry_run), } } } @@ -238,3 +247,55 @@ impl ProtoRepr for proto::MinProver { } } } + +impl ProtoRepr for proto::MaxReplica { + type Type = (String, usize); + fn read(&self) -> anyhow::Result { + Ok(( + required(&self.cluster).context("cluster")?.parse()?, + *required(&self.max).context("max")? as usize, + )) + } + fn build(this: &Self::Type) -> Self { + Self { + cluster: Some(this.0.to_string()), + max: Some(this.1 as u64), + } + } +} + +impl ProtoRepr for proto::ScalerTarget { + type Type = configs::prover_autoscaler::ScalerTarget; + fn read(&self) -> anyhow::Result { + Ok(Self::Type { + queue_report_field: required(&self.queue_report_field) + .and_then(|x| Ok((*x).parse()?)) + .context("queue_report_field")?, + deployment: required(&self.deployment).context("deployment")?.clone(), + max_replicas: self + .max_replicas + .iter() + .enumerate() + .map(|(i, e)| e.read().context(i)) + .collect::>() + .context("max_replicas")?, + speed: match self.speed { + Some(x) => x as usize, + None => Self::Type::default_speed(), + }, + }) + } + + fn build(this: &Self::Type) -> Self { + Self { + queue_report_field: Some(this.queue_report_field.to_string()), + deployment: Some(this.deployment.clone()), + max_replicas: this + .max_replicas + .iter() + .map(|(k, v)| proto::MaxReplica::build(&(k.clone(), *v))) + .collect(), + speed: Some(this.speed as u64), + } + } +} diff --git a/core/lib/protobuf_config/src/secrets.rs b/core/lib/protobuf_config/src/secrets.rs index 07ab340c231..f5bc10a3e34 100644 --- a/core/lib/protobuf_config/src/secrets.rs +++ b/core/lib/protobuf_config/src/secrets.rs @@ -2,20 +2,20 @@ use std::str::FromStr; use anyhow::Context; use secrecy::ExposeSecret; -use zksync_basic_types::{api_key::APIKey, seed_phrase::SeedPhrase, url::SensitiveUrl}; +use zksync_basic_types::{ + secrets::{APIKey, PrivateKey, SeedPhrase}, + url::SensitiveUrl, +}; use zksync_config::configs::{ consensus::{AttesterSecretKey, ConsensusSecrets, NodeSecretKey, ValidatorSecretKey}, - da_client::avail::AvailSecrets, + da_client::{avail::AvailSecrets, celestia::CelestiaSecrets, eigen::EigenSecrets}, secrets::{DataAvailabilitySecrets, Secrets}, DatabaseSecrets, L1Secrets, }; use zksync_protobuf::{required, ProtoRepr}; use crate::{ - proto::{ - secrets as proto, - secrets::{data_availability_secrets::DaSecrets, AvailSecret}, - }, + proto::{secrets as proto, secrets::data_availability_secrets::DaSecrets}, read_optional_repr, }; @@ -128,6 +128,16 @@ impl ProtoRepr for proto::DataAvailabilitySecrets { gas_relay_api_key, }) } + DaSecrets::Celestia(celestia) => DataAvailabilitySecrets::Celestia(CelestiaSecrets { + private_key: PrivateKey::from_str( + required(&celestia.private_key).context("private_key")?, + )?, + }), + DaSecrets::Eigen(eigen) => DataAvailabilitySecrets::Eigen(EigenSecrets { + private_key: PrivateKey::from_str( + required(&eigen.private_key).context("private_key")?, + )?, + }), }; Ok(client) @@ -164,11 +174,19 @@ impl ProtoRepr for proto::DataAvailabilitySecrets { None }; - Some(DaSecrets::Avail(AvailSecret { + Some(DaSecrets::Avail(proto::AvailSecret { seed_phrase, gas_relay_api_key, })) } + DataAvailabilitySecrets::Celestia(config) => { + Some(DaSecrets::Celestia(proto::CelestiaSecret { + private_key: Some(config.private_key.0.expose_secret().to_string()), + })) + } + DataAvailabilitySecrets::Eigen(config) => Some(DaSecrets::Eigen(proto::EigenSecret { + private_key: Some(config.private_key.0.expose_secret().to_string()), + })), }; Self { diff --git a/core/lib/protobuf_config/src/timestamp_asserter.rs b/core/lib/protobuf_config/src/timestamp_asserter.rs new file mode 100644 index 00000000000..5984caff8c6 --- /dev/null +++ b/core/lib/protobuf_config/src/timestamp_asserter.rs @@ -0,0 +1,19 @@ +use anyhow::Context; +use zksync_config::configs::chain::TimestampAsserterConfig; +use zksync_protobuf::{required, ProtoRepr}; + +impl ProtoRepr for crate::proto::config::timestamp_asserter::TimestampAsserter { + type Type = TimestampAsserterConfig; + fn read(&self) -> anyhow::Result { + Ok(Self::Type { + min_time_till_end_sec: *required(&self.min_time_till_end_sec) + .context("timestamp_asserter_min_time_till_end_sec")?, + }) + } + + fn build(this: &Self::Type) -> Self { + Self { + min_time_till_end_sec: Some(this.min_time_till_end_sec), + } + } +} diff --git a/core/lib/types/src/api/mod.rs b/core/lib/types/src/api/mod.rs index a4eb6460553..409dc372757 100644 --- a/core/lib/types/src/api/mod.rs +++ b/core/lib/types/src/api/mod.rs @@ -257,8 +257,6 @@ pub struct TransactionReceipt { pub l2_to_l1_logs: Vec, /// Status: either 1 (success) or 0 (failure). pub status: U64, - /// State root. - pub root: H256, /// Logs bloom #[serde(rename = "logsBloom")] pub logs_bloom: Bloom, diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index 69e6e42fd51..320264f28f0 100644 --- a/core/lib/types/src/lib.rs +++ b/core/lib/types/src/lib.rs @@ -5,7 +5,7 @@ #![allow(clippy::upper_case_acronyms, clippy::derive_partial_eq_without_eq)] -use std::fmt; +use std::{fmt, ops::Range}; use anyhow::Context as _; use fee::encoding_len; @@ -416,3 +416,8 @@ impl Transaction { }) } } + +#[derive(Clone, Serialize, Debug, Default, Eq, PartialEq, Hash)] +pub struct TransactionTimeRangeConstraint { + pub timestamp_asserter_range: Option>, +} diff --git a/core/lib/vm_executor/src/oneshot/mock.rs b/core/lib/vm_executor/src/oneshot/mock.rs index a7363c633c6..e211328b5ec 100644 --- a/core/lib/vm_executor/src/oneshot/mock.rs +++ b/core/lib/vm_executor/src/oneshot/mock.rs @@ -4,18 +4,21 @@ use async_trait::async_trait; use zksync_multivm::interface::{ executor::{OneshotExecutor, TransactionValidator}, storage::ReadStorage, - tracer::{ValidationError, ValidationParams}, + tracer::{ValidationError, ValidationParams, ValidationTraces}, ExecutionResult, OneshotEnv, OneshotTracingParams, OneshotTransactionExecutionResult, TxExecutionArgs, TxExecutionMode, VmExecutionResultAndLogs, }; use zksync_types::{l2::L2Tx, Transaction}; type TxResponseFn = dyn Fn(&Transaction, &OneshotEnv) -> VmExecutionResultAndLogs + Send + Sync; +type TxValidationTracesResponseFn = + dyn Fn(&Transaction, &OneshotEnv) -> ValidationTraces + Send + Sync; /// Mock [`OneshotExecutor`] implementation. pub struct MockOneshotExecutor { call_responses: Box, tx_responses: Box, + tx_validation_traces_responses: Box, } impl fmt::Debug for MockOneshotExecutor { @@ -35,6 +38,7 @@ impl Default for MockOneshotExecutor { tx_responses: Box::new(|tx, _| { panic!("Unexpect transaction call: {tx:?}"); }), + tx_validation_traces_responses: Box::new(|_, _| ValidationTraces::default()), } } } @@ -57,6 +61,13 @@ impl MockOneshotExecutor { self.tx_responses = self.wrap_responses(responses); } + pub fn set_tx_validation_traces_responses(&mut self, responses: F) + where + F: Fn(&Transaction, &OneshotEnv) -> ValidationTraces + 'static + Send + Sync, + { + self.tx_validation_traces_responses = Box::new(responses); + } + fn wrap_responses(&mut self, responses: F) -> Box where F: Fn(&Transaction, &OneshotEnv) -> ExecutionResult + 'static + Send + Sync, @@ -82,11 +93,11 @@ impl MockOneshotExecutor { self.tx_responses = Box::new(responses); } - fn mock_inspect(&self, env: OneshotEnv, args: TxExecutionArgs) -> VmExecutionResultAndLogs { + fn mock_inspect(&self, env: &OneshotEnv, args: TxExecutionArgs) -> VmExecutionResultAndLogs { match env.system.execution_mode { - TxExecutionMode::EthCall => (self.call_responses)(&args.transaction, &env), + TxExecutionMode::EthCall => (self.call_responses)(&args.transaction, env), TxExecutionMode::VerifyExecute | TxExecutionMode::EstimateFee => { - (self.tx_responses)(&args.transaction, &env) + (self.tx_responses)(&args.transaction, env) } } } @@ -105,7 +116,7 @@ where _params: OneshotTracingParams, ) -> anyhow::Result { Ok(OneshotTransactionExecutionResult { - tx_result: Box::new(self.mock_inspect(env, args)), + tx_result: Box::new(self.mock_inspect(&env, args)), compression_result: Ok(()), call_traces: vec![], }) @@ -123,14 +134,16 @@ where env: OneshotEnv, tx: L2Tx, _validation_params: ValidationParams, - ) -> anyhow::Result> { + ) -> anyhow::Result> { Ok( match self - .mock_inspect(env, TxExecutionArgs::for_validation(tx)) + .mock_inspect(&env, TxExecutionArgs::for_validation(tx.clone())) .result { ExecutionResult::Halt { reason } => Err(ValidationError::FailedTx(reason)), - ExecutionResult::Success { .. } | ExecutionResult::Revert { .. } => Ok(()), + ExecutionResult::Success { .. } | ExecutionResult::Revert { .. } => { + Ok((self.tx_validation_traces_responses)(&tx.into(), &env)) + } }, ) } diff --git a/core/lib/vm_executor/src/oneshot/mod.rs b/core/lib/vm_executor/src/oneshot/mod.rs index 522349ca369..42da1b5a375 100644 --- a/core/lib/vm_executor/src/oneshot/mod.rs +++ b/core/lib/vm_executor/src/oneshot/mod.rs @@ -18,7 +18,7 @@ use zksync_multivm::{ interface::{ executor::{OneshotExecutor, TransactionValidator}, storage::{ReadStorage, StorageView, StorageWithOverrides}, - tracer::{ValidationError, ValidationParams}, + tracer::{ValidationError, ValidationParams, ValidationTraces}, utils::{DivergenceHandler, ShadowVm}, Call, ExecutionResult, InspectExecutionMode, OneshotEnv, OneshotTracingParams, OneshotTransactionExecutionResult, StoredL2BlockEnv, TxExecutionArgs, TxExecutionMode, @@ -172,13 +172,14 @@ where env: OneshotEnv, tx: L2Tx, validation_params: ValidationParams, - ) -> anyhow::Result> { + ) -> anyhow::Result> { anyhow::ensure!( env.system.execution_mode == TxExecutionMode::VerifyExecute, "Unexpected execution mode for tx validation: {:?} (expected `VerifyExecute`)", env.system.execution_mode ); + let l1_batch_env = env.l1_batch.clone(); let sandbox = VmSandbox { fast_vm_mode: if !is_supported_by_fast_vm(env.system.version) { FastVmMode::Old // the fast VM doesn't support old protocol versions @@ -197,8 +198,13 @@ where sandbox.execute_in_vm_with_tracer(|vm, transaction| match vm { Vm::Legacy(vm) => { - let (validation_tracer, mut validation_result) = - ValidationTracer::::new(validation_params, version); + let validation_tracer = ValidationTracer::::new( + validation_params, + version, + l1_batch_env.timestamp, + ); + let mut validation_result = validation_tracer.get_result(); + let validation_traces = validation_tracer.get_traces(); let tracers = validation_tracer.into_tracer_pointer(); vm.push_transaction(transaction); @@ -215,15 +221,19 @@ where (ExecutionResult::Halt { reason }, _) => { Err(ValidationError::FailedTx(reason)) } - _ => Ok(()), + _ => Ok(validation_traces.lock().unwrap().clone()), } } Vm::Fast(FastVmInstance::Fast(vm)) => { vm.push_transaction(transaction); - let mut tracer = WithBuiltinTracers::for_validation((), validation_params); + let mut tracer = WithBuiltinTracers::for_validation( + (), + validation_params, + l1_batch_env.timestamp, + ); let result_and_logs = vm.inspect(&mut tracer, InspectExecutionMode::OneTx); - if let Some(violation) = tracer.validation_error() { + if let Some(violation) = tracer.validation().validation_error() { return Err(ValidationError::ViolatedRule(violation)); } match result_and_logs.result { @@ -231,16 +241,25 @@ where ExecutionResult::Revert { .. } => { unreachable!("Revert can only happen at the end of a transaction") } - ExecutionResult::Success { .. } => Ok(()), + ExecutionResult::Success { .. } => Ok(tracer.validation().traces()), } } Vm::Fast(FastVmInstance::Shadowed(vm)) => { vm.push_transaction(transaction); - let tracer = WithBuiltinTracers::for_validation((), validation_params.clone()); + let tracer = WithBuiltinTracers::for_validation( + (), + validation_params.clone(), + l1_batch_env.timestamp, + ); - let (validation_tracer, mut validation_result) = - ValidationTracer::::new(validation_params, version); + let validation_tracer = ValidationTracer::::new( + validation_params, + version, + l1_batch_env.timestamp, + ); + let validation_traces = validation_tracer.get_traces(); + let mut validation_result = validation_tracer.get_result(); let legacy_tracers: Box< dyn MultiVMTracer>, HistoryEnabled>, > = validation_tracer.into_tracer_pointer(); @@ -252,7 +271,8 @@ where let result_and_logs = vm.inspect(&mut aggregate_tracer, InspectExecutionMode::OneTx); - let fast_result = if let Some(violation) = aggregate_tracer.1.validation_error() + let fast_result = if let Some(violation) = + aggregate_tracer.1.validation().validation_error() { Err(ValidationError::ViolatedRule(violation)) } else { @@ -263,7 +283,9 @@ where ExecutionResult::Revert { .. } => { unreachable!("Revert can only happen at the end of a transaction") } - ExecutionResult::Success { .. } => Ok(()), + ExecutionResult::Success { .. } => { + Ok(aggregate_tracer.1.validation().traces()) + } } }; @@ -278,7 +300,7 @@ where (ExecutionResult::Halt { reason }, _) => { Err(ValidationError::FailedTx(reason)) } - _ => Ok(()), + _ => Ok(validation_traces.lock().unwrap().clone()), }; if fast_result != legacy_result { diff --git a/core/lib/vm_interface/src/executor.rs b/core/lib/vm_interface/src/executor.rs index 60522ba338a..30534b1420c 100644 --- a/core/lib/vm_interface/src/executor.rs +++ b/core/lib/vm_interface/src/executor.rs @@ -7,7 +7,7 @@ use zksync_types::{commitment::PubdataParams, l2::L2Tx, Transaction}; use crate::{ storage::{ReadStorage, StorageView}, - tracer::{ValidationError, ValidationParams}, + tracer::{ValidationError, ValidationParams, ValidationTraces}, BatchTransactionExecutionResult, FinishedL1Batch, L1BatchEnv, L2BlockEnv, OneshotEnv, OneshotTracingParams, OneshotTransactionExecutionResult, SystemEnv, TxExecutionArgs, }; @@ -69,5 +69,5 @@ pub trait TransactionValidator: OneshotExecutor { env: OneshotEnv, tx: L2Tx, validation_params: ValidationParams, - ) -> anyhow::Result>; + ) -> anyhow::Result>; } diff --git a/core/lib/vm_interface/src/types/errors/halt.rs b/core/lib/vm_interface/src/types/errors/halt.rs index 88328e42b81..d24f55ab504 100644 --- a/core/lib/vm_interface/src/types/errors/halt.rs +++ b/core/lib/vm_interface/src/types/errors/halt.rs @@ -42,6 +42,7 @@ pub enum Halt { VMPanic, TracerCustom(String), FailedToPublishCompressedBytecodes, + FailedBlockTimestampAssertion, } impl fmt::Display for Halt { @@ -116,6 +117,9 @@ impl fmt::Display for Halt { Halt::FailedToPublishCompressedBytecodes => { write!(f, "Failed to publish compressed bytecodes") } + Halt::FailedBlockTimestampAssertion => { + write!(f, "Transaction failed block.timestamp assertion") + } } } } diff --git a/core/lib/vm_interface/src/types/tracer.rs b/core/lib/vm_interface/src/types/tracer.rs index eb0c608b494..168834f28ce 100644 --- a/core/lib/vm_interface/src/types/tracer.rs +++ b/core/lib/vm_interface/src/types/tracer.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, fmt}; +use std::{collections::HashSet, fmt, ops::Range, time}; use zksync_types::{Address, U256}; @@ -57,6 +57,17 @@ pub struct ValidationParams { pub trusted_address_slots: HashSet<(Address, U256)>, /// Number of computational gas that validation step is allowed to use. pub computational_gas_limit: u32, + /// Parameters of the timestamp asserter if configured + pub timestamp_asserter_params: Option, +} + +#[derive(Debug, Clone)] +pub struct TimestampAsserterParams { + /// Address of the timestamp asserter. This contract is allowed to touch block.timestamp regardless + /// of the calling context. + pub address: Address, + /// Minimum time between current block.timestamp and the end of the asserted range + pub min_time_till_end: time::Duration, } /// Rules that can be violated when validating a transaction. @@ -70,6 +81,8 @@ pub enum ViolatedValidationRule { TouchedDisallowedContext, /// The transaction used too much gas during validation. TookTooManyComputationalGas(u32), + /// The transaction failed block.timestamp assertion because the block.timestamp is too close to the range end + TimestampAssertionCloseToRangeEnd, } impl fmt::Display for ViolatedValidationRule { @@ -91,6 +104,9 @@ impl fmt::Display for ViolatedValidationRule { "Took too many computational gas, allowed limit: {gas_limit}" ) } + ViolatedValidationRule::TimestampAssertionCloseToRangeEnd => { + write!(f, "block.timestamp is too close to the range end") + } } } } @@ -104,6 +120,30 @@ pub enum ValidationError { ViolatedRule(ViolatedValidationRule), } +/// Traces the validation of a transaction, providing visibility into the aspects the transaction interacts with. +/// For instance, the `timestamp_asserter_range` represent the range within which the transaction might make +/// assertions on `block.timestamp`. This information is crucial for the caller, as expired transactions should +/// be excluded from the mempool. +#[derive(Debug, Clone, Default, PartialEq)] +pub struct ValidationTraces { + pub timestamp_asserter_range: Option>, +} + +impl ValidationTraces { + /// Merges two ranges by selecting the maximum of the start values and the minimum of the end values, + /// producing the narrowest possible time window. Note that overlapping ranges are essential; + /// a lack of overlap would have triggered an assertion failure in the `TimestampAsserter` contract, + /// as `block.timestamp` cannot satisfy two non-overlapping ranges. + pub fn apply_timestamp_asserter_range(&mut self, new_range: Range) { + if let Some(range) = &mut self.timestamp_asserter_range { + range.start = range.start.max(new_range.start); + range.end = range.end.min(new_range.end); + } else { + self.timestamp_asserter_range = Some(new_range); + } + } +} + impl fmt::Display for ValidationError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -116,3 +156,36 @@ impl fmt::Display for ValidationError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_apply_range_when_none() { + let mut validation_traces = ValidationTraces { + timestamp_asserter_range: None, + }; + let new_range = 10..20; + validation_traces.apply_timestamp_asserter_range(new_range.clone()); + assert_eq!(validation_traces.timestamp_asserter_range, Some(new_range)); + } + + #[test] + fn test_apply_range_with_overlap_narrower_result() { + let mut validation_traces = ValidationTraces { + timestamp_asserter_range: Some(5..25), + }; + validation_traces.apply_timestamp_asserter_range(10..20); + assert_eq!(validation_traces.timestamp_asserter_range, Some(10..20)); + } + + #[test] + fn test_apply_range_with_partial_overlap() { + let mut validation_traces = ValidationTraces { + timestamp_asserter_range: Some(10..30), + }; + validation_traces.apply_timestamp_asserter_range(20..40); + assert_eq!(validation_traces.timestamp_asserter_range, Some(20..30)); + } +} diff --git a/core/lib/vm_interface/src/utils/shadow.rs b/core/lib/vm_interface/src/utils/shadow.rs index c2dd2fcb9eb..91080f5a6fa 100644 --- a/core/lib/vm_interface/src/utils/shadow.rs +++ b/core/lib/vm_interface/src/utils/shadow.rs @@ -189,6 +189,16 @@ impl CheckDivergence for VmExecutionResultAndLogs { &self.statistics.computational_gas_used, &other.statistics.computational_gas_used, ); + + if let (Some(these_deps), Some(other_deps)) = + (&self.new_known_factory_deps, &other.new_known_factory_deps) + { + // Order deps to have a more reasonable diff on a mismatch + let these_deps = these_deps.iter().collect::>(); + let other_deps = other_deps.iter().collect::>(); + errors.check_match("new_known_factory_deps", &these_deps, &other_deps); + } + errors } } diff --git a/core/lib/vm_interface/src/vm.rs b/core/lib/vm_interface/src/vm.rs index d6205e5656d..d84765eb2d6 100644 --- a/core/lib/vm_interface/src/vm.rs +++ b/core/lib/vm_interface/src/vm.rs @@ -22,7 +22,6 @@ use crate::{ }; pub trait VmInterface { - /// Lifetime is used to be able to define `Option<&mut _>` as a dispatcher. type TracerDispatcher: Default; /// Pushes a transaction to bootloader memory for future execution with bytecode compression (if it's supported by the VM). diff --git a/core/lib/web3_decl/src/namespaces/zks.rs b/core/lib/web3_decl/src/namespaces/zks.rs index 47aae2a0835..07a7cc4ff1c 100644 --- a/core/lib/web3_decl/src/namespaces/zks.rs +++ b/core/lib/web3_decl/src/namespaces/zks.rs @@ -51,6 +51,9 @@ pub trait ZksNamespace { #[method(name = "getTestnetPaymaster")] async fn get_testnet_paymaster(&self) -> RpcResult>; + #[method(name = "getTimestampAsserter")] + async fn get_timestamp_asserter(&self) -> RpcResult>; + #[method(name = "getBridgeContracts")] async fn get_bridge_contracts(&self) -> RpcResult; diff --git a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs index eb2170bcc84..5faef68507f 100644 --- a/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs +++ b/core/lib/zksync_core_leftovers/src/temp_config_store/mod.rs @@ -6,7 +6,7 @@ use zksync_config::{ api::{HealthCheckConfig, MerkleTreeApiConfig, Web3JsonRpcConfig}, chain::{ CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, - StateKeeperConfig, + StateKeeperConfig, TimestampAsserterConfig, }, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, @@ -81,6 +81,7 @@ pub struct TempConfigStore { pub external_proof_integration_api_config: Option, pub experimental_vm_config: Option, pub prover_job_monitor_config: Option, + pub timestamp_asserter_config: Option, } impl TempConfigStore { @@ -122,6 +123,7 @@ impl TempConfigStore { .clone(), experimental_vm_config: self.experimental_vm_config.clone(), prover_job_monitor_config: self.prover_job_monitor_config.clone(), + timestamp_asserter_config: self.timestamp_asserter_config.clone(), } } @@ -203,6 +205,7 @@ fn load_env_config() -> anyhow::Result { external_proof_integration_api_config: ExternalProofIntegrationApiConfig::from_env().ok(), experimental_vm_config: ExperimentalVmConfig::from_env().ok(), prover_job_monitor_config: ProverJobMonitorConfig::from_env().ok(), + timestamp_asserter_config: TimestampAsserterConfig::from_env().ok(), }) } diff --git a/core/node/api_server/src/execution_sandbox/error.rs b/core/node/api_server/src/execution_sandbox/error.rs index 5d63d50a3c8..4523412ae19 100644 --- a/core/node/api_server/src/execution_sandbox/error.rs +++ b/core/node/api_server/src/execution_sandbox/error.rs @@ -26,6 +26,8 @@ pub(crate) enum SandboxExecutionError { that caused this error. Error description: {0}" )] UnexpectedVMBehavior(String), + #[error("Transaction failed block.timestamp assertion")] + FailedBlockTimestampAssertion, } impl From for SandboxExecutionError { @@ -67,6 +69,7 @@ impl From for SandboxExecutionError { Halt::FailedToPublishCompressedBytecodes => { Self::UnexpectedVMBehavior("Failed to publish compressed bytecodes".to_string()) } + Halt::FailedBlockTimestampAssertion => Self::FailedBlockTimestampAssertion, } } } diff --git a/core/node/api_server/src/execution_sandbox/execute.rs b/core/node/api_server/src/execution_sandbox/execute.rs index 7958b5ed3c1..d58bf6ca38f 100644 --- a/core/node/api_server/src/execution_sandbox/execute.rs +++ b/core/node/api_server/src/execution_sandbox/execute.rs @@ -9,7 +9,7 @@ use zksync_dal::{Connection, Core}; use zksync_multivm::interface::{ executor::{OneshotExecutor, TransactionValidator}, storage::{ReadStorage, StorageWithOverrides}, - tracer::{ValidationError, ValidationParams}, + tracer::{TimestampAsserterParams, ValidationError, ValidationParams, ValidationTraces}, Call, OneshotEnv, OneshotTracingParams, OneshotTransactionExecutionResult, TransactionExecutionMetrics, TxExecutionArgs, VmExecutionResultAndLogs, }; @@ -99,6 +99,7 @@ pub(crate) struct SandboxExecutor { engine: SandboxExecutorEngine, pub(super) options: SandboxExecutorOptions, storage_caches: Option, + pub(super) timestamp_asserter_params: Option, } impl SandboxExecutor { @@ -106,6 +107,7 @@ impl SandboxExecutor { options: SandboxExecutorOptions, caches: PostgresStorageCaches, missed_storage_invocation_limit: usize, + timestamp_asserter_params: Option, ) -> Self { let mut executor = MainOneshotExecutor::new(missed_storage_invocation_limit); executor.set_fast_vm_mode(options.fast_vm_mode); @@ -117,6 +119,7 @@ impl SandboxExecutor { engine: SandboxExecutorEngine::Real(executor), options, storage_caches: Some(caches), + timestamp_asserter_params, } } @@ -132,6 +135,7 @@ impl SandboxExecutor { engine: SandboxExecutorEngine::Mock(executor), options, storage_caches: None, + timestamp_asserter_params: None, } } @@ -295,7 +299,7 @@ where env: OneshotEnv, tx: L2Tx, validation_params: ValidationParams, - ) -> anyhow::Result> { + ) -> anyhow::Result> { match &self.engine { SandboxExecutorEngine::Real(executor) => { executor diff --git a/core/node/api_server/src/execution_sandbox/tests.rs b/core/node/api_server/src/execution_sandbox/tests.rs index e342f2d73de..0aff15b973e 100644 --- a/core/node/api_server/src/execution_sandbox/tests.rs +++ b/core/node/api_server/src/execution_sandbox/tests.rs @@ -217,6 +217,7 @@ async fn test_instantiating_vm(connection: Connection<'static, Core>, block_args SandboxExecutorOptions::mock().await, PostgresStorageCaches::new(1, 1), usize::MAX, + None, ); let fee_input = BatchFeeInput::l1_pegged(55, 555); @@ -265,6 +266,7 @@ async fn validating_transaction(set_balance: bool) { SandboxExecutorOptions::mock().await, PostgresStorageCaches::new(1, 1), usize::MAX, + None, ); let fee_input = BatchFeeInput::l1_pegged(55, 555); diff --git a/core/node/api_server/src/execution_sandbox/validate.rs b/core/node/api_server/src/execution_sandbox/validate.rs index 758547abbd6..3d58f807a89 100644 --- a/core/node/api_server/src/execution_sandbox/validate.rs +++ b/core/node/api_server/src/execution_sandbox/validate.rs @@ -6,7 +6,10 @@ use zksync_dal::{Connection, Core, CoreDal}; use zksync_multivm::interface::{ executor::TransactionValidator, storage::StorageWithOverrides, - tracer::{ValidationError as RawValidationError, ValidationParams}, + tracer::{ + TimestampAsserterParams, ValidationError as RawValidationError, ValidationParams, + ValidationTraces, + }, }; use zksync_types::{ fee_model::BatchFeeInput, l2::L2Tx, Address, TRUSTED_ADDRESS_SLOTS, TRUSTED_TOKEN_SLOTS, @@ -38,13 +41,14 @@ impl SandboxExecutor { block_args: BlockArgs, fee_input: BatchFeeInput, whitelisted_tokens_for_aa: &[Address], - ) -> Result<(), ValidationError> { + ) -> Result { let total_latency = SANDBOX_METRICS.sandbox[&SandboxStage::ValidateInSandbox].start(); let validation_params = get_validation_params( &mut connection, &tx, self.options.eth_call.validation_computational_gas_limit(), whitelisted_tokens_for_aa, + self.timestamp_asserter_params.clone(), ) .await .context("failed getting validation params")?; @@ -79,6 +83,7 @@ pub(super) async fn get_validation_params( tx: &L2Tx, computational_gas_limit: u32, whitelisted_tokens_for_aa: &[Address], + timestamp_asserter_params: Option, ) -> anyhow::Result { let method_latency = EXECUTION_METRICS.get_validation_params.start(); let user_address = tx.common_data.initiator_address; @@ -125,5 +130,6 @@ pub(super) async fn get_validation_params( trusted_addresses, trusted_address_slots, computational_gas_limit, + timestamp_asserter_params, }) } diff --git a/core/node/api_server/src/tx_sender/master_pool_sink.rs b/core/node/api_server/src/tx_sender/master_pool_sink.rs index 736edf0b247..06333f0c136 100644 --- a/core/node/api_server/src/tx_sender/master_pool_sink.rs +++ b/core/node/api_server/src/tx_sender/master_pool_sink.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::{Entry, HashMap}; use tokio::sync::Mutex; use zksync_dal::{transactions_dal::L2TxSubmissionResult, ConnectionPool, Core, CoreDal}; -use zksync_multivm::interface::TransactionExecutionMetrics; +use zksync_multivm::interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use zksync_shared_metrics::{TxStage, APP_METRICS}; use zksync_types::{l2::L2Tx, Address, Nonce, H256}; @@ -31,6 +31,7 @@ impl TxSink for MasterPoolSink { &self, tx: &L2Tx, execution_metrics: TransactionExecutionMetrics, + validation_traces: ValidationTraces, ) -> Result { let address_and_nonce = (tx.initiator_account(), tx.nonce()); @@ -55,7 +56,7 @@ impl TxSink for MasterPoolSink { let result = match self.master_pool.connection_tagged("api").await { Ok(mut connection) => connection .transactions_dal() - .insert_transaction_l2(tx, execution_metrics) + .insert_transaction_l2(tx, execution_metrics, validation_traces) .await .inspect(|submission_res_handle| { APP_METRICS.processed_txs[&TxStage::Mempool(*submission_res_handle)].inc(); diff --git a/core/node/api_server/src/tx_sender/mod.rs b/core/node/api_server/src/tx_sender/mod.rs index 75cc1ad602f..011d9e4e2b2 100644 --- a/core/node/api_server/src/tx_sender/mod.rs +++ b/core/node/api_server/src/tx_sender/mod.rs @@ -1,6 +1,6 @@ //! Helper module to submit transactions into the ZKsync Network. -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use anyhow::Context as _; use tokio::sync::RwLock; @@ -9,7 +9,10 @@ use zksync_dal::{ transactions_dal::L2TxSubmissionResult, Connection, ConnectionPool, Core, CoreDal, }; use zksync_multivm::{ - interface::{OneshotTracingParams, TransactionExecutionMetrics, VmExecutionResultAndLogs}, + interface::{ + tracer::TimestampAsserterParams as TracerTimestampAsserterParams, OneshotTracingParams, + TransactionExecutionMetrics, VmExecutionResultAndLogs, + }, utils::{derive_base_fee_and_gas_per_pubdata, get_max_batch_gas_limit}, }; use zksync_node_fee_model::{ApiFeeInputProvider, BatchFeeModelInputProvider}; @@ -205,6 +208,12 @@ impl TxSenderBuilder { executor_options, storage_caches, missed_storage_invocation_limit, + self.config.timestamp_asserter_params.clone().map(|params| { + TracerTimestampAsserterParams { + address: params.address, + min_time_till_end: params.min_time_till_end, + } + }), ); TxSender(Arc::new(TxSenderInner { @@ -234,6 +243,13 @@ pub struct TxSenderConfig { pub validation_computational_gas_limit: u32, pub chain_id: L2ChainId, pub whitelisted_tokens_for_aa: Vec
, + pub timestamp_asserter_params: Option, +} + +#[derive(Debug, Clone)] +pub struct TimestampAsserterParams { + pub address: Address, + pub min_time_till_end: Duration, } impl TxSenderConfig { @@ -242,6 +258,7 @@ impl TxSenderConfig { web3_json_config: &Web3JsonRpcConfig, fee_account_addr: Address, chain_id: L2ChainId, + timestamp_asserter_params: Option, ) -> Self { Self { fee_account_addr, @@ -253,6 +270,7 @@ impl TxSenderConfig { .validation_computational_gas_limit, chain_id, whitelisted_tokens_for_aa: web3_json_config.whitelisted_tokens_for_aa.clone(), + timestamp_asserter_params, } } } @@ -361,14 +379,15 @@ impl TxSender { if !execution_output.are_published_bytecodes_ok { return Err(SubmitTxError::FailedToPublishCompressedBytecodes); } - let mut stage_latency = SANDBOX_METRICS.start_tx_submit_stage(tx_hash, SubmitTxStage::DbInsert); self.ensure_tx_executable(&tx.clone().into(), &execution_output.metrics, true)?; + + let validation_traces = validation_result?; let submission_res_handle = self .0 .tx_sink - .submit_tx(&tx, execution_output.metrics) + .submit_tx(&tx, execution_output.metrics, validation_traces) .await?; match submission_res_handle { diff --git a/core/node/api_server/src/tx_sender/proxy.rs b/core/node/api_server/src/tx_sender/proxy.rs index 536a9767c1f..bba462404cf 100644 --- a/core/node/api_server/src/tx_sender/proxy.rs +++ b/core/node/api_server/src/tx_sender/proxy.rs @@ -11,7 +11,7 @@ use zksync_dal::{ helpers::wait_for_l1_batch, transactions_dal::L2TxSubmissionResult, Connection, ConnectionPool, Core, CoreDal, DalError, }; -use zksync_multivm::interface::TransactionExecutionMetrics; +use zksync_multivm::interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use zksync_shared_metrics::{TxStage, APP_METRICS}; use zksync_types::{api, l2::L2Tx, Address, Nonce, H256, U256}; use zksync_web3_decl::{ @@ -309,6 +309,7 @@ impl TxSink for TxProxy { &self, tx: &L2Tx, _execution_metrics: TransactionExecutionMetrics, + _validation_traces: ValidationTraces, ) -> Result { // We're running an external node: we have to proxy the transaction to the main node. // But before we do that, save the tx to cache in case someone will request it @@ -416,7 +417,11 @@ mod tests { let proxy = TxProxy::new(Box::new(main_node_client)); proxy - .submit_tx(&tx, TransactionExecutionMetrics::default()) + .submit_tx( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); assert!(send_tx_called.load(Ordering::Relaxed)); @@ -525,7 +530,11 @@ mod tests { let proxy = TxProxy::new(Box::new(main_node_client)); proxy - .submit_tx(&tx, TransactionExecutionMetrics::default()) + .submit_tx( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap_err(); @@ -585,7 +594,11 @@ mod tests { // Add transaction to the cache let proxy = TxProxy::new(Box::new(main_node_client)); proxy - .submit_tx(&tx, TransactionExecutionMetrics::default()) + .submit_tx( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); assert_eq!(proxy.tx_cache.get(tx.hash()).await.unwrap(), tx); @@ -662,15 +675,27 @@ mod tests { .build(); let proxy = TxProxy::new(Box::new(main_node_client)); proxy - .submit_tx(&tx, TransactionExecutionMetrics::default()) + .submit_tx( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); proxy - .submit_tx(&replacing_tx, TransactionExecutionMetrics::default()) + .submit_tx( + &replacing_tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); proxy - .submit_tx(&future_tx, TransactionExecutionMetrics::default()) + .submit_tx( + &future_tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); { diff --git a/core/node/api_server/src/tx_sender/result.rs b/core/node/api_server/src/tx_sender/result.rs index e2a51ae8e9a..cbc55a73c7c 100644 --- a/core/node/api_server/src/tx_sender/result.rs +++ b/core/node/api_server/src/tx_sender/result.rs @@ -67,6 +67,8 @@ pub enum SubmitTxError { /// Catch-all internal error (e.g., database error) that should not be exposed to the caller. #[error("internal error")] Internal(#[from] anyhow::Error), + #[error("transaction failed block.timestamp assertion")] + FailedBlockTimestampAssertion, } impl SubmitTxError { @@ -96,6 +98,7 @@ impl SubmitTxError { Self::MintedAmountOverflow => "minted-amount-overflow", Self::ProxyError(_) => "proxy-error", Self::Internal(_) => "internal", + Self::FailedBlockTimestampAssertion => "failed-block-timestamp-assertion", } } @@ -133,6 +136,9 @@ impl From for SubmitTxError { SandboxExecutionError::FailedToPayForTransaction(reason) => { Self::FailedToChargeFee(reason) } + SandboxExecutionError::FailedBlockTimestampAssertion => { + Self::FailedBlockTimestampAssertion + } } } } diff --git a/core/node/api_server/src/tx_sender/tests/mod.rs b/core/node/api_server/src/tx_sender/tests/mod.rs index ea3f77fbcd8..cbe405b2aa6 100644 --- a/core/node/api_server/src/tx_sender/tests/mod.rs +++ b/core/node/api_server/src/tx_sender/tests/mod.rs @@ -155,7 +155,7 @@ async fn create_real_tx_sender(pool: ConnectionPool) -> TxSender { executor_options.set_fast_vm_mode(FastVmMode::Shadow); let pg_caches = PostgresStorageCaches::new(1, 1); - let tx_executor = SandboxExecutor::real(executor_options, pg_caches, usize::MAX); + let tx_executor = SandboxExecutor::real(executor_options, pg_caches, usize::MAX, None); create_test_tx_sender(pool, genesis_params.config().l2_chain_id, tx_executor) .await .0 diff --git a/core/node/api_server/src/tx_sender/tests/send_tx.rs b/core/node/api_server/src/tx_sender/tests/send_tx.rs index fdd63254cf0..c861f04a832 100644 --- a/core/node/api_server/src/tx_sender/tests/send_tx.rs +++ b/core/node/api_server/src/tx_sender/tests/send_tx.rs @@ -1,8 +1,11 @@ //! Tests for sending raw transactions. +use std::ops::Range; + use assert_matches::assert_matches; +use chrono::NaiveDateTime; use test_casing::test_casing; -use zksync_multivm::interface::ExecutionResult; +use zksync_multivm::interface::{tracer::ValidationTraces, ExecutionResult}; use zksync_node_fee_model::MockBatchFeeParamsProvider; use zksync_node_test_utils::create_l2_transaction; use zksync_types::K256PrivateKey; @@ -54,6 +57,16 @@ async fn submitting_tx_requires_one_connection() { .await .unwrap() .expect("transaction is not persisted"); + + let storage_tx = storage + .transactions_dal() + .get_storage_tx_by_hash(tx_hash) + .await + .unwrap() + .expect("transaction is not persisted"); + // verify that no validation traces have been persisted + assert!(storage_tx.timestamp_asserter_range_start.is_none()); + assert!(storage_tx.timestamp_asserter_range_start.is_none()); } #[tokio::test] @@ -298,3 +311,88 @@ async fn sending_transaction_out_of_gas() { let (_, vm_result) = tx_sender.submit_tx(tx, block_args).await.unwrap(); assert_matches!(vm_result.result, ExecutionResult::Revert { .. }); } + +async fn submit_tx_with_validation_traces(actual_range: Range, expected_range: Range) { + // This test verifies that when a transaction produces ValidationTraces, + // range_start and range_end get persisted in the database + let pool = ConnectionPool::::constrained_test_pool(1).await; + let mut storage = pool.connection().await.unwrap(); + insert_genesis_batch(&mut storage, &GenesisParams::mock()) + .await + .unwrap(); + + let l2_chain_id = L2ChainId::default(); + let fee_input = MockBatchFeeParamsProvider::default() + .get_batch_fee_input_scaled(1.0, 1.0) + .await + .unwrap(); + let (base_fee, gas_per_pubdata) = + derive_base_fee_and_gas_per_pubdata(fee_input, ProtocolVersionId::latest().into()); + let tx = create_l2_transaction(base_fee, gas_per_pubdata); + let tx_hash = tx.hash(); + + // Manually set sufficient balance for the tx initiator. + StateBuilder::default() + .with_balance(tx.initiator_account(), u64::MAX.into()) + .apply(&mut storage) + .await; + drop(storage); + + let mut tx_executor = MockOneshotExecutor::default(); + tx_executor.set_tx_responses(move |received_tx, _| { + assert_eq!(received_tx.hash(), tx_hash); + ExecutionResult::Success { output: vec![] } + }); + tx_executor.set_tx_validation_traces_responses(move |tx, _| { + assert_eq!(tx.hash(), tx_hash); + ValidationTraces { + timestamp_asserter_range: Some(actual_range.clone()), + } + }); + + let tx_executor = SandboxExecutor::mock(tx_executor).await; + let (tx_sender, _) = create_test_tx_sender(pool.clone(), l2_chain_id, tx_executor).await; + let block_args = pending_block_args(&tx_sender).await; + + let submission_result = tx_sender.submit_tx(tx, block_args).await.unwrap(); + assert_matches!(submission_result.0, L2TxSubmissionResult::Added); + + let mut storage = pool.connection().await.unwrap(); + let storage_tx = storage + .transactions_dal() + .get_storage_tx_by_hash(tx_hash) + .await + .unwrap() + .expect("transaction is not persisted"); + assert_eq!( + expected_range.start, + storage_tx + .timestamp_asserter_range_start + .unwrap() + .and_utc() + .timestamp() + ); + assert_eq!( + expected_range.end, + storage_tx + .timestamp_asserter_range_end + .unwrap() + .and_utc() + .timestamp() + ); +} + +#[tokio::test] +async fn submitting_tx_with_validation_traces() { + // This test verifies that when a transaction produces ValidationTraces, + // range_start and range_end get persisted in the database + submit_tx_with_validation_traces(10..20, 10..20).await; +} + +#[tokio::test] +async fn submitting_tx_with_validation_traces_resulting_into_overflow() { + // This test verifies that the timestamp in ValidationTraces is capped at + // the maximum value supported by the NaiveDateTime type + submit_tx_with_validation_traces(10..u64::MAX, 10..NaiveDateTime::MAX.and_utc().timestamp()) + .await; +} diff --git a/core/node/api_server/src/tx_sender/tx_sink.rs b/core/node/api_server/src/tx_sender/tx_sink.rs index 3d764816fe0..1a6a7a733cc 100644 --- a/core/node/api_server/src/tx_sender/tx_sink.rs +++ b/core/node/api_server/src/tx_sender/tx_sink.rs @@ -1,5 +1,5 @@ use zksync_dal::{transactions_dal::L2TxSubmissionResult, Connection, Core}; -use zksync_multivm::interface::TransactionExecutionMetrics; +use zksync_multivm::interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use zksync_types::{ api::{Transaction, TransactionDetails, TransactionId}, l2::L2Tx, @@ -28,6 +28,7 @@ pub trait TxSink: std::fmt::Debug + Send + Sync + 'static { &self, tx: &L2Tx, execution_metrics: TransactionExecutionMetrics, + validation_traces: ValidationTraces, ) -> Result; /// Attempts to look up the pending nonce for the account in the sink-specific storage. diff --git a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs index 31c8f15bb1e..21f3f5ae49e 100644 --- a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs +++ b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/zks.rs @@ -58,6 +58,10 @@ impl ZksNamespaceServer for ZksNamespace { Ok(self.get_bridge_contracts_impl().await) } + async fn get_timestamp_asserter(&self) -> RpcResult> { + Ok(self.get_timestamp_asserter_impl()) + } + async fn l1_chain_id(&self) -> RpcResult { Ok(self.l1_chain_id_impl()) } diff --git a/core/node/api_server/src/web3/namespaces/zks.rs b/core/node/api_server/src/web3/namespaces/zks.rs index bcfd7daf346..1a4114bd2c6 100644 --- a/core/node/api_server/src/web3/namespaces/zks.rs +++ b/core/node/api_server/src/web3/namespaces/zks.rs @@ -151,6 +151,10 @@ impl ZksNamespace { self.state.bridge_addresses_handle.read().await } + pub fn get_timestamp_asserter_impl(&self) -> Option
{ + self.state.api_config.timestamp_asserter_address + } + pub fn l1_chain_id_impl(&self) -> U64 { U64::from(*self.state.api_config.l1_chain_id) } diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index a2aee8c7420..d43771811ee 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -115,6 +115,7 @@ pub struct InternalApiConfig { pub filters_disabled: bool, pub dummy_verifier: bool, pub l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, + pub timestamp_asserter_address: Option
, } impl InternalApiConfig { @@ -168,6 +169,7 @@ impl InternalApiConfig { filters_disabled: web3_config.filters_disabled, dummy_verifier: genesis_config.dummy_verifier, l1_batch_commit_data_generator_mode: genesis_config.l1_batch_commit_data_generator_mode, + timestamp_asserter_address: contracts_config.l2_timestamp_asserter_addr, } } } diff --git a/core/node/api_server/src/web3/testonly.rs b/core/node/api_server/src/web3/testonly.rs index 2d642b9a04b..540ea085711 100644 --- a/core/node/api_server/src/web3/testonly.rs +++ b/core/node/api_server/src/web3/testonly.rs @@ -34,6 +34,7 @@ pub(crate) async fn create_test_tx_sender( &web3_config, wallets.state_keeper.unwrap().fee_account.address(), l2_chain_id, + None, ); let storage_caches = PostgresStorageCaches::new(1, 1); diff --git a/core/node/api_server/src/web3/tests/mod.rs b/core/node/api_server/src/web3/tests/mod.rs index d8080f1dba5..17e92200d66 100644 --- a/core/node/api_server/src/web3/tests/mod.rs +++ b/core/node/api_server/src/web3/tests/mod.rs @@ -19,8 +19,8 @@ use zksync_config::{ use zksync_contracts::BaseSystemContracts; use zksync_dal::{transactions_dal::L2TxSubmissionResult, Connection, ConnectionPool, CoreDal}; use zksync_multivm::interface::{ - TransactionExecutionMetrics, TransactionExecutionResult, TxExecutionStatus, VmEvent, - VmExecutionMetrics, + tracer::ValidationTraces, TransactionExecutionMetrics, TransactionExecutionResult, + TxExecutionStatus, VmEvent, VmExecutionMetrics, }; use zksync_node_genesis::{insert_genesis_batch, mock_genesis_config, GenesisParams}; use zksync_node_test_utils::{ @@ -364,7 +364,11 @@ async fn store_custom_l2_block( let l2_tx = result.transaction.clone().try_into().unwrap(); let tx_submission_result = storage .transactions_dal() - .insert_transaction_l2(&l2_tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &l2_tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); assert_matches!(tx_submission_result, L2TxSubmissionResult::Added); @@ -771,7 +775,11 @@ impl HttpTest for TransactionCountTest { pending_tx.common_data.nonce = Nonce(2); storage .transactions_dal() - .insert_transaction_l2(&pending_tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &pending_tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); @@ -851,7 +859,11 @@ impl HttpTest for TransactionCountAfterSnapshotRecoveryTest { let mut storage = pool.connection().await?; storage .transactions_dal() - .insert_transaction_l2(&pending_tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &pending_tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); diff --git a/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs b/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs index 0922101e59d..599aba36f3e 100644 --- a/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs +++ b/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs @@ -220,10 +220,16 @@ impl BaseTokenL1Behaviour { if receipt.status == Some(1.into()) { return Ok(receipt.gas_used); } + let reason = (*l1_params.eth_client) + .as_ref() + .failure_reason(hash) + .await + .context("failed getting failure reason of `setTokenMultiplier` transaction")?; return Err(anyhow::Error::msg(format!( - "`setTokenMultiplier` transaction {:?} failed with status {:?}", + "`setTokenMultiplier` transaction {:?} failed with status {:?}, reason: {:?}", hex::encode(hash), - receipt.status + receipt.status, + reason ))); } else { tokio::time::sleep(sleep_duration).await; diff --git a/core/node/consensus/src/en.rs b/core/node/consensus/src/en.rs index 6e3619f57e2..ec8d3c19b54 100644 --- a/core/node/consensus/src/en.rs +++ b/core/node/consensus/src/en.rs @@ -35,14 +35,6 @@ pub(super) struct EN { impl EN { /// Task running a consensus node for the external node. /// It may be a validator, but it cannot be a leader (cannot propose blocks). - /// - /// If `enable_pregenesis` is false, - /// before starting the consensus node it fetches all the blocks - /// older than consensus genesis from the main node using json RPC. - /// NOTE: currently `enable_pregenesis` is hardcoded to `false` in `era.rs`. - /// True is used only in tests. Once the `block_metadata` RPC is enabled everywhere - /// this flag should be removed and fetching pregenesis blocks will always be done - /// over the gossip network. pub async fn run( self, ctx: &ctx::Ctx, @@ -50,7 +42,6 @@ impl EN { cfg: ConsensusConfig, secrets: ConsensusSecrets, build_version: Option, - enable_pregenesis: bool, ) -> anyhow::Result<()> { let attester = config::attester_key(&secrets).context("attester_key")?; @@ -74,24 +65,13 @@ impl EN { .await .wrap("try_update_global_config()")?; - let mut payload_queue = conn + let payload_queue = conn .new_payload_queue(ctx, actions, self.sync_state.clone()) .await .wrap("new_payload_queue()")?; drop(conn); - // Fetch blocks before the genesis. - if !enable_pregenesis { - self.fetch_blocks( - ctx, - &mut payload_queue, - Some(global_config.genesis.first_block), - ) - .await - .wrap("fetch_blocks()")?; - } - // Monitor the genesis of the main node. // If it changes, it means that a hard fork occurred and we need to reset the consensus state. s.spawn_bg::<()>({ diff --git a/core/node/consensus/src/era.rs b/core/node/consensus/src/era.rs index 916b7cdd89a..3150f839680 100644 --- a/core/node/consensus/src/era.rs +++ b/core/node/consensus/src/era.rs @@ -59,18 +59,8 @@ pub async fn run_external_node( is_validator = secrets.validator_key.is_some(), "running external node" ); - // We will enable it once the main node on all envs supports - // `block_metadata()` JSON RPC method. - let enable_pregenesis = false; - en.run( - ctx, - actions, - cfg, - secrets, - Some(build_version), - enable_pregenesis, - ) - .await + en.run(ctx, actions, cfg, secrets, Some(build_version)) + .await } None => { tracing::info!("running fetcher"); diff --git a/core/node/consensus/src/testonly.rs b/core/node/consensus/src/testonly.rs index db433665e57..ef4226c915f 100644 --- a/core/node/consensus/src/testonly.rs +++ b/core/node/consensus/src/testonly.rs @@ -73,7 +73,6 @@ pub(super) struct ConfigSet { net: network::Config, pub(super) config: config::ConsensusConfig, pub(super) secrets: config::ConsensusSecrets, - pub(super) enable_pregenesis: bool, } impl ConfigSet { @@ -83,17 +82,11 @@ impl ConfigSet { config: make_config(&net, None), secrets: make_secrets(&net, None), net, - enable_pregenesis: self.enable_pregenesis, } } } -pub(super) fn new_configs( - rng: &mut impl Rng, - setup: &Setup, - seed_peers: usize, - pregenesis: bool, -) -> Vec { +pub(super) fn new_configs(rng: &mut impl Rng, setup: &Setup, seed_peers: usize) -> Vec { let net_cfgs = network::testonly::new_configs(rng, setup, 0); let genesis_spec = config::GenesisSpec { chain_id: setup.genesis.chain_id.0.try_into().unwrap(), @@ -133,7 +126,6 @@ pub(super) fn new_configs( config: make_config(&net, Some(genesis_spec.clone())), secrets: make_secrets(&net, setup.attester_keys.get(i).cloned()), net, - enable_pregenesis: pregenesis, }) .collect() } @@ -473,7 +465,6 @@ impl StateKeeper { cfgs.config, cfgs.secrets, cfgs.net.build_version, - cfgs.enable_pregenesis, ) .await } diff --git a/core/node/consensus/src/tests/attestation.rs b/core/node/consensus/src/tests/attestation.rs index 2701a986e9e..5ee17d5e2ed 100644 --- a/core/node/consensus/src/tests/attestation.rs +++ b/core/node/consensus/src/tests/attestation.rs @@ -1,6 +1,6 @@ use anyhow::Context as _; use rand::Rng as _; -use test_casing::{test_casing, Product}; +use test_casing::test_casing; use tracing::Instrument as _; use zksync_concurrency::{ctx, error::Wrap, scope}; use zksync_consensus_roles::{ @@ -12,7 +12,7 @@ use zksync_test_account::Account; use zksync_types::ProtocolVersionId; use zksync_web3_decl::namespaces::EnNamespaceClient as _; -use super::{POLL_INTERVAL, PREGENESIS, VERSIONS}; +use super::{POLL_INTERVAL, VERSIONS}; use crate::{ mn::run_main_node, registry::{testonly, Registry}, @@ -126,9 +126,9 @@ async fn test_attestation_status_api(version: ProtocolVersionId) { // Test running a couple of attesters (which are also validators). // Main node is expected to collect all certificates. // External nodes are expected to just vote for the batch. -#[test_casing(4, Product((VERSIONS,PREGENESIS)))] +#[test_casing(2, VERSIONS)] #[tokio::test] -async fn test_multiple_attesters(version: ProtocolVersionId, pregenesis: bool) { +async fn test_multiple_attesters(version: ProtocolVersionId) { const NODES: usize = 4; zksync_concurrency::testonly::abort_on_panic(); @@ -137,7 +137,7 @@ async fn test_multiple_attesters(version: ProtocolVersionId, pregenesis: bool) { let account = &mut Account::random(); let to_fund = &[account.address]; let setup = Setup::new(rng, 4); - let mut cfgs = new_configs(rng, &setup, NODES, pregenesis); + let mut cfgs = new_configs(rng, &setup, NODES); scope::run!(ctx, |ctx, s| async { let validator_pool = ConnectionPool::test(false, version).await; let (mut validator, runner) = StateKeeper::new(ctx, validator_pool.clone()).await?; diff --git a/core/node/consensus/src/tests/mod.rs b/core/node/consensus/src/tests/mod.rs index 8da17cfba8a..663ccab4990 100644 --- a/core/node/consensus/src/tests/mod.rs +++ b/core/node/consensus/src/tests/mod.rs @@ -26,7 +26,6 @@ mod attestation; const VERSIONS: [ProtocolVersionId; 2] = [ProtocolVersionId::latest(), ProtocolVersionId::next()]; const FROM_SNAPSHOT: [bool; 2] = [true, false]; -const PREGENESIS: [bool; 2] = [true, false]; const POLL_INTERVAL: time::Duration = time::Duration::milliseconds(500); #[test_casing(2, VERSIONS)] @@ -190,14 +189,14 @@ async fn test_validator_block_store(version: ProtocolVersionId) { // In the current implementation, consensus certificates are created asynchronously // for the L2 blocks constructed by the StateKeeper. This means that consensus actor // is effectively just back filling the consensus certificates for the L2 blocks in storage. -#[test_casing(8, Product((FROM_SNAPSHOT,VERSIONS,PREGENESIS)))] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test] -async fn test_validator(from_snapshot: bool, version: ProtocolVersionId, pregenesis: bool) { +async fn test_validator(from_snapshot: bool, version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let account = &mut Account::random(); scope::run!(ctx, |ctx, s| async { @@ -254,14 +253,14 @@ async fn test_validator(from_snapshot: bool, version: ProtocolVersionId, pregene } // Test running a validator node and 2 full nodes recovered from different snapshots. -#[test_casing(4, Product((VERSIONS,PREGENESIS)))] +#[test_casing(2, VERSIONS)] #[tokio::test] -async fn test_nodes_from_various_snapshots(version: ProtocolVersionId, pregenesis: bool) { +async fn test_nodes_from_various_snapshots(version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let account = &mut Account::random(); scope::run!(ctx, |ctx, s| async { @@ -335,14 +334,14 @@ async fn test_nodes_from_various_snapshots(version: ProtocolVersionId, pregenesi .unwrap(); } -#[test_casing(8, Product((FROM_SNAPSHOT,VERSIONS,PREGENESIS)))] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test] -async fn test_config_change(from_snapshot: bool, version: ProtocolVersionId, pregenesis: bool) { +async fn test_config_change(from_snapshot: bool, version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let mut validator_cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let mut validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let node_cfg = validator_cfg.new_fullnode(rng); let account = &mut Account::random(); @@ -412,16 +411,16 @@ async fn test_config_change(from_snapshot: bool, version: ProtocolVersionId, pre // Test running a validator node and a couple of full nodes. // Validator is producing signed blocks and fetchers are expected to fetch // them directly or indirectly. -#[test_casing(8, Product((FROM_SNAPSHOT,VERSIONS,PREGENESIS)))] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test] -async fn test_full_nodes(from_snapshot: bool, version: ProtocolVersionId, pregenesis: bool) { +async fn test_full_nodes(from_snapshot: bool, version: ProtocolVersionId) { const NODES: usize = 2; zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let account = &mut Account::random(); // topology: @@ -500,16 +499,16 @@ async fn test_full_nodes(from_snapshot: bool, version: ProtocolVersionId, pregen } // Test running external node (non-leader) validators. -#[test_casing(8, Product((FROM_SNAPSHOT,VERSIONS,PREGENESIS)))] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test] -async fn test_en_validators(from_snapshot: bool, version: ProtocolVersionId, pregenesis: bool) { +async fn test_en_validators(from_snapshot: bool, version: ProtocolVersionId) { const NODES: usize = 3; zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, NODES); - let cfgs = testonly::new_configs(rng, &setup, 1, pregenesis); + let cfgs = testonly::new_configs(rng, &setup, 1); let account = &mut Account::random(); // Run all nodes in parallel. @@ -583,18 +582,14 @@ async fn test_en_validators(from_snapshot: bool, version: ProtocolVersionId, pre } // Test fetcher back filling missing certs. -#[test_casing(8, Product((FROM_SNAPSHOT,VERSIONS,PREGENESIS)))] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test] -async fn test_p2p_fetcher_backfill_certs( - from_snapshot: bool, - version: ProtocolVersionId, - pregenesis: bool, -) { +async fn test_p2p_fetcher_backfill_certs(from_snapshot: bool, version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let node_cfg = validator_cfg.new_fullnode(rng); let account = &mut Account::random(); @@ -668,16 +663,16 @@ async fn test_p2p_fetcher_backfill_certs( } // Test temporary fetcher fetching blocks if a lot of certs are missing. -#[test_casing(8, Product((FROM_SNAPSHOT,VERSIONS,PREGENESIS)))] +#[test_casing(4, Product((FROM_SNAPSHOT,VERSIONS)))] #[tokio::test] -async fn test_temporary_fetcher(from_snapshot: bool, version: ProtocolVersionId, pregenesis: bool) { +async fn test_temporary_fetcher(from_snapshot: bool, version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); // We force certs to be missing on EN by having 1 of the validators permanently offline. // This way no blocks will be finalized at all, so no one will have certs. let setup = Setup::new(rng, 2); - let validator_cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let node_cfg = validator_cfg.new_fullnode(rng); let account = &mut Account::random(); @@ -749,8 +744,7 @@ async fn test_temporary_fetcher_termination(from_snapshot: bool, version: Protoc let ctx = &ctx::test_root(&ctx::AffineClock::new(10.)); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let pregenesis = true; - let validator_cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let node_cfg = validator_cfg.new_fullnode(rng); let account = &mut Account::random(); @@ -797,14 +791,14 @@ async fn test_temporary_fetcher_termination(from_snapshot: bool, version: Protoc .unwrap(); } -#[test_casing(4, Product((VERSIONS,PREGENESIS)))] +#[test_casing(2, VERSIONS)] #[tokio::test] -async fn test_with_pruning(version: ProtocolVersionId, pregenesis: bool) { +async fn test_with_pruning(version: ProtocolVersionId) { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::RealClock); let rng = &mut ctx.rng(); let setup = Setup::new(rng, 1); - let validator_cfg = testonly::new_configs(rng, &setup, 0, pregenesis)[0].clone(); + let validator_cfg = testonly::new_configs(rng, &setup, 0)[0].clone(); let node_cfg = validator_cfg.new_fullnode(rng); let account = &mut Account::random(); diff --git a/core/node/da_clients/Cargo.toml b/core/node/da_clients/Cargo.toml index fa2f15920bd..e0c85b3030a 100644 --- a/core/node/da_clients/Cargo.toml +++ b/core/node/da_clients/Cargo.toml @@ -23,6 +23,7 @@ zksync_types.workspace = true zksync_object_store.workspace = true zksync_da_client.workspace = true zksync_env_config.workspace = true +zksync_basic_types.workspace = true futures.workspace = true # Avail dependencies @@ -33,10 +34,24 @@ base58.workspace = true serde_json.workspace = true hex.workspace = true blake2b_simd.workspace = true - -jsonrpsee = { workspace = true, features = ["ws-client"] } parity-scale-codec = { workspace = true, features = ["derive"] } subxt-signer = { workspace = true, features = ["sr25519", "native"] } +jsonrpsee = { workspace = true, features = ["ws-client"] } reqwest = { workspace = true } bytes = { workspace = true } backon.workspace = true + +# Celestia dependencies +http.workspace = true +bincode.workspace = true +celestia-types.workspace = true +secp256k1.workspace = true +sha2.workspace = true +prost.workspace = true +bech32.workspace = true +ripemd.workspace = true +tonic = { workspace = true, features = ["tls-roots", "prost", "codegen"] } +pbjson-types.workspace = true + +# Eigen dependencies +tokio-stream.workspace = true diff --git a/core/node/da_clients/README.md b/core/node/da_clients/README.md index df06cef2419..1b22e5198a6 100644 --- a/core/node/da_clients/README.md +++ b/core/node/da_clients/README.md @@ -8,3 +8,5 @@ Currently, the following DataAvailability clients are implemented: utilizing the DA framework. - `Object Store client` that stores the pubdata in the Object Store(GCS). - `Avail` that sends the pubdata to the Avail DA layer. +- `Celestia` that sends the pubdata to the Celestia DA layer. +- `Eigen` that sends the pubdata to the Eigen DA layer. diff --git a/core/node/da_clients/src/avail/client.rs b/core/node/da_clients/src/avail/client.rs index 46d652d5713..c0ead429d91 100644 --- a/core/node/da_clients/src/avail/client.rs +++ b/core/node/da_clients/src/avail/client.rs @@ -16,7 +16,10 @@ use zksync_types::{ H256, U256, }; -use crate::avail::sdk::{GasRelayClient, RawAvailClient}; +use crate::{ + avail::sdk::{GasRelayClient, RawAvailClient}, + utils::{to_non_retriable_da_error, to_retriable_da_error}, +}; #[derive(Debug, Clone)] enum AvailClientMode { @@ -192,7 +195,7 @@ impl DataAvailabilityClient for AvailClient { let response = self .api_client .get(&url) - .timeout(Duration::from_secs(self.config.timeout as u64)) + .timeout(Duration::from_millis(self.config.timeout_ms as u64)) .send() .await .map_err(to_retriable_da_error)?; @@ -225,17 +228,3 @@ impl DataAvailabilityClient for AvailClient { Some(RawAvailClient::MAX_BLOB_SIZE) } } - -pub fn to_non_retriable_da_error(error: impl Into) -> DAError { - DAError { - error: error.into(), - is_retriable: false, - } -} - -pub fn to_retriable_da_error(error: impl Into) -> DAError { - DAError { - error: error.into(), - is_retriable: true, - } -} diff --git a/core/node/da_clients/src/avail/sdk.rs b/core/node/da_clients/src/avail/sdk.rs index f693280ba4a..19309dc3cbf 100644 --- a/core/node/da_clients/src/avail/sdk.rs +++ b/core/node/da_clients/src/avail/sdk.rs @@ -18,7 +18,7 @@ use subxt_signer::{ }; use zksync_types::H256; -use crate::avail::client::to_non_retriable_da_error; +use crate::utils::to_non_retriable_da_error; const PROTOCOL_VERSION: u8 = 4; diff --git a/core/node/da_clients/src/celestia/README.md b/core/node/da_clients/src/celestia/README.md new file mode 100644 index 00000000000..a3142a7d761 --- /dev/null +++ b/core/node/da_clients/src/celestia/README.md @@ -0,0 +1,19 @@ +# Celestia client + +--- + +This is an implementation of the Celestia client capable of sending the blobs to DA layer. Normally, the light client is +required to send the blobs to Celestia, but this implementation is capable of sending the blobs to DA layer directly. + +This is a simplified and adapted version of astria's code, look +[here](https://github.com/astriaorg/astria/tree/main/crates/astria-sequencer-relayer) for original implementation. + +The generated files are copied from +[here](https://github.com/astriaorg/astria/tree/main/crates/astria-core/src/generated), which is not perfect, but allows +us to use them without adding the proto files and the infrastructure to generate the `.rs`. + +While moving the files, the `#[cfg(feature = "client")]` annotations were removed for simplicity, so client code is +available by default. + +If there is a need to generate the files from the proto files, the `tools/protobuf-compiler` from astria's repo can be +used. diff --git a/core/node/da_clients/src/celestia/client.rs b/core/node/da_clients/src/celestia/client.rs new file mode 100644 index 00000000000..df0735d4e1e --- /dev/null +++ b/core/node/da_clients/src/celestia/client.rs @@ -0,0 +1,109 @@ +use std::{ + fmt::{Debug, Formatter}, + str::FromStr, + sync::Arc, + time, +}; + +use async_trait::async_trait; +use celestia_types::{blob::Commitment, nmt::Namespace, Blob}; +use serde::{Deserialize, Serialize}; +use subxt_signer::ExposeSecret; +use tonic::transport::Endpoint; +use zksync_config::configs::da_client::celestia::{CelestiaConfig, CelestiaSecrets}; +use zksync_da_client::{ + types::{DAError, DispatchResponse, InclusionData}, + DataAvailabilityClient, +}; + +use crate::{ + celestia::sdk::{BlobTxHash, RawCelestiaClient}, + utils::to_non_retriable_da_error, +}; + +/// An implementation of the `DataAvailabilityClient` trait that interacts with the Avail network. +#[derive(Clone)] +pub struct CelestiaClient { + config: CelestiaConfig, + client: Arc, +} + +impl CelestiaClient { + pub async fn new(config: CelestiaConfig, secrets: CelestiaSecrets) -> anyhow::Result { + let grpc_channel = Endpoint::from_str(config.api_node_url.clone().as_str())? + .timeout(time::Duration::from_millis(config.timeout_ms)) + .connect() + .await?; + + let private_key = secrets.private_key.0.expose_secret().to_string(); + let client = RawCelestiaClient::new(grpc_channel, private_key, config.chain_id.clone()) + .expect("could not create Celestia client"); + + Ok(Self { + config, + client: Arc::new(client), + }) + } +} +#[derive(Serialize, Deserialize)] +pub struct BlobId { + pub commitment: Commitment, + pub height: u64, +} + +#[async_trait] +impl DataAvailabilityClient for CelestiaClient { + async fn dispatch_blob( + &self, + _: u32, // batch number + data: Vec, + ) -> Result { + let namespace_bytes = + hex::decode(&self.config.namespace).map_err(to_non_retriable_da_error)?; + let namespace = + Namespace::new_v0(namespace_bytes.as_slice()).map_err(to_non_retriable_da_error)?; + let blob = Blob::new(namespace, data).map_err(to_non_retriable_da_error)?; + + let commitment = blob.commitment; + let blob_tx = self + .client + .prepare(vec![blob]) + .await + .map_err(to_non_retriable_da_error)?; + + let blob_tx_hash = BlobTxHash::compute(&blob_tx); + let height = self + .client + .submit(blob_tx_hash, blob_tx) + .await + .map_err(to_non_retriable_da_error)?; + + let blob_id = BlobId { commitment, height }; + let blob_bytes = bincode::serialize(&blob_id).map_err(to_non_retriable_da_error)?; + + Ok(DispatchResponse { + blob_id: hex::encode(&blob_bytes), + }) + } + + async fn get_inclusion_data(&self, _: &str) -> Result, DAError> { + Ok(Some(InclusionData { data: vec![] })) + } + + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + + fn blob_size_limit(&self) -> Option { + Some(1973786) // almost 2MB + } +} + +impl Debug for CelestiaClient { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CelestiaClient") + .field("config.api_node_url", &self.config.api_node_url) + .field("config.namespace", &self.config.namespace) + .finish() + } +} diff --git a/core/node/da_clients/src/celestia/generated/celestia.blob.v1.rs b/core/node/da_clients/src/celestia/generated/celestia.blob.v1.rs new file mode 100644 index 00000000000..ee6ed85655e --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/celestia.blob.v1.rs @@ -0,0 +1,200 @@ +// This file is @generated by prost-build. +/// Params defines the parameters for the module. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Params { + #[prost(uint32, tag = "1")] + pub gas_per_blob_byte: u32, + #[prost(uint64, tag = "2")] + pub gov_max_square_size: u64, +} +impl ::prost::Name for Params { + const NAME: &'static str = "Params"; + const PACKAGE: &'static str = "celestia.blob.v1"; + fn full_name() -> ::prost::alloc::string::String { + "celestia.blob.v1.Params".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/celestia.blob.v1.Params".into() + } +} +/// QueryParamsRequest is the request type for the Query/Params RPC method. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct QueryParamsRequest {} +impl ::prost::Name for QueryParamsRequest { + const NAME: &'static str = "QueryParamsRequest"; + const PACKAGE: &'static str = "celestia.blob.v1"; + fn full_name() -> ::prost::alloc::string::String { + "celestia.blob.v1.QueryParamsRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/celestia.blob.v1.QueryParamsRequest".into() + } +} +/// QueryParamsResponse is the response type for the Query/Params RPC method. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct QueryParamsResponse { + #[prost(message, optional, tag = "1")] + pub params: ::core::option::Option, +} +impl ::prost::Name for QueryParamsResponse { + const NAME: &'static str = "QueryParamsResponse"; + const PACKAGE: &'static str = "celestia.blob.v1"; + fn full_name() -> ::prost::alloc::string::String { + "celestia.blob.v1.QueryParamsResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/celestia.blob.v1.QueryParamsResponse".into() + } +} +/// Generated client implementations. +pub mod query_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Query defines the gRPC query service. + #[derive(Debug, Clone)] + pub struct QueryClient { + inner: tonic::client::Grpc, + } + impl QueryClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl QueryClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> QueryClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + QueryClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Params queries the parameters of the module. + pub async fn params( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/celestia.blob.v1.Query/Params", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("celestia.blob.v1.Query", "Params")); + self.inner.unary(req, path, codec).await + } + } +} + +/// MsgPayForBlobs pays for the inclusion of a blob in the block. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgPayForBlobs { + #[prost(string, tag = "1")] + pub signer: ::prost::alloc::string::String, + /// namespaces is a list of namespaces that the blobs are associated with. A + /// namespace is a byte slice of length 29 where the first byte is the + /// namespaceVersion and the subsequent 28 bytes are the namespaceId. + #[prost(bytes = "bytes", repeated, tag = "2")] + pub namespaces: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, + #[prost(uint32, repeated, tag = "3")] + pub blob_sizes: ::prost::alloc::vec::Vec, + /// share_commitments is a list of share commitments (one per blob). + #[prost(bytes = "bytes", repeated, tag = "4")] + pub share_commitments: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, + /// share_versions are the versions of the share format that the blobs + /// associated with this message should use when included in a block. The + /// share_versions specified must match the share_versions used to generate the + /// share_commitment in this message. + #[prost(uint32, repeated, tag = "8")] + pub share_versions: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for MsgPayForBlobs { + const NAME: &'static str = "MsgPayForBlobs"; + const PACKAGE: &'static str = "celestia.blob.v1"; + fn full_name() -> ::prost::alloc::string::String { + "celestia.blob.v1.MsgPayForBlobs".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/celestia.blob.v1.MsgPayForBlobs".into() + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.auth.v1beta1.rs b/core/node/da_clients/src/celestia/generated/cosmos.auth.v1beta1.rs new file mode 100644 index 00000000000..98314985a8e --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.auth.v1beta1.rs @@ -0,0 +1,257 @@ +// This file is @generated by prost-build. +/// BaseAccount defines a base account type. It contains all the necessary fields +/// for basic account functionality. Any custom account type should extend this +/// type for additional functionality (e.g. vesting). +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BaseAccount { + #[prost(string, tag = "1")] + pub address: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub pub_key: ::core::option::Option<::pbjson_types::Any>, + #[prost(uint64, tag = "3")] + pub account_number: u64, + #[prost(uint64, tag = "4")] + pub sequence: u64, +} +impl ::prost::Name for BaseAccount { + const NAME: &'static str = "BaseAccount"; + const PACKAGE: &'static str = "cosmos.auth.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.auth.v1beta1.BaseAccount".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.auth.v1beta1.BaseAccount".into() + } +} +/// Params defines the parameters for the auth module. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Params { + #[prost(uint64, tag = "1")] + pub max_memo_characters: u64, + #[prost(uint64, tag = "2")] + pub tx_sig_limit: u64, + #[prost(uint64, tag = "3")] + pub tx_size_cost_per_byte: u64, + #[prost(uint64, tag = "4")] + pub sig_verify_cost_ed25519: u64, + #[prost(uint64, tag = "5")] + pub sig_verify_cost_secp256k1: u64, +} +impl ::prost::Name for Params { + const NAME: &'static str = "Params"; + const PACKAGE: &'static str = "cosmos.auth.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.auth.v1beta1.Params".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.auth.v1beta1.Params".into() + } +} +/// QueryAccountRequest is the request type for the Query/Account RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryAccountRequest { + /// address defines the address to query for. + #[prost(string, tag = "1")] + pub address: ::prost::alloc::string::String, +} +impl ::prost::Name for QueryAccountRequest { + const NAME: &'static str = "QueryAccountRequest"; + const PACKAGE: &'static str = "cosmos.auth.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.auth.v1beta1.QueryAccountRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.auth.v1beta1.QueryAccountRequest".into() + } +} +/// QueryAccountResponse is the response type for the Query/Account RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct QueryAccountResponse { + /// account defines the account of the corresponding address. + #[prost(message, optional, tag = "1")] + pub account: ::core::option::Option<::pbjson_types::Any>, +} +impl ::prost::Name for QueryAccountResponse { + const NAME: &'static str = "QueryAccountResponse"; + const PACKAGE: &'static str = "cosmos.auth.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.auth.v1beta1.QueryAccountResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.auth.v1beta1.QueryAccountResponse".into() + } +} +/// QueryParamsRequest is the request type for the Query/Params RPC method. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct QueryParamsRequest {} +impl ::prost::Name for QueryParamsRequest { + const NAME: &'static str = "QueryParamsRequest"; + const PACKAGE: &'static str = "cosmos.auth.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.auth.v1beta1.QueryParamsRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.auth.v1beta1.QueryParamsRequest".into() + } +} +/// QueryParamsResponse is the response type for the Query/Params RPC method. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct QueryParamsResponse { + /// params defines the parameters of the module. + #[prost(message, optional, tag = "1")] + pub params: ::core::option::Option, +} +impl ::prost::Name for QueryParamsResponse { + const NAME: &'static str = "QueryParamsResponse"; + const PACKAGE: &'static str = "cosmos.auth.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.auth.v1beta1.QueryParamsResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.auth.v1beta1.QueryParamsResponse".into() + } +} +/// Generated client implementations. +pub mod query_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Query defines the gRPC querier service. + #[derive(Debug, Clone)] + pub struct QueryClient { + inner: tonic::client::Grpc, + } + impl QueryClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl QueryClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> QueryClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + QueryClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Account returns account details based on address. + pub async fn account( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/cosmos.auth.v1beta1.Query/Account", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("cosmos.auth.v1beta1.Query", "Account")); + self.inner.unary(req, path, codec).await + } + /// Params queries all parameters. + pub async fn params( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/cosmos.auth.v1beta1.Query/Params", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("cosmos.auth.v1beta1.Query", "Params")); + self.inner.unary(req, path, codec).await + } + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.base.abci.v1beta1.rs b/core/node/da_clients/src/celestia/generated/cosmos.base.abci.v1beta1.rs new file mode 100644 index 00000000000..6b0f9fc1956 --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.base.abci.v1beta1.rs @@ -0,0 +1,125 @@ +// This file is @generated by prost-build. +/// TxResponse defines a structure containing relevant tx data and metadata. The +/// tags are stringified and the log is JSON decoded. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TxResponse { + /// The block height + #[prost(int64, tag = "1")] + pub height: i64, + /// The transaction hash. + #[prost(string, tag = "2")] + pub txhash: ::prost::alloc::string::String, + /// Namespace for the Code + #[prost(string, tag = "3")] + pub codespace: ::prost::alloc::string::String, + /// Response code. + #[prost(uint32, tag = "4")] + pub code: u32, + /// Result bytes, if any. + #[prost(string, tag = "5")] + pub data: ::prost::alloc::string::String, + /// The output of the application's logger (raw string). May be + /// non-deterministic. + #[prost(string, tag = "6")] + pub raw_log: ::prost::alloc::string::String, + /// The output of the application's logger (typed). May be non-deterministic. + #[prost(message, repeated, tag = "7")] + pub logs: ::prost::alloc::vec::Vec, + /// Additional information. May be non-deterministic. + #[prost(string, tag = "8")] + pub info: ::prost::alloc::string::String, + /// Amount of gas requested for transaction. + #[prost(int64, tag = "9")] + pub gas_wanted: i64, + /// Amount of gas consumed by transaction. + #[prost(int64, tag = "10")] + pub gas_used: i64, + /// The request transaction bytes. + #[prost(message, optional, tag = "11")] + pub tx: ::core::option::Option<::pbjson_types::Any>, + /// Time of the previous block. For heights > 1, it's the weighted median of + /// the timestamps of the valid votes in the block.LastCommit. For height == 1, + /// it's genesis time. + #[prost(string, tag = "12")] + pub timestamp: ::prost::alloc::string::String, + /// Events defines all the events emitted by processing a transaction. Note, + /// these events include those emitted by processing all the messages and those + /// emitted from the ante. Whereas Logs contains the events, with + /// additional metadata, emitted only by processing the messages. + /// + /// Since: cosmos-sdk 0.42.11, 0.44.5, 0.45 + #[prost(message, repeated, tag = "13")] + pub events: ::prost::alloc::vec::Vec< + super::super::super::tendermint::abci::Event, + >, +} +impl ::prost::Name for TxResponse { + const NAME: &'static str = "TxResponse"; + const PACKAGE: &'static str = "cosmos.base.abci.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.base.abci.v1beta1.TxResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.base.abci.v1beta1.TxResponse".into() + } +} +/// ABCIMessageLog defines a structure containing an indexed tx ABCI message log. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AbciMessageLog { + #[prost(uint32, tag = "1")] + pub msg_index: u32, + #[prost(string, tag = "2")] + pub log: ::prost::alloc::string::String, + /// Events contains a slice of Event objects that were emitted during some + /// execution. + #[prost(message, repeated, tag = "3")] + pub events: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for AbciMessageLog { + const NAME: &'static str = "ABCIMessageLog"; + const PACKAGE: &'static str = "cosmos.base.abci.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.base.abci.v1beta1.ABCIMessageLog".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.base.abci.v1beta1.ABCIMessageLog".into() + } +} +/// StringEvent defines en Event object wrapper where all the attributes +/// contain key/value pairs that are strings instead of raw bytes. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StringEvent { + #[prost(string, tag = "1")] + pub r#type: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub attributes: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for StringEvent { + const NAME: &'static str = "StringEvent"; + const PACKAGE: &'static str = "cosmos.base.abci.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.base.abci.v1beta1.StringEvent".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.base.abci.v1beta1.StringEvent".into() + } +} +/// Attribute defines an attribute wrapper where the key and value are +/// strings instead of raw bytes. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Attribute { + #[prost(string, tag = "1")] + pub key: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub value: ::prost::alloc::string::String, +} +impl ::prost::Name for Attribute { + const NAME: &'static str = "Attribute"; + const PACKAGE: &'static str = "cosmos.base.abci.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.base.abci.v1beta1.Attribute".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.base.abci.v1beta1.Attribute".into() + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.base.node.v1beta1.rs b/core/node/da_clients/src/celestia/generated/cosmos.base.node.v1beta1.rs new file mode 100644 index 00000000000..89bb519bd81 --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.base.node.v1beta1.rs @@ -0,0 +1,146 @@ +// This file is @generated by prost-build. +/// ConfigRequest defines the request structure for the Config gRPC query. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct ConfigRequest {} +impl ::prost::Name for ConfigRequest { + const NAME: &'static str = "ConfigRequest"; + const PACKAGE: &'static str = "cosmos.base.node.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.base.node.v1beta1.ConfigRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.base.node.v1beta1.ConfigRequest".into() + } +} +/// ConfigResponse defines the response structure for the Config gRPC query. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConfigResponse { + #[prost(string, tag = "1")] + pub minimum_gas_price: ::prost::alloc::string::String, +} +impl ::prost::Name for ConfigResponse { + const NAME: &'static str = "ConfigResponse"; + const PACKAGE: &'static str = "cosmos.base.node.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.base.node.v1beta1.ConfigResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.base.node.v1beta1.ConfigResponse".into() + } +} +/// Generated client implementations. +pub mod service_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Service defines the gRPC querier service for node related queries. + #[derive(Debug, Clone)] + pub struct ServiceClient { + inner: tonic::client::Grpc, + } + impl ServiceClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ServiceClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ServiceClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + ServiceClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Config queries for the operator configuration. + pub async fn config( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/cosmos.base.node.v1beta1.Service/Config", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("cosmos.base.node.v1beta1.Service", "Config")); + self.inner.unary(req, path, codec).await + } + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.base.v1beta1.rs b/core/node/da_clients/src/celestia/generated/cosmos.base.v1beta1.rs new file mode 100644 index 00000000000..d13fb784d97 --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.base.v1beta1.rs @@ -0,0 +1,19 @@ +// This file is @generated by prost-build. +/// Coin defines a token with a denomination and an amount. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Coin { + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub amount: ::prost::alloc::string::String, +} +impl ::prost::Name for Coin { + const NAME: &'static str = "Coin"; + const PACKAGE: &'static str = "cosmos.base.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.base.v1beta1.Coin".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.base.v1beta1.Coin".into() + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.crypto.multisig.v1beta1.rs b/core/node/da_clients/src/celestia/generated/cosmos.crypto.multisig.v1beta1.rs new file mode 100644 index 00000000000..c514b3739b2 --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.crypto.multisig.v1beta1.rs @@ -0,0 +1,40 @@ +// This file is @generated by prost-build. +/// MultiSignature wraps the signatures from a multisig.LegacyAminoPubKey. +/// See cosmos.tx.v1betata1.ModeInfo.Multi for how to specify which signers +/// signed and with which modes. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MultiSignature { + #[prost(bytes = "bytes", repeated, tag = "1")] + pub signatures: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, +} +impl ::prost::Name for MultiSignature { + const NAME: &'static str = "MultiSignature"; + const PACKAGE: &'static str = "cosmos.crypto.multisig.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.crypto.multisig.v1beta1.MultiSignature".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.crypto.multisig.v1beta1.MultiSignature".into() + } +} +/// CompactBitArray is an implementation of a space efficient bit array. +/// This is used to ensure that the encoded data takes up a minimal amount of +/// space after proto encoding. +/// This is not thread safe, and is not intended for concurrent usage. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CompactBitArray { + #[prost(uint32, tag = "1")] + pub extra_bits_stored: u32, + #[prost(bytes = "bytes", tag = "2")] + pub elems: ::prost::bytes::Bytes, +} +impl ::prost::Name for CompactBitArray { + const NAME: &'static str = "CompactBitArray"; + const PACKAGE: &'static str = "cosmos.crypto.multisig.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.crypto.multisig.v1beta1.CompactBitArray".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.crypto.multisig.v1beta1.CompactBitArray".into() + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.crypto.secp256k1.rs b/core/node/da_clients/src/celestia/generated/cosmos.crypto.secp256k1.rs new file mode 100644 index 00000000000..081aec09682 --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.crypto.secp256k1.rs @@ -0,0 +1,21 @@ +// This file is @generated by prost-build. +/// PubKey defines a secp256k1 public key +/// Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte +/// if the y-coordinate is the lexicographically largest of the two associated with +/// the x-coordinate. Otherwise the first byte is a 0x03. +/// This prefix is followed with the x-coordinate. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PubKey { + #[prost(bytes = "bytes", tag = "1")] + pub key: ::prost::bytes::Bytes, +} +impl ::prost::Name for PubKey { + const NAME: &'static str = "PubKey"; + const PACKAGE: &'static str = "cosmos.crypto.secp256k1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.crypto.secp256k1.PubKey".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.crypto.secp256k1.PubKey".into() + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.tx.signing.v1beta1.rs b/core/node/da_clients/src/celestia/generated/cosmos.tx.signing.v1beta1.rs new file mode 100644 index 00000000000..54f3fa9d00d --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.tx.signing.v1beta1.rs @@ -0,0 +1,72 @@ +// This file is @generated by prost-build. +/// SignMode represents a signing mode with its own security guarantees. +/// +/// This enum should be considered a registry of all known sign modes +/// in the Cosmos ecosystem. Apps are not expected to support all known +/// sign modes. Apps that would like to support custom sign modes are +/// encouraged to open a small PR against this file to add a new case +/// to this SignMode enum describing their sign mode so that different +/// apps have a consistent version of this enum. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SignMode { + /// SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + /// rejected. + Unspecified = 0, + /// SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + /// verified with raw bytes from Tx. + Direct = 1, + /// SIGN_MODE_TEXTUAL is a future signing mode that will verify some + /// human-readable textual representation on top of the binary representation + /// from SIGN_MODE_DIRECT. It is currently not supported. + Textual = 2, + /// SIGN_MODE_DIRECT_AUX specifies a signing mode which uses + /// SignDocDirectAux. As opposed to SIGN_MODE_DIRECT, this sign mode does not + /// require signers signing over other signers' `signer_info`. It also allows + /// for adding Tips in transactions. + /// + /// Since: cosmos-sdk 0.46 + DirectAux = 3, + /// SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + /// Amino JSON and will be removed in the future. + LegacyAminoJson = 127, + /// SIGN_MODE_EIP_191 specifies the sign mode for EIP 191 signing on the Cosmos + /// SDK. Ref: + /// + /// Currently, SIGN_MODE_EIP_191 is registered as a SignMode enum variant, + /// but is not implemented on the SDK by default. To enable EIP-191, you need + /// to pass a custom `TxConfig` that has an implementation of + /// `SignModeHandler` for EIP-191. The SDK may decide to fully support + /// EIP-191 in the future. + /// + /// Since: cosmos-sdk 0.45.2 + Eip191 = 191, +} +impl SignMode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "SIGN_MODE_UNSPECIFIED", + Self::Direct => "SIGN_MODE_DIRECT", + Self::Textual => "SIGN_MODE_TEXTUAL", + Self::DirectAux => "SIGN_MODE_DIRECT_AUX", + Self::LegacyAminoJson => "SIGN_MODE_LEGACY_AMINO_JSON", + Self::Eip191 => "SIGN_MODE_EIP_191", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SIGN_MODE_UNSPECIFIED" => Some(Self::Unspecified), + "SIGN_MODE_DIRECT" => Some(Self::Direct), + "SIGN_MODE_TEXTUAL" => Some(Self::Textual), + "SIGN_MODE_DIRECT_AUX" => Some(Self::DirectAux), + "SIGN_MODE_LEGACY_AMINO_JSON" => Some(Self::LegacyAminoJson), + "SIGN_MODE_EIP_191" => Some(Self::Eip191), + _ => None, + } + } +} diff --git a/core/node/da_clients/src/celestia/generated/cosmos.tx.v1beta1.rs b/core/node/da_clients/src/celestia/generated/cosmos.tx.v1beta1.rs new file mode 100644 index 00000000000..7783eabcdba --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/cosmos.tx.v1beta1.rs @@ -0,0 +1,553 @@ +// This file is @generated by prost-build. +/// Tx is the standard type used for broadcasting transactions. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Tx { + /// body is the processable content of the transaction + #[prost(message, optional, tag = "1")] + pub body: ::core::option::Option, + /// auth_info is the authorization related content of the transaction, + /// specifically signers, signer modes and fee + #[prost(message, optional, tag = "2")] + pub auth_info: ::core::option::Option, + /// signatures is a list of signatures that matches the length and order of + /// AuthInfo's signer_infos to allow connecting signature meta information like + /// public key and signing mode by position. + #[prost(bytes = "bytes", repeated, tag = "3")] + pub signatures: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, +} +impl ::prost::Name for Tx { + const NAME: &'static str = "Tx"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.Tx".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.Tx".into() + } +} +/// SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SignDoc { + /// body_bytes is protobuf serialization of a TxBody that matches the + /// representation in TxRaw. + #[prost(bytes = "bytes", tag = "1")] + pub body_bytes: ::prost::bytes::Bytes, + /// auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + /// representation in TxRaw. + #[prost(bytes = "bytes", tag = "2")] + pub auth_info_bytes: ::prost::bytes::Bytes, + /// chain_id is the unique identifier of the chain this transaction targets. + /// It prevents signed transactions from being used on another chain by an + /// attacker + #[prost(string, tag = "3")] + pub chain_id: ::prost::alloc::string::String, + /// account_number is the account number of the account in state + #[prost(uint64, tag = "4")] + pub account_number: u64, +} +impl ::prost::Name for SignDoc { + const NAME: &'static str = "SignDoc"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.SignDoc".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.SignDoc".into() + } +} +/// TxBody is the body of a transaction that all signers sign over. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TxBody { + /// messages is a list of messages to be executed. The required signers of + /// those messages define the number and order of elements in AuthInfo's + /// signer_infos and Tx's signatures. Each required signer address is added to + /// the list only the first time it occurs. + /// By convention, the first required signer (usually from the first message) + /// is referred to as the primary signer and pays the fee for the whole + /// transaction. + #[prost(message, repeated, tag = "1")] + pub messages: ::prost::alloc::vec::Vec<::pbjson_types::Any>, + /// memo is any arbitrary note/comment to be added to the transaction. + /// WARNING: in clients, any publicly exposed text should not be called memo, + /// but should be called `note` instead (see ). + #[prost(string, tag = "2")] + pub memo: ::prost::alloc::string::String, + /// timeout is the block height after which this transaction will not + /// be processed by the chain + #[prost(uint64, tag = "3")] + pub timeout_height: u64, + /// extension_options are arbitrary options that can be added by chains + /// when the default options are not sufficient. If any of these are present + /// and can't be handled, the transaction will be rejected + #[prost(message, repeated, tag = "1023")] + pub extension_options: ::prost::alloc::vec::Vec<::pbjson_types::Any>, + /// extension_options are arbitrary options that can be added by chains + /// when the default options are not sufficient. If any of these are present + /// and can't be handled, they will be ignored + #[prost(message, repeated, tag = "2047")] + pub non_critical_extension_options: ::prost::alloc::vec::Vec<::pbjson_types::Any>, +} +impl ::prost::Name for TxBody { + const NAME: &'static str = "TxBody"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.TxBody".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.TxBody".into() + } +} +/// AuthInfo describes the fee and signer modes that are used to sign a +/// transaction. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuthInfo { + /// signer_infos defines the signing modes for the required signers. The number + /// and order of elements must match the required signers from TxBody's + /// messages. The first element is the primary signer and the one which pays + /// the fee. + #[prost(message, repeated, tag = "1")] + pub signer_infos: ::prost::alloc::vec::Vec, + /// Fee is the fee and gas limit for the transaction. The first signer is the + /// primary signer and the one which pays the fee. The fee can be calculated + /// based on the cost of evaluating the body and doing signature verification + /// of the signers. This can be estimated via simulation. + #[prost(message, optional, tag = "2")] + pub fee: ::core::option::Option, + /// Tip is the optional tip used for transactions fees paid in another denom. + /// + /// This field is ignored if the chain didn't enable tips, i.e. didn't add the + /// `TipDecorator` in its posthandler. + /// + /// Since: cosmos-sdk 0.46 + #[prost(message, optional, tag = "3")] + pub tip: ::core::option::Option, +} +impl ::prost::Name for AuthInfo { + const NAME: &'static str = "AuthInfo"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.AuthInfo".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.AuthInfo".into() + } +} +/// SignerInfo describes the public key and signing mode of a single top-level +/// signer. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SignerInfo { + /// public_key is the public key of the signer. It is optional for accounts + /// that already exist in state. If unset, the verifier can use the required \ + /// signer address for this position and lookup the public key. + #[prost(message, optional, tag = "1")] + pub public_key: ::core::option::Option<::pbjson_types::Any>, + /// mode_info describes the signing mode of the signer and is a nested + /// structure to support nested multisig pubkey's + #[prost(message, optional, tag = "2")] + pub mode_info: ::core::option::Option, + /// sequence is the sequence of the account, which describes the + /// number of committed transactions signed by a given address. It is used to + /// prevent replay attacks. + #[prost(uint64, tag = "3")] + pub sequence: u64, +} +impl ::prost::Name for SignerInfo { + const NAME: &'static str = "SignerInfo"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.SignerInfo".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.SignerInfo".into() + } +} +/// ModeInfo describes the signing mode of a single or nested multisig signer. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ModeInfo { + /// sum is the oneof that specifies whether this represents a single or nested + /// multisig signer + #[prost(oneof = "mode_info::Sum", tags = "1, 2")] + pub sum: ::core::option::Option, +} +/// Nested message and enum types in `ModeInfo`. +pub mod mode_info { + /// Single is the mode info for a single signer. It is structured as a message + /// to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the + /// future + #[derive(Clone, Copy, PartialEq, ::prost::Message)] + pub struct Single { + /// mode is the signing mode of the single signer + #[prost(enumeration = "super::super::signing::SignMode", tag = "1")] + pub mode: i32, + } + impl ::prost::Name for Single { + const NAME: &'static str = "Single"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.ModeInfo.Single".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.ModeInfo.Single".into() + } + } + /// Multi is the mode info for a multisig public key + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Multi { + /// bitarray specifies which keys within the multisig are signing + #[prost(message, optional, tag = "1")] + pub bitarray: ::core::option::Option< + super::super::super::crypto::multisig::CompactBitArray, + >, + /// mode_infos is the corresponding modes of the signers of the multisig + /// which could include nested multisig public keys + #[prost(message, repeated, tag = "2")] + pub mode_infos: ::prost::alloc::vec::Vec, + } + impl ::prost::Name for Multi { + const NAME: &'static str = "Multi"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.ModeInfo.Multi".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.ModeInfo.Multi".into() + } + } + /// sum is the oneof that specifies whether this represents a single or nested + /// multisig signer + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Sum { + /// single represents a single signer + #[prost(message, tag = "1")] + Single(Single), + /// multi represents a nested multisig signer + #[prost(message, tag = "2")] + Multi(Multi), + } +} +impl ::prost::Name for ModeInfo { + const NAME: &'static str = "ModeInfo"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.ModeInfo".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.ModeInfo".into() + } +} +/// Fee includes the amount of coins paid in fees and the maximum +/// gas to be used by the transaction. The ratio yields an effective "gasprice", +/// which must be above some miminum to be accepted into the mempool. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Fee { + /// amount is the amount of coins to be paid as a fee + #[prost(message, repeated, tag = "1")] + pub amount: ::prost::alloc::vec::Vec, + /// gas_limit is the maximum gas that can be used in transaction processing + /// before an out of gas error occurs + #[prost(uint64, tag = "2")] + pub gas_limit: u64, + /// if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees. + /// the payer must be a tx signer (and thus have signed this field in AuthInfo). + /// setting this field does *not* change the ordering of required signers for the transaction. + #[prost(string, tag = "3")] + pub payer: ::prost::alloc::string::String, + /// if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used + /// to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does + /// not support fee grants, this will fail + #[prost(string, tag = "4")] + pub granter: ::prost::alloc::string::String, +} +impl ::prost::Name for Fee { + const NAME: &'static str = "Fee"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.Fee".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.Fee".into() + } +} +/// Tip is the tip used for meta-transactions. +/// +/// Since: cosmos-sdk 0.46 +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Tip { + /// amount is the amount of the tip + #[prost(message, repeated, tag = "1")] + pub amount: ::prost::alloc::vec::Vec, + /// tipper is the address of the account paying for the tip + #[prost(string, tag = "2")] + pub tipper: ::prost::alloc::string::String, +} +impl ::prost::Name for Tip { + const NAME: &'static str = "Tip"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.Tip".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.Tip".into() + } +} +/// BroadcastTxRequest is the request type for the Service.BroadcastTxRequest +/// RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BroadcastTxRequest { + /// tx_bytes is the raw transaction. + #[prost(bytes = "bytes", tag = "1")] + pub tx_bytes: ::prost::bytes::Bytes, + #[prost(enumeration = "BroadcastMode", tag = "2")] + pub mode: i32, +} +impl ::prost::Name for BroadcastTxRequest { + const NAME: &'static str = "BroadcastTxRequest"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.BroadcastTxRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.BroadcastTxRequest".into() + } +} +/// BroadcastTxResponse is the response type for the +/// Service.BroadcastTx method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BroadcastTxResponse { + /// tx_response is the queried TxResponses. + #[prost(message, optional, tag = "1")] + pub tx_response: ::core::option::Option< + super::super::base::abci::TxResponse, + >, +} +impl ::prost::Name for BroadcastTxResponse { + const NAME: &'static str = "BroadcastTxResponse"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.BroadcastTxResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.BroadcastTxResponse".into() + } +} +/// GetTxRequest is the request type for the Service.GetTx +/// RPC method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetTxRequest { + /// hash is the tx hash to query, encoded as a hex string. + #[prost(string, tag = "1")] + pub hash: ::prost::alloc::string::String, +} +impl ::prost::Name for GetTxRequest { + const NAME: &'static str = "GetTxRequest"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.GetTxRequest".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.GetTxRequest".into() + } +} +/// GetTxResponse is the response type for the Service.GetTx method. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetTxResponse { + /// tx is the queried transaction. + #[prost(message, optional, tag = "1")] + pub tx: ::core::option::Option, + /// tx_response is the queried TxResponses. + #[prost(message, optional, tag = "2")] + pub tx_response: ::core::option::Option< + super::super::base::abci::TxResponse, + >, +} +impl ::prost::Name for GetTxResponse { + const NAME: &'static str = "GetTxResponse"; + const PACKAGE: &'static str = "cosmos.tx.v1beta1"; + fn full_name() -> ::prost::alloc::string::String { + "cosmos.tx.v1beta1.GetTxResponse".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/cosmos.tx.v1beta1.GetTxResponse".into() + } +} +/// BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC method. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum BroadcastMode { + /// zero-value for mode ordering + Unspecified = 0, + /// BROADCAST_MODE_BLOCK defines a tx broadcasting mode where the client waits for + /// the tx to be committed in a block. + Block = 1, + /// BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits for + /// a CheckTx execution response only. + Sync = 2, + /// BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client returns + /// immediately. + Async = 3, +} +impl BroadcastMode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Unspecified => "BROADCAST_MODE_UNSPECIFIED", + Self::Block => "BROADCAST_MODE_BLOCK", + Self::Sync => "BROADCAST_MODE_SYNC", + Self::Async => "BROADCAST_MODE_ASYNC", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "BROADCAST_MODE_UNSPECIFIED" => Some(Self::Unspecified), + "BROADCAST_MODE_BLOCK" => Some(Self::Block), + "BROADCAST_MODE_SYNC" => Some(Self::Sync), + "BROADCAST_MODE_ASYNC" => Some(Self::Async), + _ => None, + } + } +} +/// Generated client implementations. +pub mod service_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Service defines a gRPC service for interacting with transactions. + #[derive(Debug, Clone)] + pub struct ServiceClient { + inner: tonic::client::Grpc, + } + impl ServiceClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ServiceClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ServiceClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + ServiceClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// GetTx fetches a tx by hash. + pub async fn get_tx( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/cosmos.tx.v1beta1.Service/GetTx", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("cosmos.tx.v1beta1.Service", "GetTx")); + self.inner.unary(req, path, codec).await + } + /// BroadcastTx broadcast transaction. + pub async fn broadcast_tx( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/cosmos.tx.v1beta1.Service/BroadcastTx", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("cosmos.tx.v1beta1.Service", "BroadcastTx")); + self.inner.unary(req, path, codec).await + } + } +} diff --git a/core/node/da_clients/src/celestia/generated/tendermint.abci.rs b/core/node/da_clients/src/celestia/generated/tendermint.abci.rs new file mode 100644 index 00000000000..ab3bbeb946f --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/tendermint.abci.rs @@ -0,0 +1,42 @@ +// This file is @generated by prost-build. +/// Event allows application developers to attach additional information to +/// ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and ResponseDeliverTx. +/// Later, transactions may be queried using these events. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Event { + #[prost(string, tag = "1")] + pub r#type: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub attributes: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for Event { + const NAME: &'static str = "Event"; + const PACKAGE: &'static str = "tendermint.abci"; + fn full_name() -> ::prost::alloc::string::String { + "tendermint.abci.Event".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/tendermint.abci.Event".into() + } +} +/// EventAttribute is a single key-value pair, associated with an event. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EventAttribute { + #[prost(bytes = "bytes", tag = "1")] + pub key: ::prost::bytes::Bytes, + #[prost(bytes = "bytes", tag = "2")] + pub value: ::prost::bytes::Bytes, + /// nondeterministic + #[prost(bool, tag = "3")] + pub index: bool, +} +impl ::prost::Name for EventAttribute { + const NAME: &'static str = "EventAttribute"; + const PACKAGE: &'static str = "tendermint.abci"; + fn full_name() -> ::prost::alloc::string::String { + "tendermint.abci.EventAttribute".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/tendermint.abci.EventAttribute".into() + } +} diff --git a/core/node/da_clients/src/celestia/generated/tendermint.types.rs b/core/node/da_clients/src/celestia/generated/tendermint.types.rs new file mode 100644 index 00000000000..000e3f2c1fb --- /dev/null +++ b/core/node/da_clients/src/celestia/generated/tendermint.types.rs @@ -0,0 +1,48 @@ +// This file is @generated by prost-build. +/// Blob (named after binary large object) is a chunk of data submitted by a user +/// to be published to the Celestia blockchain. The data of a Blob is published +/// to a namespace and is encoded into shares based on the format specified by +/// share_version. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Blob { + #[prost(bytes = "bytes", tag = "1")] + pub namespace_id: ::prost::bytes::Bytes, + #[prost(bytes = "bytes", tag = "2")] + pub data: ::prost::bytes::Bytes, + #[prost(uint32, tag = "3")] + pub share_version: u32, + #[prost(uint32, tag = "4")] + pub namespace_version: u32, +} +impl ::prost::Name for Blob { + const NAME: &'static str = "Blob"; + const PACKAGE: &'static str = "tendermint.types"; + fn full_name() -> ::prost::alloc::string::String { + "tendermint.types.Blob".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/tendermint.types.Blob".into() + } +} +/// BlobTx wraps an encoded sdk.Tx with a second field to contain blobs of data. +/// The raw bytes of the blobs are not signed over, instead we verify each blob +/// using the relevant MsgPayForBlobs that is signed over in the encoded sdk.Tx. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobTx { + #[prost(bytes = "bytes", tag = "1")] + pub tx: ::prost::bytes::Bytes, + #[prost(message, repeated, tag = "2")] + pub blobs: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub type_id: ::prost::alloc::string::String, +} +impl ::prost::Name for BlobTx { + const NAME: &'static str = "BlobTx"; + const PACKAGE: &'static str = "tendermint.types"; + fn full_name() -> ::prost::alloc::string::String { + "tendermint.types.BlobTx".into() + } + fn type_url() -> ::prost::alloc::string::String { + "/tendermint.types.BlobTx".into() + } +} diff --git a/core/node/da_clients/src/celestia/mod.rs b/core/node/da_clients/src/celestia/mod.rs new file mode 100644 index 00000000000..ce648531f28 --- /dev/null +++ b/core/node/da_clients/src/celestia/mod.rs @@ -0,0 +1,58 @@ +mod client; +mod sdk; + +pub use self::client::CelestiaClient; + +pub mod celestia_proto { + include!("generated/celestia.blob.v1.rs"); +} + +pub mod cosmos { + pub mod auth { + include!("generated/cosmos.auth.v1beta1.rs"); + } + + pub mod base { + pub mod abci { + include!("generated/cosmos.base.abci.v1beta1.rs"); + } + + pub mod node { + include!("generated/cosmos.base.node.v1beta1.rs"); + } + + pub mod v1beta1 { + include!("generated/cosmos.base.v1beta1.rs"); + } + } + + pub mod tx { + pub mod signing { + include!("generated/cosmos.tx.signing.v1beta1.rs"); + } + + pub mod v1beta1 { + include!("generated/cosmos.tx.v1beta1.rs"); + } + } + + pub mod crypto { + pub mod multisig { + include!("generated/cosmos.crypto.multisig.v1beta1.rs"); + } + + pub mod secp256k1 { + include!("generated/cosmos.crypto.secp256k1.rs"); + } + } +} + +pub mod tendermint { + pub mod abci { + include!("generated/tendermint.abci.rs"); + } + + pub mod types { + include!("generated/tendermint.types.rs"); + } +} diff --git a/core/node/da_clients/src/celestia/sdk.rs b/core/node/da_clients/src/celestia/sdk.rs new file mode 100644 index 00000000000..5fd9aea79f0 --- /dev/null +++ b/core/node/da_clients/src/celestia/sdk.rs @@ -0,0 +1,602 @@ +use std::{ + fmt::{Display, Formatter, Result}, + str::FromStr, + time::{Duration, Instant}, +}; + +use celestia_types::Blob; +use prost::{bytes::Bytes, Message, Name}; +use secp256k1::{PublicKey, Secp256k1, SecretKey}; +use sha2::Digest; +use tonic::transport::Channel; + +use super::{ + celestia_proto::{ + query_client::QueryClient as BlobQueryClient, MsgPayForBlobs, + QueryParamsRequest as QueryBlobParamsRequest, + }, + cosmos::{ + auth::{ + query_client::QueryClient as AuthQueryClient, BaseAccount, QueryAccountRequest, + QueryParamsRequest as QueryAuthParamsRequest, + }, + base::{ + node::{ + service_client::ServiceClient as MinGasPriceClient, + ConfigRequest as MinGasPriceRequest, + }, + v1beta1::Coin, + }, + crypto::secp256k1 as ec_proto, + tx::v1beta1::{ + mode_info::{Single, Sum}, + service_client::ServiceClient as TxClient, + AuthInfo, BroadcastMode, BroadcastTxRequest, Fee, GetTxRequest, ModeInfo, SignDoc, + SignerInfo, Tx, TxBody, + }, + }, + tendermint::types::{Blob as PbBlob, BlobTx}, +}; + +const UNITS_SUFFIX: &str = "utia"; +pub const ADDRESS_LENGTH: usize = 20; +const ACCOUNT_ADDRESS_PREFIX: bech32::Hrp = bech32::Hrp::parse_unchecked("celestia"); +const BLOB_TX_TYPE_ID: &str = "BLOB"; + +#[derive(Clone)] +pub(crate) struct RawCelestiaClient { + grpc_channel: Channel, + address: String, + chain_id: String, + signing_key: SecretKey, +} + +impl RawCelestiaClient { + pub(crate) fn new( + grpc_channel: Channel, + private_key: String, + chain_id: String, + ) -> anyhow::Result { + let signing_key = SecretKey::from_str(&private_key) + .map_err(|e| anyhow::anyhow!("Failed to parse private key: {}", e))?; + let address = get_address(signing_key.public_key(&Secp256k1::new()))?; + + Ok(Self { + grpc_channel, + address, + chain_id, + signing_key, + }) + } + + /// Prepares a blob transaction for the given blobs. + pub(crate) async fn prepare(&self, blobs: Vec) -> anyhow::Result { + let (gas_per_blob_byte, tx_size_cost_per_byte, min_gas_price, base_account) = tokio::try_join!( + self.get_gas_per_blob_byte(), + self.fetch_tx_size_cost_per_byte(), + self.fetch_min_gas_price(), + self.fetch_account(), + )?; + + let msg_pay_for_blobs = new_msg_pay_for_blobs(blobs.as_slice(), self.address.clone())?; + + let gas_limit = estimate_gas( + &msg_pay_for_blobs.blob_sizes, + gas_per_blob_byte, + tx_size_cost_per_byte, + ); + let fee = calculate_fee(min_gas_price, gas_limit); + + let signed_tx = new_signed_tx( + &msg_pay_for_blobs, + &base_account, + gas_limit, + fee, + self.chain_id.clone(), + &self.signing_key, + ); + + Ok(new_blob_tx(&signed_tx, blobs.iter())) + } + + /// Submits the blob transaction to the node and returns the height of the block in which it was + pub(super) async fn submit( + &self, + blob_tx_hash: BlobTxHash, + blob_tx: BlobTx, + ) -> anyhow::Result { + let mut client: TxClient = TxClient::new(self.grpc_channel.clone()); + let hex_encoded_tx_hash = self.broadcast_tx(&mut client, blob_tx).await?; + if hex_encoded_tx_hash != blob_tx_hash.clone().hex() { + tracing::error!( + "tx hash {} returned from celestia app is not the same as \ + the locally calculated one {}; submission file has invalid data", + hex_encoded_tx_hash, + blob_tx_hash + ); + } + tracing::info!(tx_hash = %hex_encoded_tx_hash, "broadcast blob transaction succeeded"); + + let height = self + .confirm_submission(&mut client, hex_encoded_tx_hash) + .await; + Ok(height) + } + + /// Fetches the gas cost per byte for blobs from the node. + async fn get_gas_per_blob_byte(&self) -> anyhow::Result { + let mut blob_query_client = BlobQueryClient::new(self.grpc_channel.clone()); + let response = blob_query_client.params(QueryBlobParamsRequest {}).await; + + let params = response + .map_err(|status| { + anyhow::format_err!( + "failed to get blob params, code: {}, message: {}", + status.code(), + status.message() + ) + })? + .into_inner() + .params + .ok_or_else(|| anyhow::anyhow!("EmptyBlobParams"))?; + + Ok(params.gas_per_blob_byte) + } + + /// Fetches the transaction size cost per byte from the node. + async fn fetch_tx_size_cost_per_byte(&self) -> anyhow::Result { + let mut auth_query_client = AuthQueryClient::new(self.grpc_channel.clone()); + let response = auth_query_client.params(QueryAuthParamsRequest {}).await; + + let params = response + .map_err(|status| { + anyhow::format_err!( + "failed to get auth params, code: {}, message: {}", + status.code(), + status.message() + ) + })? + .into_inner() + .params + .ok_or_else(|| anyhow::anyhow!("EmptyAuthParams"))?; + + Ok(params.tx_size_cost_per_byte) + } + + /// Fetches the minimum gas price from the node. + async fn fetch_min_gas_price(&self) -> anyhow::Result { + let mut min_gas_price_client = MinGasPriceClient::new(self.grpc_channel.clone()); + let response = min_gas_price_client.config(MinGasPriceRequest {}).await; + + let min_gas_price_with_suffix = response + .map_err(|status| { + anyhow::format_err!( + "failed to get price params, code: {}, message: {}", + status.code(), + status.message() + ) + })? + .into_inner() + .minimum_gas_price; + + let min_gas_price_str = min_gas_price_with_suffix + .strip_suffix(UNITS_SUFFIX) + .ok_or_else(|| { + anyhow::anyhow!( + "MinGasPrice bad suffix, min_gas_price: {}, expected_suffix: {}", + min_gas_price_with_suffix.clone(), + UNITS_SUFFIX + ) + })?; + + min_gas_price_str.parse::().map_err(|source| { + anyhow::anyhow!( + "Failed to parse min gas price, min_gas_price: {}, err: {}", + min_gas_price_str, + source, + ) + }) + } + + /// Fetches the account info for the current address. + async fn fetch_account(&self) -> anyhow::Result { + let mut auth_query_client = AuthQueryClient::new(self.grpc_channel.clone()); + let request = QueryAccountRequest { + address: self.address.clone(), + }; + + let account_info = auth_query_client.account(request).await.map_err(|status| { + anyhow::anyhow!( + "failed to get account info, code: {}, message: {}", + status.code(), + status.message() + ) + })?; + + let account_as_any = account_info + .into_inner() + .account + .ok_or_else(|| anyhow::anyhow!("empty account info"))?; + let expected_type_url = BaseAccount::type_url(); + + if expected_type_url == account_as_any.type_url { + return BaseAccount::decode(&*account_as_any.value) + .map_err(|error| anyhow::anyhow!("failed to decode account info: {}", error)); + } + + Err(anyhow::anyhow!( + "unexpected account type, expected: {}, got: {}", + expected_type_url, + account_as_any.type_url + )) + } + + /// Broadcasts the transaction and returns the transaction hash. + async fn broadcast_tx( + &self, + client: &mut TxClient, + blob_tx: BlobTx, + ) -> anyhow::Result { + let request = BroadcastTxRequest { + tx_bytes: Bytes::from(blob_tx.encode_to_vec()), + mode: i32::from(BroadcastMode::Sync), + }; + + let mut tx_response = client + .broadcast_tx(request) + .await + .map_err(|status| { + anyhow::anyhow!( + "failed to broadcast the tx, code: {}, message: {}", + status.code(), + status.message() + ) + })? + .into_inner() + .tx_response + .ok_or_else(|| anyhow::anyhow!("empty broadcast tx response"))?; + + if tx_response.code != 0 { + return Err(anyhow::format_err!( + "failed to broadcast the tx, tx_hash: {}, code: {}, namespace: {}, log: {}", + tx_response.txhash, + tx_response.code, + tx_response.codespace, + tx_response.raw_log, + )); + } + + tx_response.txhash.make_ascii_lowercase(); + Ok(tx_response.txhash) + } + + /// Waits for the transaction to be included in a block and returns the height of that block. + async fn confirm_submission( + &self, + client: &mut TxClient, + hex_encoded_tx_hash: String, + ) -> u64 { + // The min seconds to sleep after receiving a GetTx response and sending the next request. + const MIN_POLL_INTERVAL_SECS: u64 = 1; + // The max seconds to sleep after receiving a GetTx response and sending the next request. + const MAX_POLL_INTERVAL_SECS: u64 = 12; + // How long to wait after starting `confirm_submission` before starting to log errors. + const START_LOGGING_DELAY: Duration = Duration::from_secs(12); + // The minimum duration between logging errors. + const LOG_ERROR_INTERVAL: Duration = Duration::from_secs(5); + + let start = Instant::now(); + let mut logged_at = start; + + let mut log_if_due = |maybe_error: Option| { + if start.elapsed() <= START_LOGGING_DELAY || logged_at.elapsed() <= LOG_ERROR_INTERVAL { + return; + } + let reason = maybe_error + .map_or(anyhow::anyhow!("transaction still pending"), |error| { + anyhow::anyhow!("transaction still pending, error: {}", error) + }); + tracing::warn!( + %reason, + tx_hash = %hex_encoded_tx_hash, + elapsed_seconds = start.elapsed().as_secs_f32(), + "waiting to confirm blob submission" + ); + logged_at = Instant::now(); + }; + + let mut sleep_secs = MIN_POLL_INTERVAL_SECS; + loop { + tokio::time::sleep(Duration::from_secs(sleep_secs)).await; + let res = self + .clone() + .get_tx(client, hex_encoded_tx_hash.clone()) + .await; + match res { + Ok(Some(height)) => return height, + Ok(None) => { + sleep_secs = MIN_POLL_INTERVAL_SECS; + log_if_due(None); + } + Err(error) => { + sleep_secs = + std::cmp::min(sleep_secs.saturating_mul(2), MAX_POLL_INTERVAL_SECS); + log_if_due(Some(error)); + } + } + } + } + + /// Returns the height of the block in which the transaction was included (if it was). + async fn get_tx( + self, + client: &mut TxClient, + hex_encoded_tx_hash: String, + ) -> anyhow::Result> { + let request = GetTxRequest { + hash: hex_encoded_tx_hash, + }; + let response = client.get_tx(request).await; + + let ok_response = match response { + Ok(resp) => resp, + Err(status) => { + if status.code() == tonic::Code::NotFound { + tracing::trace!(msg = status.message(), "transaction still pending"); + return Ok(None); + } + return Err(anyhow::anyhow!( + "failed to get tx, code: {}, message: {}", + status.code(), + status.message() + )); + } + }; + let tx_response = ok_response + .into_inner() + .tx_response + .ok_or_else(|| anyhow::anyhow!("Empty get tx response"))?; + if tx_response.code != 0 { + return Err(anyhow::anyhow!( + "failed to get tx, tx_hash: {}, code: {}, namespace: {}, log: {}", + tx_response.txhash, + tx_response.code, + tx_response.codespace, + tx_response.raw_log, + )); + } + if tx_response.height == 0 { + tracing::trace!(tx_hash = %tx_response.txhash, "transaction still pending"); + return Ok(None); + } + + let height = u64::try_from(tx_response.height).map_err(|_| { + anyhow::anyhow!("GetTxResponseNegativeBlockHeight: {}", tx_response.height) + })?; + + tracing::debug!(tx_hash = %tx_response.txhash, height, "transaction succeeded"); + Ok(Some(height)) + } +} + +/// Returns a `BlobTx` for the given signed tx and blobs. +fn new_blob_tx<'a>(signed_tx: &Tx, blobs: impl Iterator) -> BlobTx { + let blobs = blobs + .map(|blob| PbBlob { + namespace_id: Bytes::from(blob.namespace.id().to_vec()), + namespace_version: u32::from(blob.namespace.version()), + data: Bytes::from(blob.data.clone()), + share_version: u32::from(blob.share_version), + }) + .collect(); + BlobTx { + tx: Bytes::from(signed_tx.encode_to_vec()), + blobs, + type_id: BLOB_TX_TYPE_ID.to_string(), + } +} + +/// Returns a signed tx for the given message, account and metadata. +fn new_signed_tx( + msg_pay_for_blobs: &MsgPayForBlobs, + base_account: &BaseAccount, + gas_limit: u64, + fee: u64, + chain_id: String, + signing_key: &SecretKey, +) -> Tx { + const SIGNING_MODE_INFO: Option = Some(ModeInfo { + sum: Some(Sum::Single(Single { mode: 1 })), + }); + + let fee_coin = Coin { + denom: UNITS_SUFFIX.to_string(), + amount: fee.to_string(), + }; + let fee = Fee { + amount: vec![fee_coin], + gas_limit, + ..Fee::default() + }; + + let public_key = ec_proto::PubKey { + key: Bytes::from( + signing_key + .public_key(&Secp256k1::new()) + .serialize() + .to_vec(), + ), + }; + let public_key_as_any = pbjson_types::Any { + type_url: ec_proto::PubKey::type_url(), + value: public_key.encode_to_vec().into(), + }; + let auth_info = AuthInfo { + signer_infos: vec![SignerInfo { + public_key: Some(public_key_as_any), + mode_info: SIGNING_MODE_INFO, + sequence: base_account.sequence, + }], + fee: Some(fee), + tip: None, + }; + + let msg = pbjson_types::Any { + type_url: MsgPayForBlobs::type_url(), + value: msg_pay_for_blobs.encode_to_vec().into(), + }; + let tx_body = TxBody { + messages: vec![msg], + ..TxBody::default() + }; + + let bytes_to_sign = SignDoc { + body_bytes: Bytes::from(tx_body.encode_to_vec()), + auth_info_bytes: Bytes::from(auth_info.encode_to_vec()), + chain_id, + account_number: base_account.account_number, + } + .encode_to_vec(); + let hashed_bytes: [u8; 32] = sha2::Sha256::digest(bytes_to_sign).into(); + let signature = secp256k1::Secp256k1::new().sign_ecdsa( + &secp256k1::Message::from_slice(&hashed_bytes[..]).unwrap(), // unwrap is safe here because we know the length of the hashed bytes + signing_key, + ); + Tx { + body: Some(tx_body), + auth_info: Some(auth_info), + signatures: vec![Bytes::from(signature.serialize_compact().to_vec())], + } +} + +/// Returns the fee for the signed tx. +fn calculate_fee(min_gas_price: f64, gas_limit: u64) -> u64 { + let calculated_fee = (min_gas_price * gas_limit as f64).ceil() as u64; + tracing::info!( + "calculated fee: {}, min_gas_price: {}, gas_limit: {}", + calculated_fee, + min_gas_price, + gas_limit + ); + + calculated_fee +} + +fn estimate_gas(blob_sizes: &[u32], gas_per_blob_byte: u32, tx_size_cost_per_byte: u64) -> u64 { + // From https://github.com/celestiaorg/celestia-app/blob/v1.4.0/pkg/appconsts/global_consts.go#L28 + const SHARE_SIZE: u64 = 512; + // From https://github.com/celestiaorg/celestia-app/blob/v1.4.0/pkg/appconsts/global_consts.go#L55 + const CONTINUATION_COMPACT_SHARE_CONTENT_SIZE: u32 = 482; + // From https://github.com/celestiaorg/celestia-app/blob/v1.4.0/pkg/appconsts/global_consts.go#L59 + const FIRST_SPARSE_SHARE_CONTENT_SIZE: u32 = 478; + // From https://github.com/celestiaorg/celestia-app/blob/v1.4.0/x/blob/types/payforblob.go#L40 + const PFB_GAS_FIXED_COST: u64 = 75_000; + // From https://github.com/celestiaorg/celestia-app/blob/v1.4.0/x/blob/types/payforblob.go#L44 + const BYTES_PER_BLOB_INFO: u64 = 70; + + // From https://github.com/celestiaorg/celestia-app/blob/v1.4.0/pkg/shares/share_sequence.go#L126 + // + // `blob_len` is the size in bytes of one blob's `data` field. + fn sparse_shares_needed(blob_len: u32) -> u64 { + if blob_len == 0 { + return 0; + } + + if blob_len < FIRST_SPARSE_SHARE_CONTENT_SIZE { + return 1; + } + + // Use `u64` here to avoid overflow while adding below. + let mut bytes_available = u64::from(FIRST_SPARSE_SHARE_CONTENT_SIZE); + let mut shares_needed = 1_u64; + while bytes_available < u64::from(blob_len) { + bytes_available = bytes_available + .checked_add(u64::from(CONTINUATION_COMPACT_SHARE_CONTENT_SIZE)) + .expect( + "this can't overflow, as on each iteration `bytes_available < u32::MAX`, and \ + we're adding at most `u32::MAX` to it", + ); + shares_needed = shares_needed.checked_add(1).expect( + "this can't overflow, as the loop cannot execute for `u64::MAX` iterations", + ); + } + shares_needed + } + + let total_shares_used: u64 = blob_sizes.iter().copied().map(sparse_shares_needed).sum(); + let blob_count = blob_sizes.len().try_into().unwrap_or(u64::MAX); + + let shares_gas = total_shares_used + .saturating_mul(SHARE_SIZE) + .saturating_mul(u64::from(gas_per_blob_byte)); + let blob_info_gas = tx_size_cost_per_byte + .saturating_mul(BYTES_PER_BLOB_INFO) + .saturating_mul(blob_count); + + shares_gas + .saturating_add(blob_info_gas) + .saturating_add(PFB_GAS_FIXED_COST) +} + +/// Prepares a `MsgPayForBlobs` message for the given blobs. +fn new_msg_pay_for_blobs(blobs: &[Blob], signer: String) -> anyhow::Result { + let mut blob_sizes = Vec::with_capacity(blobs.len()); + let mut namespaces = Vec::with_capacity(blobs.len()); + let mut share_commitments = Vec::with_capacity(blobs.len()); + let mut share_versions = Vec::with_capacity(blobs.len()); + for blob in blobs { + blob_sizes.push(blob.data.len()); + namespaces.push(Bytes::from(blob.namespace.as_bytes().to_vec())); + share_commitments.push(Bytes::from(blob.commitment.0.to_vec())); + share_versions.push(u32::from(blob.share_version)); + } + + let blob_sizes = blob_sizes + .into_iter() + .map(|blob_size| { + u32::try_from(blob_size) + .map_err(|_| anyhow::anyhow!("blob too large, size: {}", blob_size)) + }) + .collect::>()?; + + Ok(MsgPayForBlobs { + signer, + namespaces, + blob_sizes, + share_commitments, + share_versions, + }) +} + +fn get_address(public_key: PublicKey) -> anyhow::Result { + use ripemd::{Digest, Ripemd160}; + + let sha_digest = sha2::Sha256::digest(public_key.serialize()); + let ripemd_digest = Ripemd160::digest(&sha_digest[..]); + let mut bytes = [0u8; ADDRESS_LENGTH]; + bytes.copy_from_slice(&ripemd_digest[..ADDRESS_LENGTH]); + + Ok(bech32::encode::( + ACCOUNT_ADDRESS_PREFIX, + bytes.as_slice(), + )?) +} + +#[derive(Clone, Debug)] +pub(super) struct BlobTxHash([u8; 32]); + +impl BlobTxHash { + pub(super) fn compute(blob_tx: &BlobTx) -> Self { + Self(sha2::Sha256::digest(&blob_tx.tx).into()) + } + + pub(super) fn hex(self) -> String { + hex::encode(self.0) + } +} + +impl Display for BlobTxHash { + fn fmt(&self, formatter: &mut Formatter<'_>) -> Result { + write!(formatter, "{}", hex::encode(self.0)) + } +} diff --git a/core/node/da_clients/src/eigen/README.md b/core/node/da_clients/src/eigen/README.md new file mode 100644 index 00000000000..634b4eb5878 --- /dev/null +++ b/core/node/da_clients/src/eigen/README.md @@ -0,0 +1,35 @@ +# EigenDA client + +--- + +This is an implementation of the EigenDA client capable of sending the blobs to DA layer. It uses authenticated +requests, though the auth headers are kind of mocked in the current API implementation. + +The generated files are received by compiling the `.proto` files from EigenDA repo using the following function: + +```rust +pub fn compile_protos() { + let fds = protox::compile( + [ + "proto/common.proto", + "proto/disperser.proto", + ], + ["."], + ) + .expect("protox failed to build"); + + tonic_build::configure() + .build_client(true) + .build_server(false) + .skip_protoc_run() + .out_dir("generated") + .compile_fds(fds) + .unwrap(); +} +``` + +proto files are not included here to not create confusion in case they are not updated in time, so the EigenDA +[repo](https://github.com/Layr-Labs/eigenda/tree/master/api/proto) has to be a source of truth for the proto files. + +The generated folder here is considered a temporary solution until the EigenDA has a library with either a protogen, or +preferably a full Rust client implementation. diff --git a/core/node/da_clients/src/eigen/client.rs b/core/node/da_clients/src/eigen/client.rs new file mode 100644 index 00000000000..d977620526a --- /dev/null +++ b/core/node/da_clients/src/eigen/client.rs @@ -0,0 +1,65 @@ +use std::{str::FromStr, sync::Arc}; + +use async_trait::async_trait; +use secp256k1::SecretKey; +use subxt_signer::ExposeSecret; +use zksync_config::{configs::da_client::eigen::EigenSecrets, EigenConfig}; +use zksync_da_client::{ + types::{DAError, DispatchResponse, InclusionData}, + DataAvailabilityClient, +}; + +use super::sdk::RawEigenClient; +use crate::utils::to_non_retriable_da_error; + +#[derive(Debug, Clone)] +pub struct EigenClient { + client: Arc, +} + +impl EigenClient { + pub async fn new(config: EigenConfig, secrets: EigenSecrets) -> anyhow::Result { + let private_key = SecretKey::from_str(secrets.private_key.0.expose_secret().as_str()) + .map_err(|e| anyhow::anyhow!("Failed to parse private key: {}", e))?; + + Ok(EigenClient { + client: Arc::new( + RawEigenClient::new( + config.rpc_node_url, + config.inclusion_polling_interval_ms, + private_key, + ) + .await?, + ), + }) + } +} + +#[async_trait] +impl DataAvailabilityClient for EigenClient { + async fn dispatch_blob( + &self, + _: u32, // batch number + data: Vec, + ) -> Result { + let blob_id = self + .client + .dispatch_blob(data) + .await + .map_err(to_non_retriable_da_error)?; + + Ok(DispatchResponse::from(blob_id)) + } + + async fn get_inclusion_data(&self, _: &str) -> Result, DAError> { + Ok(Some(InclusionData { data: vec![] })) + } + + fn clone_boxed(&self) -> Box { + Box::new(self.clone()) + } + + fn blob_size_limit(&self) -> Option { + Some(1920 * 1024) // 2mb - 128kb as a buffer + } +} diff --git a/core/node/da_clients/src/eigen/generated/common.rs b/core/node/da_clients/src/eigen/generated/common.rs new file mode 100644 index 00000000000..0599b9af412 --- /dev/null +++ b/core/node/da_clients/src/eigen/generated/common.rs @@ -0,0 +1,63 @@ +// This file is @generated by prost-build. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct G1Commitment { + /// The X coordinate of the KZG commitment. This is the raw byte representation of the field element. + #[prost(bytes = "vec", tag = "1")] + pub x: ::prost::alloc::vec::Vec, + /// The Y coordinate of the KZG commitment. This is the raw byte representation of the field element. + #[prost(bytes = "vec", tag = "2")] + pub y: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct G2Commitment { + /// The A0 element of the X coordinate of G2 point. + #[prost(bytes = "vec", tag = "1")] + pub x_a0: ::prost::alloc::vec::Vec, + /// The A1 element of the X coordinate of G2 point. + #[prost(bytes = "vec", tag = "2")] + pub x_a1: ::prost::alloc::vec::Vec, + /// The A0 element of the Y coordinate of G2 point. + #[prost(bytes = "vec", tag = "3")] + pub y_a0: ::prost::alloc::vec::Vec, + /// The A1 element of the Y coordinate of G2 point. + #[prost(bytes = "vec", tag = "4")] + pub y_a1: ::prost::alloc::vec::Vec, +} +/// BlobCommitment represents commitment of a specific blob, containing its +/// KZG commitment, degree proof, the actual degree, and data length in number of symbols. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobCommitment { + #[prost(message, optional, tag = "1")] + pub commitment: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub length_commitment: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub length_proof: ::core::option::Option, + #[prost(uint32, tag = "4")] + pub data_length: u32, +} +/// BlobCertificate is what gets attested by the network +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobCertificate { + #[prost(uint32, tag = "1")] + pub version: u32, + #[prost(bytes = "vec", tag = "2")] + pub blob_key: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "3")] + pub blob_commitment: ::core::option::Option, + #[prost(uint32, repeated, tag = "4")] + pub quorum_numbers: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "5")] + pub reference_block_number: u32, +} +/// A chunk of a blob. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ChunkData { + #[prost(bytes = "vec", tag = "1")] + pub data: ::prost::alloc::vec::Vec, +} diff --git a/core/node/da_clients/src/eigen/generated/disperser.rs b/core/node/da_clients/src/eigen/generated/disperser.rs new file mode 100644 index 00000000000..7e94d910ecb --- /dev/null +++ b/core/node/da_clients/src/eigen/generated/disperser.rs @@ -0,0 +1,517 @@ +// This file is @generated by prost-build. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuthenticatedRequest { + #[prost(oneof = "authenticated_request::Payload", tags = "1, 2")] + pub payload: ::core::option::Option, +} +/// Nested message and enum types in `AuthenticatedRequest`. +pub mod authenticated_request { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Payload { + #[prost(message, tag = "1")] + DisperseRequest(super::DisperseBlobRequest), + #[prost(message, tag = "2")] + AuthenticationData(super::AuthenticationData), + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuthenticatedReply { + #[prost(oneof = "authenticated_reply::Payload", tags = "1, 2")] + pub payload: ::core::option::Option, +} +/// Nested message and enum types in `AuthenticatedReply`. +pub mod authenticated_reply { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Payload { + #[prost(message, tag = "1")] + BlobAuthHeader(super::BlobAuthHeader), + #[prost(message, tag = "2")] + DisperseReply(super::DisperseBlobReply), + } +} +/// BlobAuthHeader contains information about the blob for the client to verify and sign. +/// - Once payments are enabled, the BlobAuthHeader will contain the KZG commitment to the blob, which the client +/// will verify and sign. Having the client verify the KZG commitment instead of calculating it avoids +/// the need for the client to have the KZG structured reference string (SRS), which can be large. +/// The signed KZG commitment prevents the disperser from sending a different blob to the DA Nodes +/// than the one the client sent. +/// - In the meantime, the BlobAuthHeader contains a simple challenge parameter is used to prevent +/// replay attacks in the event that a signature is leaked. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobAuthHeader { + #[prost(uint32, tag = "1")] + pub challenge_parameter: u32, +} +/// AuthenticationData contains the signature of the BlobAuthHeader. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AuthenticationData { + #[prost(bytes = "vec", tag = "1")] + pub authentication_data: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DisperseBlobRequest { + /// The data to be dispersed. + /// The size of data must be <= 2MiB. Every 32 bytes of data chunk is interpreted as an integer in big endian format + /// where the lower address has more significant bits. The integer must stay in the valid range to be interpreted + /// as a field element on the bn254 curve. The valid range is + /// 0 <= x < 21888242871839275222246405745257275088548364400416034343698204186575808495617 + /// containing slightly less than 254 bits and more than 253 bits. If any one of the 32 bytes chunk is outside the range, + /// the whole request is deemed as invalid, and rejected. + #[prost(bytes = "vec", tag = "1")] + pub data: ::prost::alloc::vec::Vec, + /// The quorums to which the blob will be sent, in addition to the required quorums which are configured + /// on the EigenDA smart contract. If required quorums are included here, an error will be returned. + /// The disperser will ensure that the encoded blobs for each quorum are all processed + /// within the same batch. + #[prost(uint32, repeated, tag = "2")] + pub custom_quorum_numbers: ::prost::alloc::vec::Vec, + /// The account ID of the client. This should be a hex-encoded string of the ECSDA public key + /// corresponding to the key used by the client to sign the BlobAuthHeader. + #[prost(string, tag = "3")] + pub account_id: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DisperseBlobReply { + /// The status of the blob associated with the request_id. + #[prost(enumeration = "BlobStatus", tag = "1")] + pub result: i32, + /// The request ID generated by the disperser. + /// Once a request is accepted (although not processed), a unique request ID will be + /// generated. + /// Two different DisperseBlobRequests (determined by the hash of the DisperseBlobRequest) + /// will have different IDs, and the same DisperseBlobRequest sent repeatedly at different + /// times will also have different IDs. + /// The client should use this ID to query the processing status of the request (via + /// the GetBlobStatus API). + #[prost(bytes = "vec", tag = "2")] + pub request_id: ::prost::alloc::vec::Vec, +} +/// BlobStatusRequest is used to query the status of a blob. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobStatusRequest { + #[prost(bytes = "vec", tag = "1")] + pub request_id: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobStatusReply { + /// The status of the blob. + #[prost(enumeration = "BlobStatus", tag = "1")] + pub status: i32, + /// The blob info needed for clients to confirm the blob against the EigenDA contracts. + #[prost(message, optional, tag = "2")] + pub info: ::core::option::Option, +} +/// RetrieveBlobRequest contains parameters to retrieve the blob. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RetrieveBlobRequest { + #[prost(bytes = "vec", tag = "1")] + pub batch_header_hash: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "2")] + pub blob_index: u32, +} +/// RetrieveBlobReply contains the retrieved blob data +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RetrieveBlobReply { + #[prost(bytes = "vec", tag = "1")] + pub data: ::prost::alloc::vec::Vec, +} +/// BlobInfo contains information needed to confirm the blob against the EigenDA contracts +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobInfo { + #[prost(message, optional, tag = "1")] + pub blob_header: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub blob_verification_proof: ::core::option::Option, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobHeader { + /// KZG commitment of the blob. + #[prost(message, optional, tag = "1")] + pub commitment: ::core::option::Option, + /// The length of the blob in symbols (each symbol is 32 bytes). + #[prost(uint32, tag = "2")] + pub data_length: u32, + /// The params of the quorums that this blob participates in. + #[prost(message, repeated, tag = "3")] + pub blob_quorum_params: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobQuorumParam { + /// The ID of the quorum. + #[prost(uint32, tag = "1")] + pub quorum_number: u32, + /// The max percentage of stake within the quorum that can be held by or delegated + /// to adversarial operators. Currently, this and the next parameter are standardized + /// across the quorum using values read from the EigenDA contracts. + #[prost(uint32, tag = "2")] + pub adversary_threshold_percentage: u32, + /// The min percentage of stake that must attest in order to consider + /// the dispersal is successful. + #[prost(uint32, tag = "3")] + pub confirmation_threshold_percentage: u32, + /// The length of each chunk. + #[prost(uint32, tag = "4")] + pub chunk_length: u32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlobVerificationProof { + /// batch_id is an incremental ID assigned to a batch by EigenDAServiceManager + #[prost(uint32, tag = "1")] + pub batch_id: u32, + /// The index of the blob in the batch (which is logically an ordered list of blobs). + #[prost(uint32, tag = "2")] + pub blob_index: u32, + #[prost(message, optional, tag = "3")] + pub batch_metadata: ::core::option::Option, + /// inclusion_proof is a merkle proof for a blob header's inclusion in a batch + #[prost(bytes = "vec", tag = "4")] + pub inclusion_proof: ::prost::alloc::vec::Vec, + /// indexes of quorums in BatchHeader.quorum_numbers that match the quorums in BlobHeader.blob_quorum_params + /// Ex. BlobHeader.blob_quorum_params = [ + /// { + /// quorum_number = 0, + /// ... + /// }, + /// { + /// quorum_number = 3, + /// ... + /// }, + /// { + /// quorum_number = 5, + /// ... + /// }, + /// ] + /// BatchHeader.quorum_numbers = \[0, 5, 3\] => 0x000503 + /// Then, quorum_indexes = \[0, 2, 1\] => 0x000201 + #[prost(bytes = "vec", tag = "5")] + pub quorum_indexes: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BatchMetadata { + #[prost(message, optional, tag = "1")] + pub batch_header: ::core::option::Option, + /// The hash of all public keys of the operators that did not sign the batch. + #[prost(bytes = "vec", tag = "2")] + pub signatory_record_hash: ::prost::alloc::vec::Vec, + /// The fee payment paid by users for dispersing this batch. It's the bytes + /// representation of a big.Int value. + #[prost(bytes = "vec", tag = "3")] + pub fee: ::prost::alloc::vec::Vec, + /// The Ethereum block number at which the batch is confirmed onchain. + #[prost(uint32, tag = "4")] + pub confirmation_block_number: u32, + /// This is the hash of the ReducedBatchHeader defined onchain, see: + /// + /// The is the message that the operators will sign their signatures on. + #[prost(bytes = "vec", tag = "5")] + pub batch_header_hash: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BatchHeader { + /// The root of the merkle tree with the hashes of blob headers as leaves. + #[prost(bytes = "vec", tag = "1")] + pub batch_root: ::prost::alloc::vec::Vec, + /// All quorums associated with blobs in this batch. Sorted in ascending order. + /// Ex. \[0, 2, 1\] => 0x000102 + #[prost(bytes = "vec", tag = "2")] + pub quorum_numbers: ::prost::alloc::vec::Vec, + /// The percentage of stake that has signed for this batch. + /// The quorum_signed_percentages\[i\] is percentage for the quorum_numbers\[i\]. + #[prost(bytes = "vec", tag = "3")] + pub quorum_signed_percentages: ::prost::alloc::vec::Vec, + /// The Ethereum block number at which the batch was created. + /// The Disperser will encode and disperse the blobs based on the onchain info + /// (e.g. operator stakes) at this block number. + #[prost(uint32, tag = "4")] + pub reference_block_number: u32, +} +/// BlobStatus represents the status of a blob. +/// The status of a blob is updated as the blob is processed by the disperser. +/// The status of a blob can be queried by the client using the GetBlobStatus API. +/// Intermediate states are states that the blob can be in while being processed, and it can be updated to a differet state: +/// - PROCESSING +/// - DISPERSING +/// - CONFIRMED +/// Terminal states are states that will not be updated to a different state: +/// - FAILED +/// - FINALIZED +/// - INSUFFICIENT_SIGNATURES +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum BlobStatus { + Unknown = 0, + /// PROCESSING means that the blob is currently being processed by the disperser + Processing = 1, + /// CONFIRMED means that the blob has been dispersed to DA Nodes and the dispersed + /// batch containing the blob has been confirmed onchain + Confirmed = 2, + /// FAILED means that the blob has failed permanently (for reasons other than insufficient + /// signatures, which is a separate state) + Failed = 3, + /// FINALIZED means that the block containing the blob's confirmation transaction has been finalized on Ethereum + Finalized = 4, + /// INSUFFICIENT_SIGNATURES means that the confirmation threshold for the blob was not met + /// for at least one quorum. + InsufficientSignatures = 5, + /// DISPERSING means that the blob is currently being dispersed to DA Nodes and being confirmed onchain + Dispersing = 6, +} +impl BlobStatus { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + BlobStatus::Unknown => "UNKNOWN", + BlobStatus::Processing => "PROCESSING", + BlobStatus::Confirmed => "CONFIRMED", + BlobStatus::Failed => "FAILED", + BlobStatus::Finalized => "FINALIZED", + BlobStatus::InsufficientSignatures => "INSUFFICIENT_SIGNATURES", + BlobStatus::Dispersing => "DISPERSING", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "UNKNOWN" => Some(Self::Unknown), + "PROCESSING" => Some(Self::Processing), + "CONFIRMED" => Some(Self::Confirmed), + "FAILED" => Some(Self::Failed), + "FINALIZED" => Some(Self::Finalized), + "INSUFFICIENT_SIGNATURES" => Some(Self::InsufficientSignatures), + "DISPERSING" => Some(Self::Dispersing), + _ => None, + } + } +} +/// Generated client implementations. +pub mod disperser_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Disperser defines the public APIs for dispersing blobs. + #[derive(Debug, Clone)] + pub struct DisperserClient { + inner: tonic::client::Grpc, + } + impl DisperserClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl DisperserClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> DisperserClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + DisperserClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// This API accepts blob to disperse from clients. + /// This executes the dispersal async, i.e. it returns once the request + /// is accepted. The client could use GetBlobStatus() API to poll the the + /// processing status of the blob. + pub async fn disperse_blob( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/disperser.Disperser/DisperseBlob", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("disperser.Disperser", "DisperseBlob")); + self.inner.unary(req, path, codec).await + } + /// DisperseBlobAuthenticated is similar to DisperseBlob, except that it requires the + /// client to authenticate itself via the AuthenticationData message. The protoco is as follows: + /// 1. The client sends a DisperseBlobAuthenticated request with the DisperseBlobRequest message + /// 2. The Disperser sends back a BlobAuthHeader message containing information for the client to + /// verify and sign. + /// 3. The client verifies the BlobAuthHeader and sends back the signed BlobAuthHeader in an + /// AuthenticationData message. + /// 4. The Disperser verifies the signature and returns a DisperseBlobReply message. + pub async fn disperse_blob_authenticated( + &mut self, + request: impl tonic::IntoStreamingRequest< + Message = super::AuthenticatedRequest, + >, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/disperser.Disperser/DisperseBlobAuthenticated", + ); + let mut req = request.into_streaming_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("disperser.Disperser", "DisperseBlobAuthenticated"), + ); + self.inner.streaming(req, path, codec).await + } + /// This API is meant to be polled for the blob status. + pub async fn get_blob_status( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/disperser.Disperser/GetBlobStatus", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("disperser.Disperser", "GetBlobStatus")); + self.inner.unary(req, path, codec).await + } + /// This retrieves the requested blob from the Disperser's backend. + /// This is a more efficient way to retrieve blobs than directly retrieving + /// from the DA Nodes (see detail about this approach in + /// api/proto/retriever/retriever.proto). + /// The blob should have been initially dispersed via this Disperser service + /// for this API to work. + pub async fn retrieve_blob( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/disperser.Disperser/RetrieveBlob", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("disperser.Disperser", "RetrieveBlob")); + self.inner.unary(req, path, codec).await + } + } +} diff --git a/core/node/da_clients/src/eigen/mod.rs b/core/node/da_clients/src/eigen/mod.rs new file mode 100644 index 00000000000..699eae89424 --- /dev/null +++ b/core/node/da_clients/src/eigen/mod.rs @@ -0,0 +1,14 @@ +mod client; +mod sdk; + +pub use self::client::EigenClient; + +#[allow(clippy::all)] +pub(crate) mod disperser { + include!("generated/disperser.rs"); +} + +#[allow(clippy::all)] +pub(crate) mod common { + include!("generated/common.rs"); +} diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs new file mode 100644 index 00000000000..7ab7ea3ce33 --- /dev/null +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -0,0 +1,246 @@ +use std::{str::FromStr, time::Duration}; + +use secp256k1::{ecdsa::RecoverableSignature, SecretKey}; +use tokio::sync::mpsc; +use tokio_stream::{wrappers::ReceiverStream, StreamExt}; +use tonic::{ + transport::{Channel, ClientTlsConfig, Endpoint}, + Streaming, +}; + +use crate::eigen::{ + disperser, + disperser::{ + authenticated_request::Payload::{AuthenticationData, DisperseRequest}, + disperser_client::DisperserClient, + AuthenticatedReply, BlobAuthHeader, BlobVerificationProof, DisperseBlobReply, + }, +}; + +#[derive(Debug, Clone)] +pub struct RawEigenClient { + client: DisperserClient, + polling_interval: Duration, + private_key: SecretKey, + account_id: String, +} + +pub(crate) const DATA_CHUNK_SIZE: usize = 32; + +impl RawEigenClient { + pub(crate) const BUFFER_SIZE: usize = 1000; + + pub async fn new( + rpc_node_url: String, + inclusion_polling_interval_ms: u64, + private_key: SecretKey, + ) -> anyhow::Result { + let endpoint = + Endpoint::from_str(rpc_node_url.as_str())?.tls_config(ClientTlsConfig::new())?; + let client = DisperserClient::connect(endpoint) + .await + .map_err(|e| anyhow::anyhow!("Failed to connect to Disperser server: {}", e))?; + let polling_interval = Duration::from_millis(inclusion_polling_interval_ms); + + let account_id = get_account_id(&private_key); + + Ok(RawEigenClient { + client, + polling_interval, + private_key, + account_id, + }) + } + + pub async fn dispatch_blob(&self, data: Vec) -> anyhow::Result { + let mut client_clone = self.client.clone(); + let (tx, rx) = mpsc::channel(Self::BUFFER_SIZE); + + let response_stream = client_clone.disperse_blob_authenticated(ReceiverStream::new(rx)); + let padded_data = convert_by_padding_empty_byte(&data); + + // 1. send DisperseBlobRequest + self.disperse_data(padded_data, &tx).await?; + + // this await is blocked until the first response on the stream, so we only await after sending the `DisperseBlobRequest` + let mut response_stream = response_stream.await?.into_inner(); + + // 2. receive BlobAuthHeader + let blob_auth_header = self.receive_blob_auth_header(&mut response_stream).await?; + + // 3. sign and send BlobAuthHeader + self.submit_authentication_data(blob_auth_header.clone(), &tx) + .await?; + + // 4. receive DisperseBlobReply + let reply = response_stream + .next() + .await + .ok_or_else(|| anyhow::anyhow!("No response from server"))? + .unwrap() + .payload + .ok_or_else(|| anyhow::anyhow!("No payload in response"))?; + + let disperser::authenticated_reply::Payload::DisperseReply(disperse_reply) = reply else { + return Err(anyhow::anyhow!("Unexpected response from server")); + }; + + // 5. poll for blob status until it reaches the Confirmed state + let verification_proof = self + .await_for_inclusion(client_clone, disperse_reply) + .await?; + let blob_id = format!( + "{}:{}", + verification_proof.batch_id, verification_proof.blob_index + ); + tracing::info!("Blob dispatch confirmed, blob id: {}", blob_id); + + Ok(blob_id) + } + + async fn disperse_data( + &self, + data: Vec, + tx: &mpsc::Sender, + ) -> anyhow::Result<()> { + let req = disperser::AuthenticatedRequest { + payload: Some(DisperseRequest(disperser::DisperseBlobRequest { + data, + custom_quorum_numbers: vec![], + account_id: self.account_id.clone(), + })), + }; + + tx.send(req) + .await + .map_err(|e| anyhow::anyhow!("Failed to send DisperseBlobRequest: {}", e)) + } + + async fn submit_authentication_data( + &self, + blob_auth_header: BlobAuthHeader, + tx: &mpsc::Sender, + ) -> anyhow::Result<()> { + // TODO: replace challenge_parameter with actual auth header when it is available + let digest = zksync_basic_types::web3::keccak256( + &blob_auth_header.challenge_parameter.to_be_bytes(), + ); + let signature: RecoverableSignature = secp256k1::Secp256k1::signing_only() + .sign_ecdsa_recoverable( + &secp256k1::Message::from_slice(&digest[..])?, + &self.private_key, + ); + let (recovery_id, sig) = signature.serialize_compact(); + + let mut signature = Vec::with_capacity(65); + signature.extend_from_slice(&sig); + signature.push(recovery_id.to_i32() as u8); + + let req = disperser::AuthenticatedRequest { + payload: Some(AuthenticationData(disperser::AuthenticationData { + authentication_data: signature, + })), + }; + + tx.send(req) + .await + .map_err(|e| anyhow::anyhow!("Failed to send AuthenticationData: {}", e)) + } + + async fn receive_blob_auth_header( + &self, + response_stream: &mut Streaming, + ) -> anyhow::Result { + let reply = response_stream + .next() + .await + .ok_or_else(|| anyhow::anyhow!("No response from server"))?; + + let Ok(reply) = reply else { + return Err(anyhow::anyhow!("Err from server: {:?}", reply)); + }; + + let reply = reply + .payload + .ok_or_else(|| anyhow::anyhow!("No payload in response"))?; + + if let disperser::authenticated_reply::Payload::BlobAuthHeader(blob_auth_header) = reply { + Ok(blob_auth_header) + } else { + Err(anyhow::anyhow!("Unexpected response from server")) + } + } + + async fn await_for_inclusion( + &self, + mut client: DisperserClient, + disperse_blob_reply: DisperseBlobReply, + ) -> anyhow::Result { + let polling_request = disperser::BlobStatusRequest { + request_id: disperse_blob_reply.request_id, + }; + + loop { + tokio::time::sleep(self.polling_interval).await; + let resp = client + .get_blob_status(polling_request.clone()) + .await? + .into_inner(); + + match disperser::BlobStatus::try_from(resp.status)? { + disperser::BlobStatus::Processing | disperser::BlobStatus::Dispersing => {} + disperser::BlobStatus::Failed => { + return Err(anyhow::anyhow!("Blob dispatch failed")) + } + disperser::BlobStatus::InsufficientSignatures => { + return Err(anyhow::anyhow!("Insufficient signatures")) + } + disperser::BlobStatus::Confirmed | disperser::BlobStatus::Finalized => { + let verification_proof = resp + .info + .ok_or_else(|| anyhow::anyhow!("No blob header in response"))? + .blob_verification_proof + .ok_or_else(|| anyhow::anyhow!("No blob verification proof in response"))?; + + return Ok(verification_proof); + } + + _ => return Err(anyhow::anyhow!("Received unknown blob status")), + } + } + } +} + +fn get_account_id(secret_key: &SecretKey) -> String { + let public_key = + secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), secret_key); + let hex = hex::encode(public_key.serialize_uncompressed()); + + format!("0x{}", hex) +} + +fn convert_by_padding_empty_byte(data: &[u8]) -> Vec { + let parse_size = DATA_CHUNK_SIZE - 1; + + // Calculate the number of chunks + let data_len = (data.len() + parse_size - 1) / parse_size; + + // Pre-allocate `valid_data` with enough space for all chunks + let mut valid_data = vec![0u8; data_len * DATA_CHUNK_SIZE]; + let mut valid_end = data_len * DATA_CHUNK_SIZE; + + for (i, chunk) in data.chunks(parse_size).enumerate() { + let offset = i * DATA_CHUNK_SIZE; + valid_data[offset] = 0x00; // Set first byte of each chunk to 0x00 for big-endian compliance + + let copy_end = offset + 1 + chunk.len(); + valid_data[offset + 1..copy_end].copy_from_slice(chunk); + + if i == data_len - 1 && chunk.len() < parse_size { + valid_end = offset + 1 + chunk.len(); + } + } + + valid_data.truncate(valid_end); + valid_data +} diff --git a/core/node/da_clients/src/lib.rs b/core/node/da_clients/src/lib.rs index 48311ce4c3f..8a4c565a650 100644 --- a/core/node/da_clients/src/lib.rs +++ b/core/node/da_clients/src/lib.rs @@ -1,3 +1,6 @@ pub mod avail; +pub mod celestia; +pub mod eigen; pub mod no_da; pub mod object_store; +mod utils; diff --git a/core/node/da_clients/src/no_da.rs b/core/node/da_clients/src/no_da.rs index 2710c9ce9d9..db0557510ed 100644 --- a/core/node/da_clients/src/no_da.rs +++ b/core/node/da_clients/src/no_da.rs @@ -15,7 +15,7 @@ impl DataAvailabilityClient for NoDAClient { } async fn get_inclusion_data(&self, _: &str) -> Result, DAError> { - return Ok(Some(InclusionData::default())); + Ok(Some(InclusionData::default())) } fn clone_boxed(&self) -> Box { diff --git a/core/node/da_clients/src/utils.rs b/core/node/da_clients/src/utils.rs new file mode 100644 index 00000000000..d717d41f0e0 --- /dev/null +++ b/core/node/da_clients/src/utils.rs @@ -0,0 +1,15 @@ +use zksync_da_client::types::DAError; + +pub fn to_non_retriable_da_error(error: impl Into) -> DAError { + DAError { + error: error.into(), + is_retriable: false, + } +} + +pub fn to_retriable_da_error(error: impl Into) -> DAError { + DAError { + error: error.into(), + is_retriable: true, + } +} diff --git a/core/node/eth_watch/src/tests.rs b/core/node/eth_watch/src/tests.rs index d9faf7b664e..12ac8bdbf3f 100644 --- a/core/node/eth_watch/src/tests.rs +++ b/core/node/eth_watch/src/tests.rs @@ -620,6 +620,9 @@ async fn get_all_db_txs(storage: &mut Connection<'_, Core>) -> Vec .sync_mempool(&[], &[], 0, 0, 1000) .await .unwrap() + .into_iter() + .map(|x| x.0) + .collect() } fn tx_into_log(tx: L1Tx) -> Log { diff --git a/core/node/node_framework/src/implementations/layers/da_clients/celestia.rs b/core/node/node_framework/src/implementations/layers/da_clients/celestia.rs new file mode 100644 index 00000000000..69f5553d4da --- /dev/null +++ b/core/node/node_framework/src/implementations/layers/da_clients/celestia.rs @@ -0,0 +1,46 @@ +use zksync_config::{configs::da_client::celestia::CelestiaSecrets, CelestiaConfig}; +use zksync_da_client::DataAvailabilityClient; +use zksync_da_clients::celestia::CelestiaClient; + +use crate::{ + implementations::resources::da_client::DAClientResource, + wiring_layer::{WiringError, WiringLayer}, + IntoContext, +}; + +#[derive(Debug)] +pub struct CelestiaWiringLayer { + config: CelestiaConfig, + secrets: CelestiaSecrets, +} + +impl CelestiaWiringLayer { + pub fn new(config: CelestiaConfig, secrets: CelestiaSecrets) -> Self { + Self { config, secrets } + } +} + +#[derive(Debug, IntoContext)] +#[context(crate = crate)] +pub struct Output { + pub client: DAClientResource, +} + +#[async_trait::async_trait] +impl WiringLayer for CelestiaWiringLayer { + type Input = (); + type Output = Output; + + fn layer_name(&self) -> &'static str { + "celestia_client_layer" + } + + async fn wire(self, _input: Self::Input) -> Result { + let client: Box = + Box::new(CelestiaClient::new(self.config, self.secrets).await?); + + Ok(Self::Output { + client: DAClientResource(client), + }) + } +} diff --git a/core/node/node_framework/src/implementations/layers/da_clients/eigen.rs b/core/node/node_framework/src/implementations/layers/da_clients/eigen.rs new file mode 100644 index 00000000000..d5391ee433f --- /dev/null +++ b/core/node/node_framework/src/implementations/layers/da_clients/eigen.rs @@ -0,0 +1,46 @@ +use zksync_config::{configs::da_client::eigen::EigenSecrets, EigenConfig}; +use zksync_da_client::DataAvailabilityClient; +use zksync_da_clients::eigen::EigenClient; + +use crate::{ + implementations::resources::da_client::DAClientResource, + wiring_layer::{WiringError, WiringLayer}, + IntoContext, +}; + +#[derive(Debug)] +pub struct EigenWiringLayer { + config: EigenConfig, + secrets: EigenSecrets, +} + +impl EigenWiringLayer { + pub fn new(config: EigenConfig, secrets: EigenSecrets) -> Self { + Self { config, secrets } + } +} + +#[derive(Debug, IntoContext)] +#[context(crate = crate)] +pub struct Output { + pub client: DAClientResource, +} + +#[async_trait::async_trait] +impl WiringLayer for EigenWiringLayer { + type Input = (); + type Output = Output; + + fn layer_name(&self) -> &'static str { + "eigen_client_layer" + } + + async fn wire(self, _input: Self::Input) -> Result { + let client: Box = + Box::new(EigenClient::new(self.config, self.secrets).await?); + + Ok(Self::Output { + client: DAClientResource(client), + }) + } +} diff --git a/core/node/node_framework/src/implementations/layers/da_clients/mod.rs b/core/node/node_framework/src/implementations/layers/da_clients/mod.rs index 48311ce4c3f..c7865c74f3b 100644 --- a/core/node/node_framework/src/implementations/layers/da_clients/mod.rs +++ b/core/node/node_framework/src/implementations/layers/da_clients/mod.rs @@ -1,3 +1,5 @@ pub mod avail; +pub mod celestia; +pub mod eigen; pub mod no_da; pub mod object_store; diff --git a/core/node/node_sync/src/external_io.rs b/core/node/node_sync/src/external_io.rs index 1be7e00543f..0f5f4d6253f 100644 --- a/core/node/node_sync/src/external_io.rs +++ b/core/node/node_sync/src/external_io.rs @@ -362,6 +362,7 @@ impl StateKeeperIO for ExternalIO { async fn wait_for_next_tx( &mut self, max_wait: Duration, + _l2_block_timestamp: u64, ) -> anyhow::Result> { tracing::debug!( "Waiting for the new tx, next action is {:?}", diff --git a/core/node/state_keeper/Cargo.toml b/core/node/state_keeper/Cargo.toml index 0e924b9f066..75d7c9f1e94 100644 --- a/core/node/state_keeper/Cargo.toml +++ b/core/node/state_keeper/Cargo.toml @@ -32,6 +32,7 @@ zksync_vm_executor.workspace = true zksync_system_constants.workspace = true zksync_base_token_adjuster.workspace = true + anyhow.workspace = true async-trait.workspace = true tokio = { workspace = true, features = ["time"] } diff --git a/core/node/state_keeper/src/io/common/tests.rs b/core/node/state_keeper/src/io/common/tests.rs index ec9f906b1cd..2298d4c2ee7 100644 --- a/core/node/state_keeper/src/io/common/tests.rs +++ b/core/node/state_keeper/src/io/common/tests.rs @@ -9,7 +9,7 @@ use futures::FutureExt; use zksync_config::GenesisConfig; use zksync_contracts::BaseSystemContractsHashes; use zksync_dal::{ConnectionPool, Core}; -use zksync_multivm::interface::TransactionExecutionMetrics; +use zksync_multivm::interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use zksync_node_genesis::{insert_genesis_batch, mock_genesis_config, GenesisParams}; use zksync_node_test_utils::{ create_l1_batch, create_l2_block, create_l2_transaction, execute_l2_transaction, @@ -355,7 +355,11 @@ async fn store_pending_l2_blocks( let tx = create_l2_transaction(10, 100); storage .transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); let mut new_l2_block = create_l2_block(l2_block_number); diff --git a/core/node/state_keeper/src/io/mempool.rs b/core/node/state_keeper/src/io/mempool.rs index dfddd36aba7..370d46fd544 100644 --- a/core/node/state_keeper/src/io/mempool.rs +++ b/core/node/state_keeper/src/io/mempool.rs @@ -278,6 +278,7 @@ impl StateKeeperIO for MempoolIO { async fn wait_for_next_tx( &mut self, max_wait: Duration, + l2_block_timestamp: u64, ) -> anyhow::Result> { let started_at = Instant::now(); while started_at.elapsed() <= max_wait { @@ -285,7 +286,7 @@ impl StateKeeperIO for MempoolIO { let maybe_tx = self.mempool.next_transaction(&self.filter); get_latency.observe(); - if let Some(tx) = maybe_tx { + if let Some((tx, constraint)) = maybe_tx { // Reject transactions with too big gas limit. They are also rejected on the API level, but // we need to secure ourselves in case some tx will somehow get into mempool. if tx.gas_limit() > self.max_allowed_tx_gas_limit { @@ -298,6 +299,23 @@ impl StateKeeperIO for MempoolIO { .await?; continue; } + + // Reject transactions that violate block.timestamp constraints. Such transactions should be + // rejected at the API level, but we need to protect ourselves in case if a transaction + // goes outside of the allowed range while being in the mempool + let matches_range = constraint + .timestamp_asserter_range + .map_or(true, |x| x.contains(&l2_block_timestamp)); + + if !matches_range { + self.reject( + &tx, + UnexecutableReason::Halt(Halt::FailedBlockTimestampAssertion), + ) + .await?; + continue; + } + return Ok(Some(tx)); } else { tokio::time::sleep(self.delay_interval).await; @@ -309,9 +327,9 @@ impl StateKeeperIO for MempoolIO { async fn rollback(&mut self, tx: Transaction) -> anyhow::Result<()> { // Reset nonces in the mempool. - self.mempool.rollback(&tx); + let constraint = self.mempool.rollback(&tx); // Insert the transaction back. - self.mempool.insert(vec![tx], HashMap::new()); + self.mempool.insert(vec![(tx, constraint)], HashMap::new()); Ok(()) } diff --git a/core/node/state_keeper/src/io/mod.rs b/core/node/state_keeper/src/io/mod.rs index e2461e72d7b..fbc481fb678 100644 --- a/core/node/state_keeper/src/io/mod.rs +++ b/core/node/state_keeper/src/io/mod.rs @@ -137,8 +137,11 @@ pub trait StateKeeperIO: 'static + Send + Sync + fmt::Debug + IoSealCriteria { /// Blocks for up to `max_wait` until the next transaction is available for execution. /// Returns `None` if no transaction became available until the timeout. - async fn wait_for_next_tx(&mut self, max_wait: Duration) - -> anyhow::Result>; + async fn wait_for_next_tx( + &mut self, + max_wait: Duration, + l2_block_timestamp: u64, + ) -> anyhow::Result>; /// Marks the transaction as "not executed", so it can be retrieved from the IO again. async fn rollback(&mut self, tx: Transaction) -> anyhow::Result<()>; /// Marks the transaction as "rejected", e.g. one that is not correct and can't be executed. diff --git a/core/node/state_keeper/src/io/seal_logic/l2_block_seal_subtasks.rs b/core/node/state_keeper/src/io/seal_logic/l2_block_seal_subtasks.rs index 4fc58bce5c9..53871c54a19 100644 --- a/core/node/state_keeper/src/io/seal_logic/l2_block_seal_subtasks.rs +++ b/core/node/state_keeper/src/io/seal_logic/l2_block_seal_subtasks.rs @@ -458,7 +458,7 @@ impl L2BlockSealSubtask for InsertL2ToL1LogsSubtask { mod tests { use zksync_dal::{ConnectionPool, Core}; use zksync_multivm::{ - interface::{TransactionExecutionResult, TxExecutionStatus}, + interface::{tracer::ValidationTraces, TransactionExecutionResult, TxExecutionStatus}, utils::{get_max_batch_gas_limit, get_max_gas_per_pubdata_byte}, zk_evm_latest::ethereum_types::H256, VmVersion, @@ -487,7 +487,7 @@ mod tests { .await .unwrap() .transactions_dal() - .insert_transaction_l2(&tx, Default::default()) + .insert_transaction_l2(&tx, Default::default(), ValidationTraces::default()) .await .unwrap(); let tx_hash = tx.hash(); diff --git a/core/node/state_keeper/src/io/tests/mod.rs b/core/node/state_keeper/src/io/tests/mod.rs index ece5b67767f..adef238fe92 100644 --- a/core/node/state_keeper/src/io/tests/mod.rs +++ b/core/node/state_keeper/src/io/tests/mod.rs @@ -1,11 +1,16 @@ -use std::{collections::HashMap, time::Duration}; +use std::{ + collections::HashMap, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; use test_casing::test_casing; use zksync_contracts::BaseSystemContractsHashes; -use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_mempool::L2TxFilter; use zksync_multivm::{ - interface::{TransactionExecutionMetrics, VmEvent, VmExecutionMetrics}, + interface::{ + tracer::ValidationTraces, TransactionExecutionMetrics, VmEvent, VmExecutionMetrics, + }, utils::derive_base_fee_and_gas_per_pubdata, }; use zksync_node_test_utils::prepare_recovery_snapshot; @@ -13,8 +18,9 @@ use zksync_types::{ block::{BlockGasCount, L2BlockHasher}, commitment::L1BatchCommitmentMode, fee_model::{BatchFeeInput, PubdataIndependentBatchFeeModelInput}, + l2::L2Tx, AccountTreeId, Address, L1BatchNumber, L2BlockNumber, L2ChainId, ProtocolVersion, - ProtocolVersionId, StorageKey, H256, U256, + ProtocolVersionId, StorageKey, TransactionTimeRangeConstraint, H256, U256, }; use zksync_utils::time::seconds_since_epoch; @@ -130,6 +136,7 @@ async fn test_filter_with_no_pending_batch(commitment_mode: L1BatchCommitmentMod &mut guard, want_filter.fee_per_gas, want_filter.gas_per_pubdata, + TransactionTimeRangeConstraint::default(), ); // Now, given that there is a transaction matching the expected filter, waiting for the new batch params @@ -169,7 +176,12 @@ async fn test_timestamps_are_distinct( ) .await .unwrap(); - tester.insert_tx(&mut guard, tx_filter.fee_per_gas, tx_filter.gas_per_pubdata); + tester.insert_tx( + &mut guard, + tx_filter.fee_per_gas, + tx_filter.gas_per_pubdata, + TransactionTimeRangeConstraint::default(), + ); let l1_batch_params = mempool .wait_for_new_batch_params(&io_cursor, Duration::from_secs(10)) @@ -431,10 +443,15 @@ async fn l2_block_processing_after_snapshot_recovery(commitment_mode: L1BatchCom &mut mempool_guard, tx_filter.fee_per_gas, tx_filter.gas_per_pubdata, + TransactionTimeRangeConstraint::default(), ); storage .transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); @@ -584,10 +601,15 @@ async fn continue_unsealed_batch_on_restart(commitment_mode: L1BatchCommitmentMo &mut mempool_guard, tx_filter.fee_per_gas, tx_filter.gas_per_pubdata, + TransactionTimeRangeConstraint::default(), ); storage .transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); @@ -644,3 +666,118 @@ async fn insert_unsealed_batch_on_init(commitment_mode: L1BatchCommitmentMode) { assert_eq!(l1_batch_params.fee_input, fee_input); assert_eq!(l1_batch_params.first_l2_block.timestamp, 2); } + +#[tokio::test] +async fn test_mempool_with_timestamp_assertion() { + let connection_pool = ConnectionPool::::constrained_test_pool(2).await; + // what commitment mode to use is irrelevant here + let tester = Tester::new(L1BatchCommitmentMode::Rollup); + let mut storage = connection_pool.connection().await.unwrap(); + + tester.genesis(&connection_pool).await; + + // Insert a sealed batch so there will be a `prev_l1_batch_state_root`. + // These gas values are random and don't matter for filter calculation. + let tx_result = tester + .insert_l2_block(&connection_pool, 1, 5, BatchFeeInput::l1_pegged(55, 555)) + .await; + tester + .insert_sealed_batch(&connection_pool, 1, &[tx_result]) + .await; + + // Create a copy of the tx filter that the mempool will use. + let want_filter = l2_tx_filter( + &tester.create_batch_fee_input_provider().await, + ProtocolVersionId::latest().into(), + ) + .await + .unwrap(); + + // Create a mempool without pending batch and ensure that filter is not initialized just yet. + let (mut mempool, mut guard) = tester.create_test_mempool_io(connection_pool).await; + mempool.initialize().await.unwrap(); + assert_eq!(mempool.filter(), &L2TxFilter::default()); + + let system_time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(); + + // inserting 3 transactions - a good one, sandwiched in between two bad ones. The good one should + // be returned by wait_for_next_tx, while two bad ones should be rejected. + let rejected_tx_1 = tester.insert_tx( + &mut guard, + want_filter.fee_per_gas, + want_filter.gas_per_pubdata, + TransactionTimeRangeConstraint { + timestamp_asserter_range: Some(system_time - 20000..system_time - 10000), + }, + ); + let expected_tx = tester.insert_tx( + &mut guard, + want_filter.fee_per_gas, + want_filter.gas_per_pubdata, + TransactionTimeRangeConstraint { + timestamp_asserter_range: Some(system_time - 1000..system_time + 1000), + }, + ); + let rejected_tx_2 = tester.insert_tx( + &mut guard, + want_filter.fee_per_gas, + want_filter.gas_per_pubdata, + TransactionTimeRangeConstraint { + timestamp_asserter_range: Some(system_time + 10000..system_time + 20000), + }, + ); + insert_l2_transaction(&mut storage, &rejected_tx_1).await; + insert_l2_transaction(&mut storage, &expected_tx).await; + insert_l2_transaction(&mut storage, &rejected_tx_2).await; + + let tx = mempool + .wait_for_next_tx(Duration::from_secs(2), system_time) + .await + .unwrap() + .expect("No expected transaction in the mempool"); + assert_eq!(expected_tx.hash(), tx.hash()); + + let next_tx = mempool + .wait_for_next_tx(Duration::from_secs(2), system_time) + .await + .expect("Should be no more transactions in the mempool"); + assert!(next_tx.is_none()); + + // verify that two transactions have been rejected + let rejected_storage_tx_1 = storage + .transactions_dal() + .get_storage_tx_by_hash(rejected_tx_1.hash()) + .await + .unwrap() + .expect("Failed to find transaction"); + assert_eq!( + "rejected: Transaction failed block.timestamp assertion", + rejected_storage_tx_1.error.unwrap() + ); + + let rejected_storage_tx_2 = storage + .transactions_dal() + .get_storage_tx_by_hash(rejected_tx_2.hash()) + .await + .unwrap() + .expect("Failed to find transaction"); + assert_eq!( + "rejected: Transaction failed block.timestamp assertion", + rejected_storage_tx_2.error.unwrap() + ); +} + +async fn insert_l2_transaction(storage: &mut Connection<'_, Core>, tx: &L2Tx) { + storage + .transactions_dal() + .insert_transaction_l2( + tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) + .await + .unwrap(); +} diff --git a/core/node/state_keeper/src/io/tests/tester.rs b/core/node/state_keeper/src/io/tests/tester.rs index daedbebc75e..32a746eecdf 100644 --- a/core/node/state_keeper/src/io/tests/tester.rs +++ b/core/node/state_keeper/src/io/tests/tester.rs @@ -11,7 +11,9 @@ use zksync_contracts::BaseSystemContracts; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_eth_client::{clients::MockSettlementLayer, BaseFees}; use zksync_multivm::{ - interface::{TransactionExecutionMetrics, TransactionExecutionResult}, + interface::{ + tracer::ValidationTraces, TransactionExecutionMetrics, TransactionExecutionResult, + }, vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, }; use zksync_node_fee_model::{ @@ -30,7 +32,8 @@ use zksync_types::{ protocol_version::{L1VerifierConfig, ProtocolSemanticVersion}, pubdata_da::PubdataSendingMode, system_contracts::get_system_smart_contracts, - L2BlockNumber, L2ChainId, PriorityOpId, ProtocolVersionId, H256, + L2BlockNumber, L2ChainId, PriorityOpId, ProtocolVersionId, TransactionTimeRangeConstraint, + H256, }; use crate::{MempoolGuard, MempoolIO}; @@ -188,7 +191,11 @@ impl Tester { let tx = create_l2_transaction(10, 100); storage .transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); storage @@ -252,9 +259,10 @@ impl Tester { guard: &mut MempoolGuard, fee_per_gas: u64, gas_per_pubdata: u32, + constraint: TransactionTimeRangeConstraint, ) -> L2Tx { let tx = create_l2_transaction(fee_per_gas, gas_per_pubdata.into()); - guard.insert(vec![tx.clone().into()], Default::default()); + guard.insert(vec![(tx.clone().into(), constraint)], Default::default()); tx } } diff --git a/core/node/state_keeper/src/keeper.rs b/core/node/state_keeper/src/keeper.rs index 523dd8eceba..60e20603899 100644 --- a/core/node/state_keeper/src/keeper.rs +++ b/core/node/state_keeper/src/keeper.rs @@ -589,11 +589,10 @@ impl ZkSyncStateKeeper { Self::start_next_l2_block(new_l2_block_params, updates_manager, batch_executor) .await?; } - let waiting_latency = KEEPER_METRICS.waiting_for_tx.start(); let Some(tx) = self .io - .wait_for_next_tx(POLL_WAIT_DURATION) + .wait_for_next_tx(POLL_WAIT_DURATION, updates_manager.l2_block.timestamp) .instrument(info_span!("wait_for_next_tx")) .await .context("error waiting for next transaction")? diff --git a/core/node/state_keeper/src/mempool_actor.rs b/core/node/state_keeper/src/mempool_actor.rs index a17f2670cbb..8e9d674f878 100644 --- a/core/node/state_keeper/src/mempool_actor.rs +++ b/core/node/state_keeper/src/mempool_actor.rs @@ -111,7 +111,7 @@ impl MempoolFetcher { (filter.fee_per_gas, filter.gas_per_pubdata) }; - let transactions = storage + let transactions_with_constraints = storage .transactions_dal() .sync_mempool( &mempool_info.stashed_accounts, @@ -122,16 +122,22 @@ impl MempoolFetcher { ) .await .context("failed syncing mempool")?; + + let transactions: Vec<_> = transactions_with_constraints + .iter() + .map(|(t, _c)| t) + .collect(); + let nonces = get_transaction_nonces(&mut storage, &transactions).await?; drop(storage); #[cfg(test)] { - let transaction_hashes = transactions.iter().map(Transaction::hash).collect(); + let transaction_hashes = transactions.iter().map(|x| x.hash()).collect(); self.transaction_hashes_sender.send(transaction_hashes).ok(); } let all_transactions_loaded = transactions.len() < self.sync_batch_size; - self.mempool.insert(transactions, nonces); + self.mempool.insert(transactions_with_constraints, nonces); latency.observe(); if all_transactions_loaded { @@ -145,7 +151,7 @@ impl MempoolFetcher { /// Loads nonces for all distinct `transactions` initiators from the storage. async fn get_transaction_nonces( storage: &mut Connection<'_, Core>, - transactions: &[Transaction], + transactions: &[&Transaction], ) -> anyhow::Result> { let (nonce_keys, address_by_nonce_key): (Vec<_>, HashMap<_, _>) = transactions .iter() @@ -173,7 +179,7 @@ async fn get_transaction_nonces( #[cfg(test)] mod tests { - use zksync_multivm::interface::TransactionExecutionMetrics; + use zksync_multivm::interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; use zksync_node_fee_model::MockBatchFeeParamsProvider; use zksync_node_genesis::{insert_genesis_batch, GenesisParams}; use zksync_node_test_utils::create_l2_transaction; @@ -215,7 +221,7 @@ mod tests { let nonces = get_transaction_nonces( &mut storage, - &[transaction.into(), other_transaction.into()], + &[&transaction.into(), &other_transaction.into()], ) .await .unwrap(); @@ -261,7 +267,11 @@ mod tests { let mut storage = pool.connection().await.unwrap(); storage .transactions_dal() - .insert_transaction_l2(&transaction, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &transaction, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); drop(storage); @@ -317,7 +327,11 @@ mod tests { let mut storage = pool.connection().await.unwrap(); storage .transactions_dal() - .insert_transaction_l2(&transaction, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &transaction, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); drop(storage); @@ -370,7 +384,11 @@ mod tests { .unwrap(); storage .transactions_dal() - .insert_transaction_l2(&transaction, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &transaction, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await .unwrap(); drop(storage); diff --git a/core/node/state_keeper/src/seal_criteria/mod.rs b/core/node/state_keeper/src/seal_criteria/mod.rs index 962cc807318..b82d61666fb 100644 --- a/core/node/state_keeper/src/seal_criteria/mod.rs +++ b/core/node/state_keeper/src/seal_criteria/mod.rs @@ -54,6 +54,7 @@ fn halt_as_metric_label(halt: &Halt) -> &'static str { Halt::VMPanic => "VMPanic", Halt::TracerCustom(_) => "TracerCustom", Halt::FailedToPublishCompressedBytecodes => "FailedToPublishCompressedBytecodes", + Halt::FailedBlockTimestampAssertion => "FailedBlockTimestampAssertion", } } diff --git a/core/node/state_keeper/src/testonly/test_batch_executor.rs b/core/node/state_keeper/src/testonly/test_batch_executor.rs index 45787b18f3c..5fe05167504 100644 --- a/core/node/state_keeper/src/testonly/test_batch_executor.rs +++ b/core/node/state_keeper/src/testonly/test_batch_executor.rs @@ -731,6 +731,7 @@ impl StateKeeperIO for TestIO { async fn wait_for_next_tx( &mut self, max_wait: Duration, + _l2_block_timestamp: u64, ) -> anyhow::Result> { let action = self.pop_next_item("wait_for_next_tx"); diff --git a/core/node/state_keeper/src/types.rs b/core/node/state_keeper/src/types.rs index e112871a647..db18e32e096 100644 --- a/core/node/state_keeper/src/types.rs +++ b/core/node/state_keeper/src/types.rs @@ -6,7 +6,9 @@ use std::{ use zksync_dal::{Connection, Core, CoreDal}; use zksync_mempool::{L2TxFilter, MempoolInfo, MempoolStore}; use zksync_multivm::interface::{VmExecutionMetrics, VmExecutionResultAndLogs}; -use zksync_types::{block::BlockGasCount, Address, Nonce, PriorityOpId, Transaction}; +use zksync_types::{ + block::BlockGasCount, Address, Nonce, PriorityOpId, Transaction, TransactionTimeRangeConstraint, +}; use super::{ metrics::StateKeeperGauges, @@ -30,13 +32,32 @@ impl MempoolGuard { Self(Arc::new(Mutex::new(store))) } - pub fn insert(&mut self, transactions: Vec, nonces: HashMap) { + pub fn insert( + &mut self, + transactions: Vec<(Transaction, TransactionTimeRangeConstraint)>, + nonces: HashMap, + ) { self.0 .lock() .expect("failed to acquire mempool lock") .insert(transactions, nonces); } + #[cfg(test)] + pub fn insert_without_constraint( + &mut self, + transactions: Vec, + nonces: HashMap, + ) { + self.insert( + transactions + .into_iter() + .map(|x| (x, TransactionTimeRangeConstraint::default())) + .collect(), + nonces, + ); + } + pub fn has_next(&self, filter: &L2TxFilter) -> bool { self.0 .lock() @@ -44,18 +65,21 @@ impl MempoolGuard { .has_next(filter) } - pub fn next_transaction(&mut self, filter: &L2TxFilter) -> Option { + pub fn next_transaction( + &mut self, + filter: &L2TxFilter, + ) -> Option<(Transaction, TransactionTimeRangeConstraint)> { self.0 .lock() .expect("failed to acquire mempool lock") .next_transaction(filter) } - pub fn rollback(&mut self, rejected: &Transaction) { + pub fn rollback(&mut self, rejected: &Transaction) -> TransactionTimeRangeConstraint { self.0 .lock() .expect("failed to acquire mempool lock") - .rollback(rejected); + .rollback(rejected) } pub fn get_mempool_info(&mut self) -> MempoolInfo { diff --git a/core/node/vm_runner/src/tests/mod.rs b/core/node/vm_runner/src/tests/mod.rs index 575fd59be04..a3438d5a4e1 100644 --- a/core/node/vm_runner/src/tests/mod.rs +++ b/core/node/vm_runner/src/tests/mod.rs @@ -20,7 +20,9 @@ use zksync_types::{ StorageLog, StorageLogKind, StorageValue, H160, H256, L2_BASE_TOKEN_ADDRESS, U256, }; use zksync_utils::{bytecode::hash_bytecode, h256_to_u256, u256_to_h256}; -use zksync_vm_interface::{L1BatchEnv, L2BlockEnv, SystemEnv, TransactionExecutionMetrics}; +use zksync_vm_interface::{ + tracer::ValidationTraces, L1BatchEnv, L2BlockEnv, SystemEnv, TransactionExecutionMetrics, +}; use super::*; @@ -242,7 +244,11 @@ async fn store_l1_batches( let account = accounts.choose_mut(&mut rng).unwrap(); let tx = create_l2_transaction(account, 1000000, 100); conn.transactions_dal() - .insert_transaction_l2(&tx, TransactionExecutionMetrics::default()) + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) .await?; let mut logs = Vec::new(); let mut written_keys = Vec::new(); diff --git a/core/tests/ts-integration/contracts/custom-account/custom-account.sol b/core/tests/ts-integration/contracts/custom-account/custom-account.sol index fc90355ac64..99177212477 100644 --- a/core/tests/ts-integration/contracts/custom-account/custom-account.sol +++ b/core/tests/ts-integration/contracts/custom-account/custom-account.sol @@ -9,6 +9,10 @@ import './SystemContractsCaller.sol'; import './interfaces/IAccount.sol'; +interface ITimestampAsserter { + function assertTimestampInRange(uint256 start, uint256 end) external view; +} + contract CustomAccount is IAccount { event BootloaderBalance(uint256); @@ -18,15 +22,28 @@ contract CustomAccount is IAccount { uint256 public gasToSpent; bytes32 public lastTxHash; + address public timestampAsserterAddress; + uint256 public timestampAsserterRangeStart; + uint256 public timestampAsserterRangeEnd; + - constructor(bool _violateValidationRules) { + constructor(bool _violateValidationRules, address _timestampAsserterAddress, uint256 _timestampAsserterRangeStart, uint256 _timestampAsserterRangeEnd) { violateValidationRules = _violateValidationRules; + timestampAsserterAddress = _timestampAsserterAddress; + timestampAsserterRangeStart = _timestampAsserterRangeStart; + timestampAsserterRangeEnd = _timestampAsserterRangeEnd; } // bytes4(keccak256("isValidSignature(bytes32,bytes)") bytes4 constant EIP1271_SUCCESS_RETURN_VALUE = 0x1626ba7e; function validateTransaction(bytes32 _txHash, bytes32 _suggestedSignedTxHash, Transaction calldata _transaction) external payable override returns (bytes4 magic) { + ITimestampAsserter timestampAsserter = ITimestampAsserter(timestampAsserterAddress); + // This assertion exists to ensure that block.timestamp can be accessed in AA by using + // ITimestampAsserter contract + + timestampAsserter.assertTimestampInRange(timestampAsserterRangeStart, timestampAsserterRangeEnd); + magic = _validateTransaction(_suggestedSignedTxHash, _transaction); lastTxHash = _txHash; diff --git a/core/tests/ts-integration/src/env.ts b/core/tests/ts-integration/src/env.ts index 1de917c2362..b91fcd09f0a 100644 --- a/core/tests/ts-integration/src/env.ts +++ b/core/tests/ts-integration/src/env.ts @@ -87,7 +87,7 @@ async function loadTestEnvironmentFromFile(fileConfig: FileConfig): Promise { ); const healthcheckPort = process.env.API_HEALTHCHECK_PORT ?? '3071'; + if (!process.env.CONTRACTS_L2_TIMESTAMP_ASSERTER_ADDR) { + throw new Error('CONTRACTS_L2_TIMESTAMP_ASSERTER_ADDR is not defined'); + } + const timestampAsserterAddress = process.env.CONTRACTS_L2_TIMESTAMP_ASSERTER_ADDR.toString(); + + const timestampAsserterMinTimeTillEndSec = parseInt(process.env.TIMESTAMP_ASSERTER_MIN_TIME_TILL_END_SEC!); + return { maxLogsLimit, pathToHome, @@ -312,7 +324,9 @@ export async function loadTestEnvironmentFromEnv(): Promise { decimals: baseToken?.decimals || token.decimals, l1Address: baseToken?.address || token.address, l2Address: baseTokenAddressL2 - } + }, + timestampAsserterAddress, + timestampAsserterMinTimeTillEndSec }; } diff --git a/core/tests/ts-integration/src/types.ts b/core/tests/ts-integration/src/types.ts index c513480c1b4..014031a3dd7 100644 --- a/core/tests/ts-integration/src/types.ts +++ b/core/tests/ts-integration/src/types.ts @@ -94,6 +94,8 @@ export interface TestEnvironment { */ baseToken: Token; healthcheckPort: string; + timestampAsserterAddress: string; + timestampAsserterMinTimeTillEndSec: number; } /** diff --git a/core/tests/ts-integration/src/utils.ts b/core/tests/ts-integration/src/utils.ts index bb6fa93757e..f8378c8dff0 100644 --- a/core/tests/ts-integration/src/utils.ts +++ b/core/tests/ts-integration/src/utils.ts @@ -1,17 +1,12 @@ import { spawn as _spawn, ChildProcessWithoutNullStreams, type ProcessEnvOptions } from 'child_process'; import { assert } from 'chai'; -import { FileConfig } from 'utils/build/file-configs'; +import { FileConfig, getConfigPath } from 'utils/build/file-configs'; import { killPidWithAllChilds } from 'utils/build/kill'; import * as utils from 'utils'; import fs from 'node:fs/promises'; import * as zksync from 'zksync-ethers'; -import { - deleteInternalEnforcedL1GasPrice, - deleteInternalEnforcedPubdataPrice, - setInternalEnforcedL1GasPrice, - setInternalEnforcedPubdataPrice, - setTransactionSlots -} from '../tests/utils'; +import * as fsSync from 'fs'; +import YAML from 'yaml'; // executes a command in background and returns a child process handle // by default pipes data to parent's stdio but this can be overridden @@ -105,48 +100,149 @@ export class Node { } } +interface MainNodeOptions { + newL1GasPrice?: bigint; + newPubdataPrice?: bigint; + customBaseToken?: boolean; + externalPriceApiClientForcedNumerator?: number; + externalPriceApiClientForcedDenominator?: number; + externalPriceApiClientForcedFluctuation?: number; + baseTokenPricePollingIntervalMs?: number; + baseTokenAdjusterL1UpdateDeviationPercentage?: number; +} export class NodeSpawner { + private readonly generalConfigPath: string | undefined; + private readonly originalConfig: string | undefined; + public mainNode: Node | null; + public constructor( private readonly pathToHome: string, private readonly logs: fs.FileHandle, private readonly fileConfig: FileConfig, private readonly options: MainNodeSpawnOptions, private env?: ProcessEnvOptions['env'] - ) {} + ) { + this.mainNode = null; + if (fileConfig.loadFromFile) { + this.generalConfigPath = getConfigPath({ + pathToHome, + chain: fileConfig.chain, + configsFolder: 'configs', + config: 'general.yaml' + }); + this.originalConfig = fsSync.readFileSync(this.generalConfigPath, 'utf8'); + } + } + + public async killAndSpawnMainNode(configOverrides: MainNodeOptions | null = null): Promise { + if (this.mainNode != null) { + await this.mainNode.killAndWaitForShutdown(); + this.mainNode = null; + } + this.mainNode = await this.spawnMainNode(configOverrides); + } - public async spawnMainNode(newL1GasPrice?: string, newPubdataPrice?: string): Promise> { + private async spawnMainNode(overrides: MainNodeOptions | null): Promise> { const env = this.env ?? process.env; const { fileConfig, pathToHome, options, logs } = this; - const testMode = newPubdataPrice || newL1GasPrice; + const testMode = overrides?.newPubdataPrice != null || overrides?.newL1GasPrice != null; - console.log('New L1 Gas Price: ', newL1GasPrice); - console.log('New Pubdata Price: ', newPubdataPrice); + console.log('Overrides: ', overrides); if (fileConfig.loadFromFile) { - setTransactionSlots(pathToHome, fileConfig, testMode ? 1 : 8192); + this.restoreConfig(); + const config = this.readFileConfig(); + config['state_keeper']['transaction_slots'] = testMode ? 1 : 8192; - if (newL1GasPrice) { - setInternalEnforcedL1GasPrice(pathToHome, fileConfig, parseFloat(newL1GasPrice)); - } else { - deleteInternalEnforcedL1GasPrice(pathToHome, fileConfig); - } + if (overrides != null) { + if (overrides.newL1GasPrice) { + config['eth']['gas_adjuster']['internal_enforced_l1_gas_price'] = overrides.newL1GasPrice; + } + + if (overrides.newPubdataPrice) { + config['eth']['gas_adjuster']['internal_enforced_pubdata_price'] = overrides.newPubdataPrice; + } + + if (overrides.externalPriceApiClientForcedNumerator !== undefined) { + config['external_price_api_client']['forced_numerator'] = + overrides.externalPriceApiClientForcedNumerator; + } + + if (overrides.externalPriceApiClientForcedDenominator !== undefined) { + config['external_price_api_client']['forced_denominator'] = + overrides.externalPriceApiClientForcedDenominator; + } + + if (overrides.externalPriceApiClientForcedFluctuation !== undefined) { + config['external_price_api_client']['forced_fluctuation'] = + overrides.externalPriceApiClientForcedFluctuation; + } + + if (overrides.baseTokenPricePollingIntervalMs !== undefined) { + const cacheUpdateInterval = overrides.baseTokenPricePollingIntervalMs / 2; + // To reduce price polling interval we also need to reduce base token receipt checking and tx sending sleeps as they are blocking the poller. Also cache update needs to be reduced appropriately. + + config['base_token_adjuster']['l1_receipt_checking_sleep_ms'] = + overrides.baseTokenPricePollingIntervalMs; + config['base_token_adjuster']['l1_tx_sending_sleep_ms'] = overrides.baseTokenPricePollingIntervalMs; + config['base_token_adjuster']['price_polling_interval_ms'] = + overrides.baseTokenPricePollingIntervalMs; + config['base_token_adjuster']['price_cache_update_interval_ms'] = cacheUpdateInterval; + } - if (newPubdataPrice) { - setInternalEnforcedPubdataPrice(pathToHome, fileConfig, parseFloat(newPubdataPrice)); - } else { - deleteInternalEnforcedPubdataPrice(pathToHome, fileConfig); + if (overrides.baseTokenAdjusterL1UpdateDeviationPercentage !== undefined) { + config['base_token_adjuster']['l1_update_deviation_percentage'] = + overrides.baseTokenAdjusterL1UpdateDeviationPercentage; + } } + + this.writeFileConfig(config); } else { env['DATABASE_MERKLE_TREE_MODE'] = 'full'; - if (newPubdataPrice) { - env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_PUBDATA_PRICE'] = newPubdataPrice; - } + if (overrides != null) { + if (overrides.newPubdataPrice) { + env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_PUBDATA_PRICE'] = + overrides.newPubdataPrice.toString(); + } - if (newL1GasPrice) { - // We need to ensure that each transaction gets into its own batch for more fair comparison. - env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_L1_GAS_PRICE'] = newL1GasPrice; + if (overrides.newL1GasPrice) { + // We need to ensure that each transaction gets into its own batch for more fair comparison. + env['ETH_SENDER_GAS_ADJUSTER_INTERNAL_ENFORCED_L1_GAS_PRICE'] = overrides.newL1GasPrice.toString(); + } + + if (overrides.externalPriceApiClientForcedNumerator !== undefined) { + env['EXTERNAL_PRICE_API_CLIENT_FORCED_NUMERATOR'] = + overrides.externalPriceApiClientForcedNumerator.toString(); + } + + if (overrides.externalPriceApiClientForcedDenominator !== undefined) { + env['EXTERNAL_PRICE_API_CLIENT_FORCED_DENOMINATOR'] = + overrides.externalPriceApiClientForcedDenominator.toString(); + } + + if (overrides.externalPriceApiClientForcedFluctuation !== undefined) { + env['EXTERNAL_PRICE_API_CLIENT_FORCED_FLUCTUATION'] = + overrides.externalPriceApiClientForcedFluctuation.toString(); + } + + if (overrides.baseTokenPricePollingIntervalMs !== undefined) { + const cacheUpdateInterval = overrides.baseTokenPricePollingIntervalMs / 2; + // To reduce price polling interval we also need to reduce base token receipt checking and tx sending sleeps as they are blocking the poller. Also cache update needs to be reduced appropriately. + env['BASE_TOKEN_ADJUSTER_L1_RECEIPT_CHECKING_SLEEP_MS'] = + overrides.baseTokenPricePollingIntervalMs.toString(); + env['BASE_TOKEN_ADJUSTER_L1_TX_SENDING_SLEEP_MS'] = + overrides.baseTokenPricePollingIntervalMs.toString(); + env['BASE_TOKEN_ADJUSTER_PRICE_POLLING_INTERVAL_MS'] = + overrides.baseTokenPricePollingIntervalMs.toString(); + env['BASE_TOKEN_ADJUSTER_PRICE_CACHE_UPDATE_INTERVAL_MS'] = cacheUpdateInterval.toString(); + } + + if (overrides.baseTokenAdjusterL1UpdateDeviationPercentage !== undefined) { + env['BASE_TOKEN_ADJUSTER_L1_UPDATE_DEVIATION_PERCENTAGE'] = + overrides.baseTokenAdjusterL1UpdateDeviationPercentage.toString(); + } } if (testMode) { @@ -175,6 +271,26 @@ export class NodeSpawner { await waitForNodeToStart(proc, options.apiWeb3JsonRpcHttpUrl); return new Node(proc, options.apiWeb3JsonRpcHttpUrl, NodeType.MAIN); } + + public restoreConfig() { + if (this.generalConfigPath != void 0 && this.originalConfig != void 0) + fsSync.writeFileSync(this.generalConfigPath, this.originalConfig, 'utf8'); + } + + private readFileConfig() { + if (this.generalConfigPath == void 0) + throw new Error('Trying to set property in config while not in file mode'); + const generalConfig = fsSync.readFileSync(this.generalConfigPath, 'utf8'); + return YAML.parse(generalConfig); + } + + private writeFileConfig(config: any) { + if (this.generalConfigPath == void 0) + throw new Error('Trying to set property in config while not in file mode'); + + const newGeneralConfig = YAML.stringify(config); + fsSync.writeFileSync(this.generalConfigPath, newGeneralConfig, 'utf8'); + } } async function waitForNodeToStart(proc: ChildProcessWithoutNullStreams, l2Url: string) { diff --git a/core/tests/ts-integration/tests/base-token.test.ts b/core/tests/ts-integration/tests/base-token.test.ts index 8ecc9de3ddb..432ce70ae17 100644 --- a/core/tests/ts-integration/tests/base-token.test.ts +++ b/core/tests/ts-integration/tests/base-token.test.ts @@ -39,9 +39,8 @@ describe('base ERC20 contract checks', () => { const numerator = Number(await zksyncContract.baseTokenGasPriceMultiplierNominator()); const denominator = Number(await zksyncContract.baseTokenGasPriceMultiplierDenominator()); - // checking that the numerator and denominator don't have their default values - expect(numerator).toBe(3); - expect(denominator).toBe(2); + expect(numerator).toBe(314); + expect(denominator).toBe(1000); }); test('Can perform a deposit', async () => { diff --git a/core/tests/ts-integration/tests/custom-account.test.ts b/core/tests/ts-integration/tests/custom-account.test.ts index 46ddba95323..ebbe11b8719 100644 --- a/core/tests/ts-integration/tests/custom-account.test.ts +++ b/core/tests/ts-integration/tests/custom-account.test.ts @@ -18,6 +18,9 @@ const contracts = { // We create multiple custom accounts and we need to fund them with ETH to pay for fees. const ETH_PER_CUSTOM_ACCOUNT = L2_DEFAULT_ETH_PER_ACCOUNT / 8n; const TRANSFER_AMOUNT = 1n; +const DEFAULT_TIMESTAMP_ASSERTER_RANGE_START = 0; +// 2555971200 is a number of seconds up to 30/12/2050 +const DEFAULT_TIMESTAMP_ASSERTER_RANGE_END = 2555971200; describe('Tests for the custom account behavior', () => { let testMaster: TestMaster; @@ -25,11 +28,13 @@ describe('Tests for the custom account behavior', () => { let customAccount: zksync.Contract; let erc20Address: string; let erc20: zksync.Contract; + let timestampAsserterAddress: string; beforeAll(() => { testMaster = TestMaster.getInstance(__filename); alice = testMaster.mainAccount(); erc20Address = testMaster.environment().erc20Token.l2Address; + timestampAsserterAddress = testMaster.environment().timestampAsserterAddress; erc20 = new zksync.Contract( erc20Address, zksync.utils.IERC20, @@ -40,7 +45,17 @@ describe('Tests for the custom account behavior', () => { test('Should deploy custom account', async () => { const violateRules = false; - customAccount = await deployContract(alice, contracts.customAccount, [violateRules], 'createAccount'); + customAccount = await deployContract( + alice, + contracts.customAccount, + [ + violateRules, + timestampAsserterAddress, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_START, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_END + ], + 'createAccount' + ); // Now we need to check that it was correctly marked as an account: const contractAccountInfo = await alice.provider.getContractAccountInfo(await customAccount.getAddress()); @@ -50,6 +65,8 @@ describe('Tests for the custom account behavior', () => { // Checking that the nonce ordering is correct expect(contractAccountInfo.nonceOrdering).toEqual(zksync.types.AccountNonceOrdering.Sequential); + + return customAccount; }); test('Should fund the custom account', async () => { @@ -60,7 +77,7 @@ describe('Tests for the custom account behavior', () => { .transfer({ to: await customAccount.getAddress(), token: erc20Address, - amount: ERC20_PER_ACCOUNT / 4n + amount: ERC20_PER_ACCOUNT / 8n }) .then((tx) => tx.wait()); }); @@ -95,6 +112,122 @@ describe('Tests for the custom account behavior', () => { ).toBeAccepted([erc20BalanceChange, feeCheck]); }); + test('Should fail transaction validation due to timestamp assertion in the validation tracer - close to the range end', async () => { + const now = Math.floor(Date.now() / 1000); + const minTimeTillEnd = testMaster.environment().timestampAsserterMinTimeTillEndSec; + const rangeStart = now - 10; + const rangeEnd = now + minTimeTillEnd / 2; + + const customAccount = await deployAndFundCustomAccount( + alice, + erc20Address, + timestampAsserterAddress, + rangeStart, + rangeEnd + ); + + const tx = await erc20.transfer.populateTransaction(alice.address, TRANSFER_AMOUNT); + + await expect( + sendCustomAccountTransaction( + tx as zksync.types.Transaction, + alice.provider, + await customAccount.getAddress(), + testMaster.environment().l2ChainId + ) + ).toBeRejected( + 'failed to validate the transaction. reason: Violated validation rules: block.timestamp is too close to the range end' + ); + }); + + test('Should execute contract by custom account when timestamp asserter range end overflows', async () => { + // This test ensures that a custom account transaction completes successfully + // even when the timestamp asserter's range end exceeds `u64::MAX`. In such cases, + // the range is capped at `u64::MAX` and processed as expected. + const customAccount = await deployAndFundCustomAccount( + alice, + erc20Address, + timestampAsserterAddress, + 0, + BigInt('3402823669209384634633746074317682') // u128::MAX + ); + + const tx = await erc20.transfer.populateTransaction(alice.address, TRANSFER_AMOUNT); + const customAccountAddress = await customAccount.getAddress(); + const erc20BalanceChange = await shouldChangeTokenBalances(erc20Address, [ + { + addressToCheck: customAccountAddress, + wallet: alice, + change: -TRANSFER_AMOUNT + }, + { wallet: alice, change: TRANSFER_AMOUNT } + ]); + const feeCheck = await shouldChangeETHBalances([ + { addressToCheck: customAccountAddress, wallet: alice, change: 0n } + ]); + + await expect( + sendCustomAccountTransaction( + tx as zksync.types.Transaction, + alice.provider, + await customAccount.getAddress(), + testMaster.environment().l2ChainId + ) + ).toBeAccepted([erc20BalanceChange, feeCheck]); + }); + + test('Should fail to estimate fee due to block.timestamp assertion in the smart contract', async () => { + const now = Math.floor(Date.now() / 1000); + const rangeStart = now + 300; + const rangeEnd = now + 1000; + + const customAccount = await deployAndFundCustomAccount( + alice, + erc20Address, + timestampAsserterAddress, + rangeStart, + rangeEnd + ); + const customAccountAddress = await customAccount.getAddress(); + + const tx = await erc20.transfer.populateTransaction(alice.address, TRANSFER_AMOUNT); + + try { + await sendCustomAccountTransaction( + tx as zksync.types.Transaction, + alice.provider, + customAccountAddress, + testMaster.environment().l2ChainId, + undefined, + undefined, + false + ); + expect(null).fail('The transaction was expected to fail'); + } catch (e) { + const err = e as Error; + expect(err.message).toContain( + 'failed to validate the transaction. reason: Validation revert: Account validation error' + ); + const functionSelectorMatch = err.message.match(/function_selector\s=\s(0x[0-9a-fA-F]{8})/); + const calldataMatch = err.message.match(/data\s=\s(0x[0-9a-fA-F]+)/); + + expect(functionSelectorMatch && calldataMatch).toBeTruthy(); + + const functionSelector = functionSelectorMatch![1]; + expect(functionSelector).toBe('0x3d5740d9'); + + const calldata = calldataMatch![1]; + + const startHex = calldata.slice(74, 138); + const endHex = calldata.slice(138); + const start = BigInt(`0x${startHex}`); + const end = BigInt(`0x${endHex}`); + + expect(start).toBe(BigInt(rangeStart)); + expect(end).toBe(BigInt(rangeEnd)); + } + }); + test('Should fail the validation with incorrect signature', async () => { const tx = await erc20.transfer.populateTransaction(alice.address, TRANSFER_AMOUNT); const fakeSignature = new Uint8Array(12); @@ -112,7 +245,17 @@ describe('Tests for the custom account behavior', () => { test('Should not allow violating validation rules', async () => { // We configure account to violate storage access rules during tx validation. const violateRules = true; - const badCustomAccount = await deployContract(alice, contracts.customAccount, [violateRules], 'createAccount'); + const badCustomAccount = await deployContract( + alice, + contracts.customAccount, + [ + violateRules, + timestampAsserterAddress, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_START, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_END + ], + 'createAccount' + ); const badCustomAccountAddress = await badCustomAccount.getAddress(); // Fund the account. @@ -145,7 +288,17 @@ describe('Tests for the custom account behavior', () => { // Note that we supply "create" instead of "createAccount" here -- the code is the same, but it'll // be treated as a common contract. const violateRules = false; - const nonAccount = await deployContract(alice, contracts.customAccount, [violateRules], 'create'); + const nonAccount = await deployContract( + alice, + contracts.customAccount, + [ + violateRules, + timestampAsserterAddress, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_START, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_END + ], + 'create' + ); const nonAccountAddress = await nonAccount.getAddress(); // Fund the account. @@ -203,7 +356,12 @@ describe('Tests for the custom account behavior', () => { const badCustomAccount = await deployContract( alice, contracts.customAccount, - [violateStorageRules], + [ + violateStorageRules, + timestampAsserterAddress, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_START, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_END + ], 'createAccount' ); const badCustomAccountAddress = await badCustomAccount.getAddress(); @@ -244,7 +402,12 @@ describe('Tests for the custom account behavior', () => { const badCustomAccount = await deployContract( alice, contracts.customAccount, - [violateStorageRules], + [ + violateStorageRules, + timestampAsserterAddress, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_START, + DEFAULT_TIMESTAMP_ASSERTER_RANGE_END + ], 'createAccount' ); const badCustomAccountAddress = await badCustomAccount.getAddress(); @@ -316,12 +479,11 @@ async function sendCustomAccountTransaction( accountAddress: string, chainId: bigint, customSignature?: Uint8Array, - nonce?: number + nonce?: number, + estimateGas: boolean = true ) { - const gasLimit = await browserProvider.estimateGas({ - ...tx, - from: accountAddress - }); + const gasLimit = estimateGas ? await browserProvider.estimateGas({ ...tx, from: accountAddress }) : BigInt(100_000); // Enough gas to invoke AA contract + const gasPrice = await browserProvider.getGasPrice(); tx.gasLimit = gasLimit; @@ -345,3 +507,30 @@ async function sendCustomAccountTransaction( return await browserProvider.broadcastTransaction(serializedTx); } + +async function deployAndFundCustomAccount( + richAccount: zksync.Wallet, + erc20Address: string, + timestampAsserterAddress: string, + rangeStart: any, + rangeEnd: any +): Promise { + const customAccount = await deployContract( + richAccount, + contracts.customAccount, + [false, timestampAsserterAddress, rangeStart, rangeEnd], + 'createAccount' + ); + + await richAccount + .transfer({ to: await customAccount.getAddress(), amount: ETH_PER_CUSTOM_ACCOUNT }) + .then((tx) => tx.wait()); + await richAccount + .transfer({ + to: await customAccount.getAddress(), + token: erc20Address, + amount: ERC20_PER_ACCOUNT / 8n + }) + .then((tx) => tx.wait()); + return customAccount; +} diff --git a/core/tests/ts-integration/tests/fees.test.ts b/core/tests/ts-integration/tests/fees.test.ts index e99d3b67911..fc156e03f16 100644 --- a/core/tests/ts-integration/tests/fees.test.ts +++ b/core/tests/ts-integration/tests/fees.test.ts @@ -15,13 +15,15 @@ import { TestContextOwner, TestMaster } from '../src'; import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; import { DataAvailabityMode, Token } from '../src/types'; -import { SYSTEM_CONTEXT_ADDRESS, getTestContract } from '../src/helpers'; +import { SYSTEM_CONTEXT_ADDRESS, getTestContract, waitForNewL1Batch, anyTransaction } from '../src/helpers'; import { loadConfig, shouldLoadConfigFromFile } from 'utils/build/file-configs'; import { logsTestPath } from 'utils/build/logs'; -import path from 'path'; -import { NodeSpawner, Node, NodeType } from '../src/utils'; -import { deleteInternalEnforcedL1GasPrice, deleteInternalEnforcedPubdataPrice, setTransactionSlots } from './utils'; +import { sleep } from 'utils/build'; import { killPidWithAllChilds } from 'utils/build/kill'; +import path from 'path'; +import { NodeSpawner } from '../src/utils'; +import { sendTransfers } from '../src/context-owner'; +import { Reporter } from '../src/reporter'; declare global { var __ZKSYNC_TEST_CONTEXT_OWNER__: TestContextOwner; @@ -60,13 +62,13 @@ testFees('Test fees', function () { let tokenDetails: Token; let aliceErc20: zksync.Contract; + let isETHBasedChain: boolean; let mainLogs: fs.FileHandle; let baseTokenAddress: string; let ethClientWeb3Url: string; let apiWeb3JsonRpcHttpUrl: string; let mainNodeSpawner: NodeSpawner; - let mainNode: Node; const fileConfig = shouldLoadConfigFromFile(); const pathToHome = path.join(__dirname, '../../../..'); @@ -121,11 +123,41 @@ testFees('Test fees', function () { baseTokenAddress }); - mainNode = await mainNodeSpawner.spawnMainNode(); + await mainNodeSpawner.killAndSpawnMainNode(); alice = testMaster.mainAccount(); tokenDetails = testMaster.environment().erc20Token; aliceErc20 = new ethers.Contract(tokenDetails.l1Address, zksync.utils.IERC20, alice.ethWallet()); + + const mainWallet = new zksync.Wallet( + testMaster.environment().mainWalletPK, + alice._providerL2(), + alice._providerL1() + ); + + isETHBasedChain = baseTokenAddress == zksync.utils.ETH_ADDRESS_IN_CONTRACTS; + + // On non ETH based chains the standard deposit is not enough to run all this tests + if (!isETHBasedChain) { + const depositTx = await mainWallet.deposit({ + token: baseTokenAddress, + amount: ethers.parseEther('100'), + approveERC20: true, + approveBaseERC20: true + }); + await depositTx.wait(); + await Promise.all( + await sendTransfers( + zksync.utils.ETH_ADDRESS, + mainWallet, + { alice: alice.privateKey }, + ethers.parseEther('100'), + undefined, + undefined, + new Reporter() + ) + ); + } }); test('Test all fees', async () => { @@ -177,8 +209,10 @@ testFees('Test fees', function () { ]; for (const gasPrice of L1_GAS_PRICES_TO_TEST) { // For the sake of simplicity, we'll use the same pubdata price as the L1 gas price. - await mainNode.killAndWaitForShutdown(); - mainNode = await mainNodeSpawner.spawnMainNode(gasPrice.toString(), gasPrice.toString()); + await mainNodeSpawner.killAndSpawnMainNode({ + newL1GasPrice: gasPrice, + newPubdataPrice: gasPrice + }); reports = await appendResults( alice, @@ -213,6 +247,96 @@ testFees('Test fees', function () { console.log(`Full report: \n\n${reports.join('\n\n')}`); }); + test('Test gas price expected value', async () => { + const l1GasPrice = 2_000_000_000n; /// set to 2 gwei + await mainNodeSpawner.killAndSpawnMainNode({ + newL1GasPrice: l1GasPrice, + newPubdataPrice: l1GasPrice + }); + + // wait for new batch so gas price is updated with new config set above + await waitForNewL1Batch(alice); + + const receipt = await anyTransaction(alice); + + const feeParams = await alice._providerL2().getFeeParams(); + const feeConfig = feeParams.V2.config; + // type is missing conversion_ratio field + const conversionRatio: { numerator: bigint; denominator: bigint } = (feeParams.V2 as any)['conversion_ratio']; + if (isETHBasedChain) { + expect(conversionRatio.numerator).toBe(1); //number not bigint for some reason + expect(conversionRatio.denominator).toBe(1); + } else { + expect(conversionRatio.numerator).toBeGreaterThan(1n); + } + + // the minimum + compute overhead of 0.01gwei in validium mode + const expectedETHGasPrice = + feeConfig.minimal_l2_gas_price + + (feeConfig.compute_overhead_part * feeParams.V2.l1_gas_price * feeConfig.batch_overhead_l1_gas) / + feeConfig.max_gas_per_batch; + const expectedConvertedGasPrice = + (expectedETHGasPrice * conversionRatio.numerator) / conversionRatio.denominator; + + expect(receipt.gasPrice).toBe(BigInt(expectedConvertedGasPrice)); + }); + + test('Test base token ratio fluctuations', async () => { + const l1GasPrice = 2_000_000_000n; /// set to 2 gwei + + if (isETHBasedChain) return; + + await mainNodeSpawner.killAndSpawnMainNode({ + newL1GasPrice: l1GasPrice, + newPubdataPrice: l1GasPrice, + externalPriceApiClientForcedNumerator: 300, + externalPriceApiClientForcedDenominator: 100, + externalPriceApiClientForcedFluctuation: 20, + baseTokenPricePollingIntervalMs: 1000, + baseTokenAdjusterL1UpdateDeviationPercentage: 0 + }); + + const beginFeeParams = await alice._providerL2().getFeeParams(); + const mainContract = await alice.getMainContract(); + const beginL1Nominator = await mainContract.baseTokenGasPriceMultiplierNominator(); + let changedL2 = false; + let changedL1 = false; + for (let i = 0; i < 20; i++) { + await sleep(0.5); + const newFeeParams = await alice._providerL2().getFeeParams(); + // we need any as FeeParams is missing existing conversion_ratio field + + if ( + ((newFeeParams.V2 as any)['conversion_ratio'].numerator as number) != + ((beginFeeParams.V2 as any)['conversion_ratio'].numerator as number) + ) { + // @ts-ignore + const diff = + (newFeeParams.V2 as any)['conversion_ratio'].numerator - + (beginFeeParams.V2 as any)['conversion_ratio'].numerator; + // Deviation is 20%, Adding 5% extra for any arithmetic precision issues, 25%*300 = 75 + expect(diff).toBeLessThan(75); + expect(diff).toBeGreaterThan(-75); + changedL2 = true; + break; + } + } + expect(changedL2).toBeTruthy(); + for (let i = 0; i < 10; i++) { + const newL1Nominator = await mainContract.baseTokenGasPriceMultiplierNominator(); + if (newL1Nominator != beginL1Nominator) { + const diff = newL1Nominator - beginL1Nominator; + expect(diff).toBeLessThan(75); // as above + expect(diff).toBeGreaterThan(-75); + changedL1 = true; + break; + } + await sleep(0.5); + } + + expect(changedL1).toBeTruthy(); + }); + test('Test gas consumption under large L1 gas price', async () => { if (testMaster.environment().l1BatchCommitDataGeneratorMode === DataAvailabityMode.Validium) { // We skip this test for Validium mode, since L1 gas price has little impact on the gasLimit in this mode. @@ -233,11 +357,10 @@ testFees('Test fees', function () { // that the gasLimit is indeed over u32::MAX, which is the most important tested property. const requiredPubdataPrice = minimalL2GasPrice * 100_000n; - await mainNode.killAndWaitForShutdown(); - mainNode = await mainNodeSpawner.spawnMainNode( - requiredPubdataPrice.toString(), - requiredPubdataPrice.toString() - ); + await mainNodeSpawner.killAndSpawnMainNode({ + newL1GasPrice: requiredPubdataPrice, + newPubdataPrice: requiredPubdataPrice + }); const l1Messenger = new ethers.Contract(zksync.utils.L1_MESSENGER_ADDRESS, zksync.utils.L1_MESSENGER, alice); @@ -278,16 +401,11 @@ testFees('Test fees', function () { }); afterAll(async () => { - await mainNode.killAndWaitForShutdown(); + await mainNodeSpawner.killAndSpawnMainNode(); // Returning the pubdata price to the default one - - // Restore defaults - setTransactionSlots(pathToHome, fileConfig, 8192); - deleteInternalEnforcedL1GasPrice(pathToHome, fileConfig); - deleteInternalEnforcedPubdataPrice(pathToHome, fileConfig); - mainNode = await mainNodeSpawner.spawnMainNode(); + // Spawning with no options restores defaults. await testMaster.deinitialize(); - __ZKSYNC_TEST_CONTEXT_OWNER__.setL2NodePid(mainNode.proc.pid!); + __ZKSYNC_TEST_CONTEXT_OWNER__.setL2NodePid(mainNodeSpawner.mainNode!.proc.pid!); }); }); diff --git a/core/tests/ts-integration/tests/utils.ts b/core/tests/ts-integration/tests/utils.ts deleted file mode 100644 index 24df8a170c2..00000000000 --- a/core/tests/ts-integration/tests/utils.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as fs from 'fs'; -import { getConfigPath } from 'utils/build/file-configs'; - -export function setInternalEnforcedPubdataPrice(pathToHome: string, fileConfig: any, value: number) { - setGasAdjusterProperty(pathToHome, fileConfig, 'internal_enforced_pubdata_price', value); -} - -export function setInternalEnforcedL1GasPrice(pathToHome: string, fileConfig: any, value: number) { - setGasAdjusterProperty(pathToHome, fileConfig, 'internal_enforced_l1_gas_price', value); -} - -export function deleteInternalEnforcedPubdataPrice(pathToHome: string, fileConfig: any) { - deleteProperty(pathToHome, fileConfig, 'internal_enforced_pubdata_price'); -} - -export function deleteInternalEnforcedL1GasPrice(pathToHome: string, fileConfig: any) { - deleteProperty(pathToHome, fileConfig, 'internal_enforced_l1_gas_price'); -} - -export function setTransactionSlots(pathToHome: string, fileConfig: any, value: number) { - setPropertyInGeneralConfig(pathToHome, fileConfig, 'transaction_slots', value); -} - -function setPropertyInGeneralConfig(pathToHome: string, fileConfig: any, property: string, value: number) { - const generalConfigPath = getConfigPath({ - pathToHome, - chain: fileConfig.chain, - configsFolder: 'configs', - config: 'general.yaml' - }); - const generalConfig = fs.readFileSync(generalConfigPath, 'utf8'); - - const regex = new RegExp(`${property}:\\s*\\d+(\\.\\d+)?`, 'g'); - const newGeneralConfig = generalConfig.replace(regex, `${property}: ${value}`); - - fs.writeFileSync(generalConfigPath, newGeneralConfig, 'utf8'); -} - -function setGasAdjusterProperty(pathToHome: string, fileConfig: any, property: string, value: number) { - const generalConfigPath = getConfigPath({ - pathToHome, - chain: fileConfig.chain, - configsFolder: 'configs', - config: 'general.yaml' - }); - const generalConfig = fs.readFileSync(generalConfigPath, 'utf8'); - - // Define the regex pattern to check if the property already exists - const propertyRegex = new RegExp(`(^\\s*${property}:\\s*\\d+(\\.\\d+)?$)`, 'm'); - const gasAdjusterRegex = new RegExp('(^\\s*gas_adjuster:.*$)', 'gm'); - - let newGeneralConfig; - - if (propertyRegex.test(generalConfig)) { - // If the property exists, modify its value - newGeneralConfig = generalConfig.replace(propertyRegex, ` ${property}: ${value}`); - } else { - // If the property does not exist, add it under the gas_adjuster section - newGeneralConfig = generalConfig.replace(gasAdjusterRegex, `$1\n ${property}: ${value}`); - } - - fs.writeFileSync(generalConfigPath, newGeneralConfig, 'utf8'); -} - -function deleteProperty(pathToHome: string, fileConfig: any, property: string) { - const generalConfigPath = getConfigPath({ - pathToHome, - chain: fileConfig.chain, - configsFolder: 'configs', - config: 'general.yaml' - }); - const generalConfig = fs.readFileSync(generalConfigPath, 'utf8'); - - // Define the regex pattern to find the property line and remove it completely - const propertyRegex = new RegExp(`^\\s*${property}:.*\\n?`, 'm'); - - // Remove the line if the property exists - const newGeneralConfig = generalConfig.replace(propertyRegex, ''); - - fs.writeFileSync(generalConfigPath, newGeneralConfig, 'utf8'); -} diff --git a/docker/contract-verifier/Dockerfile b/docker/contract-verifier/Dockerfile index e9d83903d11..b1b63429a63 100644 --- a/docker/contract-verifier/Dockerfile +++ b/docker/contract-verifier/Dockerfile @@ -47,7 +47,7 @@ RUN mkdir -p /etc/zksolc-bin/vm-1.5.0-a167aa3 && \ chmod +x /etc/zksolc-bin/vm-1.5.0-a167aa3/zksolc # install zksolc 1.5.x -RUN for VERSION in $(seq -f "v1.5.%g" 0 6); do \ +RUN for VERSION in $(seq -f "v1.5.%g" 0 7); do \ mkdir -p /etc/zksolc-bin/$VERSION && \ wget https://github.com/matter-labs/zksolc-bin/raw/main/linux-amd64/zksolc-linux-amd64-musl-$VERSION -O /etc/zksolc-bin/$VERSION/zksolc && \ chmod +x /etc/zksolc-bin/$VERSION/zksolc; \ @@ -68,7 +68,7 @@ RUN for VERSION in $(seq -f "v1.4.%g" 0 1); do \ done # install zkvyper 1.5.x -RUN for VERSION in $(seq -f "v1.5.%g" 0 6); do \ +RUN for VERSION in $(seq -f "v1.5.%g" 0 7); do \ mkdir -p /etc/zkvyper-bin/$VERSION && \ wget https://github.com/matter-labs/zkvyper-bin/raw/main/linux-amd64/zkvyper-linux-amd64-musl-$VERSION -O /etc/zkvyper-bin/$VERSION/zkvyper && \ chmod +x /etc/zkvyper-bin/$VERSION/zkvyper; \ diff --git a/etc/env/base/external_price_api.toml b/etc/env/base/external_price_api.toml index bb22e86c432..fe88e71e82a 100644 --- a/etc/env/base/external_price_api.toml +++ b/etc/env/base/external_price_api.toml @@ -6,5 +6,5 @@ source = "forced" [external_price_api_client.forced] -numerator = 3 -denominator = 2 +numerator = 314 +denominator = 1000 diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index 5abee904765..6e0e913f5bc 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -299,8 +299,8 @@ base_token_adjuster: external_price_api_client: source: "forced" client_timeout_ms: 10000 - forced_numerator: 3 - forced_denominator: 2 + forced_numerator: 314 + forced_denominator: 1000 house_keeper: @@ -377,6 +377,9 @@ da_dispatcher: external_proof_integration_api: http_port: 3073 +timestamp_asserter: + min_time_till_end_sec: 60 + consensus: port: 3054 server_addr: "127.0.0.1:3054" diff --git a/prover/CHANGELOG.md b/prover/CHANGELOG.md index 0201ce4a920..6687b1450ba 100644 --- a/prover/CHANGELOG.md +++ b/prover/CHANGELOG.md @@ -1,5 +1,77 @@ # Changelog +## [17.0.0](https://github.com/matter-labs/zksync-era/compare/prover-v16.6.0...prover-v17.0.0) (2024-10-31) + + +### ⚠ BREAKING CHANGES + +* force minor bump for provers release -- v25 ([#3208](https://github.com/matter-labs/zksync-era/issues/3208)) + +### Features + +* force minor bump for provers release -- v25 ([#3208](https://github.com/matter-labs/zksync-era/issues/3208)) ([6851e35](https://github.com/matter-labs/zksync-era/commit/6851e353f01cde5d385403dedb68bcae76d06966)) + +## [16.6.0](https://github.com/matter-labs/zksync-era/compare/prover-v16.5.0...prover-v16.6.0) (2024-10-31) + + +### Features + +* (DB migration) Rename recursion_scheduler_level_vk_hash to snark_wrapper_vk_hash ([#2809](https://github.com/matter-labs/zksync-era/issues/2809)) ([64f9551](https://github.com/matter-labs/zksync-era/commit/64f95514c99f95da2a19a97ff064c29a97efc22f)) +* Add initial version prover_autoscaler ([#2993](https://github.com/matter-labs/zksync-era/issues/2993)) ([ebf9604](https://github.com/matter-labs/zksync-era/commit/ebf9604c5ab2a1cae1ffd2f9c922f35a1d0ad876)) +* added seed_peers to consensus global config ([#2920](https://github.com/matter-labs/zksync-era/issues/2920)) ([e9d1d90](https://github.com/matter-labs/zksync-era/commit/e9d1d905f1ce86f9de2cf39d79be4b5aada4a81d)) +* attester committees data extractor (BFT-434) ([#2684](https://github.com/matter-labs/zksync-era/issues/2684)) ([92dde03](https://github.com/matter-labs/zksync-era/commit/92dde039ee8a0bc08e2019b7fa6f243a34d9816f)) +* Bump crypto and protocol deps ([#2825](https://github.com/matter-labs/zksync-era/issues/2825)) ([a5ffaf1](https://github.com/matter-labs/zksync-era/commit/a5ffaf1b4e291d6f09ba8c1f224f5900665bffc4)) +* **circuit_prover:** Add circuit prover ([#2908](https://github.com/matter-labs/zksync-era/issues/2908)) ([48317e6](https://github.com/matter-labs/zksync-era/commit/48317e640a00b016bf7bf782cc94fccaf077ed6d)) +* **consensus:** Support for syncing blocks before consensus genesis over p2p network ([#3040](https://github.com/matter-labs/zksync-era/issues/3040)) ([d3edc3d](https://github.com/matter-labs/zksync-era/commit/d3edc3d817c151ed00d4fa822fdae0a746e33356)) +* **da-clients:** add secrets ([#2954](https://github.com/matter-labs/zksync-era/issues/2954)) ([f4631e4](https://github.com/matter-labs/zksync-era/commit/f4631e4466de620cc1401b326d864cdb8b48a05d)) +* gateway preparation ([#3006](https://github.com/matter-labs/zksync-era/issues/3006)) ([16f2757](https://github.com/matter-labs/zksync-era/commit/16f275756cd28024a6b11ac1ac327eb5b8b446e1)) +* Integrate tracers and implement circuits tracer in vm2 ([#2653](https://github.com/matter-labs/zksync-era/issues/2653)) ([87b02e3](https://github.com/matter-labs/zksync-era/commit/87b02e3ab5c1f61d59dd0f0eefa9ec33a7b55488)) +* Move prover data to /home/popzxc/workspace/current/zksync-era/prover/data ([#2778](https://github.com/matter-labs/zksync-era/issues/2778)) ([62e4d46](https://github.com/matter-labs/zksync-era/commit/62e4d4619dde9d6bd9102f1410eea75b0e2051c5)) +* Prover e2e test ([#2975](https://github.com/matter-labs/zksync-era/issues/2975)) ([0edd796](https://github.com/matter-labs/zksync-era/commit/0edd7962429b3530ae751bd7cc947c97193dd0ca)) +* **prover:** add CLI option to run prover with max allocation ([#2794](https://github.com/matter-labs/zksync-era/issues/2794)) ([35e4cae](https://github.com/matter-labs/zksync-era/commit/35e4cae29314fa98ce356a875e08b3e869a31036)) +* **prover:** Add endpoint to PJM to get queue reports ([#2918](https://github.com/matter-labs/zksync-era/issues/2918)) ([2cec83f](https://github.com/matter-labs/zksync-era/commit/2cec83f26e0b9309387135ca43718af4fcd6f6b1)) +* **prover:** Add error to panic message of prover ([#2807](https://github.com/matter-labs/zksync-era/issues/2807)) ([6e057eb](https://github.com/matter-labs/zksync-era/commit/6e057ebf277e0cbc7964079c01ef0348e006a53b)) +* **prover:** Add min_provers and dry_run features. Improve metrics and test. ([#3129](https://github.com/matter-labs/zksync-era/issues/3129)) ([7c28964](https://github.com/matter-labs/zksync-era/commit/7c289649b7b3c418c7193a35b51c264cf4970f3c)) +* **prover:** Add scale failure events watching and pods eviction. ([#3175](https://github.com/matter-labs/zksync-era/issues/3175)) ([dd166f8](https://github.com/matter-labs/zksync-era/commit/dd166f887b11a8dfb039a0030dda923c481f67af)) +* **prover:** Add sending scale requests for Scaler targets ([#3194](https://github.com/matter-labs/zksync-era/issues/3194)) ([767c5bc](https://github.com/matter-labs/zksync-era/commit/767c5bc6a62c402c099abe93b7dbecbb59e4acb7)) +* **prover:** Add support for scaling WGs and compressor ([#3179](https://github.com/matter-labs/zksync-era/issues/3179)) ([c41db9e](https://github.com/matter-labs/zksync-era/commit/c41db9ecec1c21b80969604f703ac6990f6f3434)) +* **prover:** Autoscaler sends scale request to appropriate agents. ([#3150](https://github.com/matter-labs/zksync-era/issues/3150)) ([bfedac0](https://github.com/matter-labs/zksync-era/commit/bfedac03b53055c6e2d5fa6bd6bdc78e2cb1724c)) +* **prover:** Extract keystore into a separate crate ([#2797](https://github.com/matter-labs/zksync-era/issues/2797)) ([e239260](https://github.com/matter-labs/zksync-era/commit/e239260d77b55fcce0b1f485029762a605cdb6d0)) +* **prover:** Optimize setup keys loading ([#2847](https://github.com/matter-labs/zksync-era/issues/2847)) ([19887ef](https://github.com/matter-labs/zksync-era/commit/19887ef21a8bbd26977353f8ee277b711850dfd2)) +* **prover:** Refactor WitnessGenerator ([#2845](https://github.com/matter-labs/zksync-era/issues/2845)) ([934634b](https://github.com/matter-labs/zksync-era/commit/934634b149377c730ec39e904508c40628ff4019)) +* **prover:** Update witness generator to zkevm_test_harness 0.150.6 ([#3029](https://github.com/matter-labs/zksync-era/issues/3029)) ([2151c28](https://github.com/matter-labs/zksync-era/commit/2151c2832498ca6e7ee1eee0bfdf6a0568345fee)) +* **prover:** Use query macro instead string literals for queries ([#2930](https://github.com/matter-labs/zksync-era/issues/2930)) ([1cf959d](https://github.com/matter-labs/zksync-era/commit/1cf959da12d2b6369f34a67ccc2575b4b173d75a)) +* **prover:** WG refactoring [#3](https://github.com/matter-labs/zksync-era/issues/3) ([#2942](https://github.com/matter-labs/zksync-era/issues/2942)) ([df68762](https://github.com/matter-labs/zksync-era/commit/df6876221936a44fa2fb8c80c01d043d229621fc)) +* **prover:** WitnessGenerator refactoring [#2](https://github.com/matter-labs/zksync-era/issues/2) ([#2899](https://github.com/matter-labs/zksync-era/issues/2899)) ([36e5340](https://github.com/matter-labs/zksync-era/commit/36e534091f73f4e3ce86e322fb20842cda6a6b61)) +* Refactor metrics/make API use binaries ([#2735](https://github.com/matter-labs/zksync-era/issues/2735)) ([8ed086a](https://github.com/matter-labs/zksync-era/commit/8ed086afecfcad30bfda44fc4d29a00beea71cca)) +* Remove prover db from house keeper ([#2795](https://github.com/matter-labs/zksync-era/issues/2795)) ([85b7346](https://github.com/matter-labs/zksync-era/commit/85b734664b4306e988da07005860a7ea0fb7d22d)) +* **tee:** use hex serialization for RPC responses ([#2887](https://github.com/matter-labs/zksync-era/issues/2887)) ([abe0440](https://github.com/matter-labs/zksync-era/commit/abe0440811ae4daf4a0f307922a282e9664308e0)) +* **utils:** Rework locate_workspace, introduce Workspace type ([#2830](https://github.com/matter-labs/zksync-era/issues/2830)) ([d256092](https://github.com/matter-labs/zksync-era/commit/d2560928cc67b40a97a5497ac8542915bf6f91a9)) +* vm2 tracers can access storage ([#3114](https://github.com/matter-labs/zksync-era/issues/3114)) ([e466b52](https://github.com/matter-labs/zksync-era/commit/e466b52948e3c4ed1cb5af4fd999a52028e4d216)) +* **vm:** Do not panic on VM divergence ([#2705](https://github.com/matter-labs/zksync-era/issues/2705)) ([7aa5721](https://github.com/matter-labs/zksync-era/commit/7aa5721d22e253d05d369a60d5bcacbf52021c48)) +* **vm:** EVM emulator support – base ([#2979](https://github.com/matter-labs/zksync-era/issues/2979)) ([deafa46](https://github.com/matter-labs/zksync-era/commit/deafa460715334a77edf9fe8aa76fa90029342c4)) +* **vm:** Extract batch executor to separate crate ([#2702](https://github.com/matter-labs/zksync-era/issues/2702)) ([b82dfa4](https://github.com/matter-labs/zksync-era/commit/b82dfa4d29fce107223c3638fe490b5cb0f28d8c)) +* **zk_toolbox:** `zk_supervisor prover` subcommand ([#2820](https://github.com/matter-labs/zksync-era/issues/2820)) ([3506731](https://github.com/matter-labs/zksync-era/commit/3506731d1702bdec8c6b5b41cabca9a257f0269b)) +* **zk_toolbox:** Add external_node consensus support ([#2821](https://github.com/matter-labs/zksync-era/issues/2821)) ([4a10d7d](https://github.com/matter-labs/zksync-era/commit/4a10d7d9554d6c1aa2f4fc46557d40baaad8ff2f)) +* **zk_toolbox:** Add SQL format for zk supervisor ([#2950](https://github.com/matter-labs/zksync-era/issues/2950)) ([540e5d7](https://github.com/matter-labs/zksync-era/commit/540e5d7554f54e80d52f1bfae37e03ca8f787baf)) +* **zk_toolbox:** deploy legacy bridge ([#2837](https://github.com/matter-labs/zksync-era/issues/2837)) ([93b4e08](https://github.com/matter-labs/zksync-era/commit/93b4e08257802d11108870d867dd59fa35e52733)) +* **zk_toolbox:** Redesign zk_toolbox commands ([#3003](https://github.com/matter-labs/zksync-era/issues/3003)) ([114834f](https://github.com/matter-labs/zksync-era/commit/114834f357421c62d596a1954fac8ce615cfde49)) +* **zkstack_cli:** Build dependencies at zkstack build time ([#3157](https://github.com/matter-labs/zksync-era/issues/3157)) ([724d9a9](https://github.com/matter-labs/zksync-era/commit/724d9a9c7f2127263845b640c843e751fd3c21ae)) + + +### Bug Fixes + +* allow compilation under current toolchain ([#3176](https://github.com/matter-labs/zksync-era/issues/3176)) ([89eadd3](https://github.com/matter-labs/zksync-era/commit/89eadd353c4fb84bb815ae56b29f4ff3467b80f3)) +* **api:** Return correct flat call tracer ([#2917](https://github.com/matter-labs/zksync-era/issues/2917)) ([218646a](https://github.com/matter-labs/zksync-era/commit/218646aa1c56200f4ffee99b7f83366e2689354f)) +* count SECP256 precompile to account validation gas limit as well ([#2859](https://github.com/matter-labs/zksync-era/issues/2859)) ([fee0c2a](https://github.com/matter-labs/zksync-era/commit/fee0c2ad08a5ab4a04252765b367eb9fbb1f3db7)) +* Fix Doc lint. ([#3158](https://github.com/matter-labs/zksync-era/issues/3158)) ([c79949b](https://github.com/matter-labs/zksync-era/commit/c79949b8ffde9867b961192afa6c815b44865ae4)) +* ignore unknown fields in rpc json response ([#2962](https://github.com/matter-labs/zksync-era/issues/2962)) ([692ea73](https://github.com/matter-labs/zksync-era/commit/692ea73f75a5fb9db2b4ac33ad24d20568638742)) +* **prover:** Do not exit on missing watcher data. ([#3119](https://github.com/matter-labs/zksync-era/issues/3119)) ([76ed6d9](https://github.com/matter-labs/zksync-era/commit/76ed6d966051c56f8e894c18461c5ea284b1a74b)) +* **prover:** fix setup_metadata_to_setup_data_key ([#2875](https://github.com/matter-labs/zksync-era/issues/2875)) ([4ae5a93](https://github.com/matter-labs/zksync-era/commit/4ae5a93e9e96cd0cd529baf9ffa78c1b21a9c4b1)) +* **prover:** Run for zero queue to allow scaling down to 0 ([#3115](https://github.com/matter-labs/zksync-era/issues/3115)) ([bbe1919](https://github.com/matter-labs/zksync-era/commit/bbe191937fa5c5711a7164fd4f0c2ae65cda0833)) +* **tee_verifier:** correctly initialize storage for re-execution ([#3017](https://github.com/matter-labs/zksync-era/issues/3017)) ([9d88373](https://github.com/matter-labs/zksync-era/commit/9d88373f1b745c489e98e5ef542644a70e815498)) +* **vm:** Prepare new VM for use in API server and fix divergences ([#2994](https://github.com/matter-labs/zksync-era/issues/2994)) ([741b77e](https://github.com/matter-labs/zksync-era/commit/741b77e080f75c6a93d3ee779b1c9ce4297618f9)) + ## [16.5.0](https://github.com/matter-labs/zksync-era/compare/prover-v16.4.0...prover-v16.5.0) (2024-08-28) diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 3449f075855..809ed9b3a66 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8685,7 +8685,7 @@ dependencies = [ [[package]] name = "zksync_vm2" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=fd27994d1d88ea4868d9ad74746bf274b97126d1#fd27994d1d88ea4868d9ad74746bf274b97126d1" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "enum_dispatch", "primitive-types", @@ -8697,7 +8697,7 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=fd27994d1d88ea4868d9ad74746bf274b97126d1#fd27994d1d88ea4868d9ad74746bf274b97126d1" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "primitive-types", ] diff --git a/prover/crates/bin/prover_autoscaler/src/agent.rs b/prover/crates/bin/prover_autoscaler/src/agent.rs index f810bc41672..030636ad659 100644 --- a/prover/crates/bin/prover_autoscaler/src/agent.rs +++ b/prover/crates/bin/prover_autoscaler/src/agent.rs @@ -96,7 +96,7 @@ pub struct ScaleRequest { pub deployments: Vec, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct ScaleResponse { pub scale_result: Vec, } diff --git a/prover/crates/bin/prover_autoscaler/src/cluster_types.rs b/prover/crates/bin/prover_autoscaler/src/cluster_types.rs index e3e4c9b4df0..db215e570ef 100644 --- a/prover/crates/bin/prover_autoscaler/src/cluster_types.rs +++ b/prover/crates/bin/prover_autoscaler/src/cluster_types.rs @@ -40,6 +40,7 @@ pub struct Namespace { #[serde(serialize_with = "ordered_map")] pub deployments: HashMap, pub pods: HashMap, + #[serde(default)] pub scale_errors: Vec, } @@ -64,4 +65,5 @@ pub enum PodStatus { Pending, LongPending, NeedToMove, + Failed, } diff --git a/prover/crates/bin/prover_autoscaler/src/global/queuer.rs b/prover/crates/bin/prover_autoscaler/src/global/queuer.rs index 32610ebf3c3..7255f479647 100644 --- a/prover/crates/bin/prover_autoscaler/src/global/queuer.rs +++ b/prover/crates/bin/prover_autoscaler/src/global/queuer.rs @@ -1,17 +1,22 @@ -use std::collections::HashMap; +use std::{collections::HashMap, ops::Deref}; use anyhow::{Context, Ok}; use reqwest::Method; -use zksync_prover_job_monitor::autoscaler_queue_reporter::VersionedQueueReport; +use zksync_config::configs::prover_autoscaler::QueueReportFields; +use zksync_prover_job_monitor::autoscaler_queue_reporter::{QueueReport, VersionedQueueReport}; use zksync_utils::http_with_retries::send_request_with_retries; use crate::metrics::{AUTOSCALER_METRICS, DEFAULT_ERROR_CODE}; const MAX_RETRIES: usize = 5; -#[derive(Debug)] -pub struct Queue { - pub queue: HashMap, +pub struct Queue(HashMap<(String, QueueReportFields), u64>); + +impl Deref for Queue { + type Target = HashMap<(String, QueueReportFields), u64>; + fn deref(&self) -> &Self::Target { + &self.0 + } } #[derive(Default)] @@ -19,6 +24,19 @@ pub struct Queuer { pub prover_job_monitor_url: String, } +fn target_to_queue(target: QueueReportFields, report: &QueueReport) -> u64 { + let res = match target { + QueueReportFields::basic_witness_jobs => report.basic_witness_jobs.all(), + QueueReportFields::leaf_witness_jobs => report.leaf_witness_jobs.all(), + QueueReportFields::node_witness_jobs => report.node_witness_jobs.all(), + QueueReportFields::recursion_tip_witness_jobs => report.recursion_tip_witness_jobs.all(), + QueueReportFields::scheduler_witness_jobs => report.scheduler_witness_jobs.all(), + QueueReportFields::proof_compressor_jobs => report.proof_compressor_jobs.all(), + QueueReportFields::prover_jobs => report.prover_jobs.all(), + }; + res as u64 +} + impl Queuer { pub fn new(pjm_url: String) -> Self { Self { @@ -26,12 +44,14 @@ impl Queuer { } } - pub async fn get_queue(&self) -> anyhow::Result { + /// Requests queue report from prover-job-monitor and parse it into Queue HashMap for provided + /// list of jobs. + pub async fn get_queue(&self, jobs: &[QueueReportFields]) -> anyhow::Result { let url = &self.prover_job_monitor_url; let response = send_request_with_retries(url, MAX_RETRIES, Method::GET, None, None).await; let response = response.map_err(|err| { AUTOSCALER_METRICS.calls[&(url.clone(), DEFAULT_ERROR_CODE)].inc(); - anyhow::anyhow!("Failed fetching queue from url: {url}: {err:?}") + anyhow::anyhow!("Failed fetching queue from URL: {url}: {err:?}") })?; AUTOSCALER_METRICS.calls[&(url.clone(), response.status().as_u16())].inc(); @@ -39,11 +59,18 @@ impl Queuer { .json::>() .await .context("Failed to read response as json")?; - Ok(Queue { - queue: response + Ok(Queue( + response .iter() - .map(|x| (x.version.to_string(), x.report.prover_jobs.queued as u64)) + .flat_map(|versioned_report| { + jobs.iter().map(move |j| { + ( + (versioned_report.version.to_string(), *j), + target_to_queue(*j, &versioned_report.report), + ) + }) + }) .collect::>(), - }) + )) } } diff --git a/prover/crates/bin/prover_autoscaler/src/global/scaler.rs b/prover/crates/bin/prover_autoscaler/src/global/scaler.rs index eb4249d071f..dc652999da5 100644 --- a/prover/crates/bin/prover_autoscaler/src/global/scaler.rs +++ b/prover/crates/bin/prover_autoscaler/src/global/scaler.rs @@ -4,7 +4,9 @@ use chrono::Utc; use debug_map_sorted::SortedOutputExt; use once_cell::sync::Lazy; use regex::Regex; -use zksync_config::configs::prover_autoscaler::{Gpu, ProverAutoscalerScalerConfig}; +use zksync_config::configs::prover_autoscaler::{ + Gpu, ProverAutoscalerScalerConfig, QueueReportFields, ScalerTarget, +}; use super::{queuer, watcher}; use crate::{ @@ -65,6 +67,12 @@ pub struct Scaler { watcher: watcher::Watcher, queuer: queuer::Queuer, + jobs: Vec, + prover_scaler: GpuScaler, + simple_scalers: Vec, +} + +pub struct GpuScaler { /// Which cluster to use first. cluster_priorities: HashMap, min_provers: HashMap, @@ -73,6 +81,16 @@ pub struct Scaler { long_pending_duration: chrono::Duration, } +pub struct SimpleScaler { + queue_report_field: QueueReportFields, + deployment: String, + /// Which cluster to use first. + cluster_priorities: HashMap, + max_replicas: HashMap, + speed: usize, + long_pending_duration: chrono::Duration, +} + struct ProverPodGpu<'a> { name: &'a str, pod: &'a Pod, @@ -102,10 +120,31 @@ impl Scaler { AUTOSCALER_METRICS.prover_protocol_version[&(namespace.clone(), version.clone())] .set(1); }); + + let mut simple_scalers = Vec::default(); + let mut jobs = vec![QueueReportFields::prover_jobs]; + for c in &config.scaler_targets { + jobs.push(c.queue_report_field); + simple_scalers.push(SimpleScaler::new( + c, + config.cluster_priorities.clone(), + chrono::Duration::seconds(config.long_pending_duration.whole_seconds()), + )) + } Self { - namespaces: config.protocol_versions, + namespaces: config.protocol_versions.clone(), watcher, queuer, + jobs, + prover_scaler: GpuScaler::new(config), + simple_scalers, + } + } +} + +impl GpuScaler { + pub fn new(config: ProverAutoscalerScalerConfig) -> Self { + Self { cluster_priorities: config.cluster_priorities, min_provers: config.min_provers, max_provers: config.max_provers, @@ -116,6 +155,7 @@ impl Scaler { } } + /// Converts a single cluster into vec of GPUPools, one for each GPU. fn convert_to_gpu_pool(&self, namespace: &String, cluster: &Cluster) -> Vec { let mut gp_map = HashMap::new(); // let Some(namespace_value) = &cluster.namespaces.get(namespace) else { @@ -218,6 +258,10 @@ impl Scaler { .then(b.max_pool_size.cmp(&a.max_pool_size)) // Reverse sort by cluster size. }); + gpu_pools.iter().for_each(|p| { + AUTOSCALER_METRICS.scale_errors[&p.name.clone()].set(p.scale_errors as u64); + }); + gpu_pools } @@ -321,47 +365,273 @@ impl Scaler { provers } + + fn diff( + namespace: &str, + provers: HashMap, + clusters: &Clusters, + requests: &mut HashMap, + ) { + provers + .into_iter() + .for_each(|(GPUPoolKey { cluster, gpu }, replicas)| { + let prover = gpu_to_prover(gpu); + clusters + .clusters + .get(&cluster) + .and_then(|c| c.namespaces.get(namespace)) + .and_then(|ns| ns.deployments.get(&prover)) + .map_or_else( + || { + tracing::error!( + "Wasn't able to find deployment {} in cluster {}, namespace {}", + prover, + cluster, + namespace + ) + }, + |deployment| { + if deployment.desired != replicas as i32 { + requests + .entry(cluster.clone()) + .or_default() + .deployments + .push(ScaleDeploymentRequest { + namespace: namespace.into(), + name: prover.clone(), + size: replicas as i32, + }); + } + }, + ); + }) + } +} + +#[derive(Default, Debug, PartialEq, Eq)] +struct Pool { + name: String, + pods: HashMap, + scale_errors: usize, + max_pool_size: usize, +} + +impl Pool { + fn sum_by_pod_status(&self, ps: PodStatus) -> usize { + self.pods.get(&ps).cloned().unwrap_or(0) + } } -fn diff( - namespace: &str, - provers: HashMap, - clusters: &Clusters, - requests: &mut HashMap, -) { - provers - .into_iter() - .for_each(|(GPUPoolKey { cluster, gpu }, n)| { - let prover = gpu_to_prover(gpu); +impl SimpleScaler { + pub fn new( + config: &ScalerTarget, + cluster_priorities: HashMap, + long_pending_duration: chrono::Duration, + ) -> Self { + Self { + queue_report_field: config.queue_report_field, + deployment: config.deployment.clone(), + cluster_priorities, + max_replicas: config.max_replicas.clone(), + speed: config.speed, + long_pending_duration, + } + } + + fn convert_to_pool(&self, namespace: &String, cluster: &Cluster) -> Option { + let Some(namespace_value) = &cluster.namespaces.get(namespace) else { + // No namespace in config, ignoring. + return None; + }; + + // TODO: Check if related deployment exists. + let mut pool = Pool { + name: cluster.name.clone(), + max_pool_size: self.max_replicas.get(&cluster.name).copied().unwrap_or(0), + scale_errors: namespace_value + .scale_errors + .iter() + .filter(|v| v.time < Utc::now() - chrono::Duration::hours(1)) // TODO Move the duration into config. + .count(), + ..Default::default() + }; + + // Initialize pool only if we have ready deployments. + pool.pods.insert(PodStatus::Running, 0); + + let pod_re = Regex::new(&format!("^{}-", self.deployment)).unwrap(); + for (_, pod) in namespace_value + .pods + .iter() + .filter(|(name, _)| pod_re.is_match(name)) + { + let mut status = PodStatus::from_str(&pod.status).unwrap_or_default(); + if status == PodStatus::Pending && pod.changed < Utc::now() - self.long_pending_duration + { + status = PodStatus::LongPending; + } + pool.pods.entry(status).and_modify(|n| *n += 1).or_insert(1); + } + + tracing::debug!("Pool pods {:?}", pool); + + Some(pool) + } + + fn sorted_clusters(&self, namespace: &String, clusters: &Clusters) -> Vec { + let mut pools: Vec = clusters + .clusters + .values() + .flat_map(|c| self.convert_to_pool(namespace, c)) + .collect(); + + pools.sort_by(|a, b| { + a.sum_by_pod_status(PodStatus::NeedToMove) + .cmp(&b.sum_by_pod_status(PodStatus::NeedToMove)) // Sort by need to evict. + .then( + a.sum_by_pod_status(PodStatus::LongPending) + .cmp(&b.sum_by_pod_status(PodStatus::LongPending)), + ) // Sort by long Pending pods. + .then(a.scale_errors.cmp(&b.scale_errors)) // Sort by scale_errors in the cluster. + .then( + self.cluster_priorities + .get(&a.name) + .unwrap_or(&1000) + .cmp(self.cluster_priorities.get(&b.name).unwrap_or(&1000)), + ) // Sort by priority. + .then(b.max_pool_size.cmp(&a.max_pool_size)) // Reverse sort by cluster size. + }); + + pools + } + + fn pods_to_speed(&self, n: usize) -> u64 { + (self.speed * n) as u64 + } + + fn normalize_queue(&self, queue: u64) -> u64 { + let speed = self.speed as u64; + // Divide and round up if there's any remainder. + (queue + speed - 1) / speed * speed + } + + fn run(&self, namespace: &String, queue: u64, clusters: &Clusters) -> HashMap { + let sorted_clusters = self.sorted_clusters(namespace, clusters); + tracing::debug!( + "Sorted clusters for namespace {}: {:?}", + namespace, + &sorted_clusters + ); + + let mut total: i64 = 0; + let mut pods: HashMap = HashMap::new(); + for cluster in &sorted_clusters { + for (status, replicas) in &cluster.pods { + match status { + PodStatus::Running | PodStatus::Pending => { + total += self.pods_to_speed(*replicas) as i64; + pods.entry(cluster.name.clone()) + .and_modify(|x| *x += replicas) + .or_insert(*replicas); + } + _ => (), // Ignore LongPending as not running here. + } + } + } + + // Remove unneeded pods. + if (total as u64) > self.normalize_queue(queue) { + for cluster in sorted_clusters.iter().rev() { + let mut excess_queue = total as u64 - self.normalize_queue(queue); + let mut excess_pods = excess_queue as usize / self.speed; + let replicas = pods.entry(cluster.name.clone()).or_default(); + if *replicas < excess_pods { + excess_pods = *replicas; + excess_queue = *replicas as u64 * self.speed as u64; + } + *replicas -= excess_pods; + total -= excess_queue as i64; + if total <= 0 { + break; + }; + } + } + + // Reduce load in over capacity pools. + for cluster in &sorted_clusters { + let replicas = pods.entry(cluster.name.clone()).or_default(); + if cluster.max_pool_size < *replicas { + let excess = *replicas - cluster.max_pool_size; + total -= (excess * self.speed) as i64; + *replicas -= excess; + } + } + + tracing::debug!("Queue covered with provers: {}", total); + // Add required pods. + if (total as u64) < queue { + for cluster in &sorted_clusters { + let mut required_queue = queue - total as u64; + let mut required_pods = self.normalize_queue(required_queue) as usize / self.speed; + let replicas = pods.entry(cluster.name.clone()).or_default(); + if *replicas + required_pods > cluster.max_pool_size { + required_pods = cluster.max_pool_size - *replicas; + required_queue = (required_pods * self.speed) as u64; + } + *replicas += required_pods; + total += required_queue as i64; + } + } + + tracing::debug!( + "run result for namespace {}: provers {:?}, total: {}", + namespace, + &pods, + total + ); + + pods + } + + fn diff( + &self, + namespace: &str, + replicas: HashMap, + clusters: &Clusters, + requests: &mut HashMap, + ) { + let deployment_name = self.deployment.clone(); + replicas.into_iter().for_each(|(cluster, replicas)| { clusters .clusters .get(&cluster) .and_then(|c| c.namespaces.get(namespace)) - .and_then(|ns| ns.deployments.get(&prover)) + .and_then(|ns| ns.deployments.get(&deployment_name)) .map_or_else( || { tracing::error!( "Wasn't able to find deployment {} in cluster {}, namespace {}", - prover, + deployment_name, cluster, namespace ) }, - |d| { - if d.desired != n as i32 { + |deployment| { + if deployment.desired != replicas as i32 { requests .entry(cluster.clone()) .or_default() .deployments .push(ScaleDeploymentRequest { namespace: namespace.into(), - name: prover.clone(), - size: n as i32, + name: deployment_name.clone(), + size: replicas as i32, }); } }, ); }) + } } /// is_namespace_running returns true if there are some pods running in it. @@ -383,7 +653,7 @@ fn is_namespace_running(namespace: &str, clusters: &Clusters) -> bool { #[async_trait::async_trait] impl Task for Scaler { async fn invoke(&self) -> anyhow::Result<()> { - let queue = self.queuer.get_queue().await.unwrap(); + let queue = self.queuer.get_queue(&self.jobs).await.unwrap(); let mut scale_requests: HashMap = HashMap::new(); { @@ -396,15 +666,39 @@ impl Task for Scaler { } for (ns, ppv) in &self.namespaces { - let q = queue.queue.get(ppv).cloned().unwrap_or(0); + // Prover + let q = queue + .get(&(ppv.to_string(), QueueReportFields::prover_jobs)) + .cloned() + .unwrap_or(0); + AUTOSCALER_METRICS.queue[&(ns.clone(), "prover".into())].set(q); tracing::debug!("Running eval for namespace {ns} and PPV {ppv} found queue {q}"); if q > 0 || is_namespace_running(ns, &guard.clusters) { - let provers = self.run(ns, q, &guard.clusters); + let provers = self.prover_scaler.run(ns, q, &guard.clusters); for (k, num) in &provers { AUTOSCALER_METRICS.provers[&(k.cluster.clone(), ns.clone(), k.gpu)] .set(*num as u64); } - diff(ns, provers, &guard.clusters, &mut scale_requests); + GpuScaler::diff(ns, provers, &guard.clusters, &mut scale_requests); + } + + // Simple Scalers. + for scaler in &self.simple_scalers { + let q = queue + .get(&(ppv.to_string(), scaler.queue_report_field)) + .cloned() + .unwrap_or(0); + AUTOSCALER_METRICS.queue[&(ns.clone(), scaler.deployment.clone())].set(q); + tracing::debug!("Running eval for namespace {ns}, PPV {ppv}, simple scaler {} found queue {q}", scaler.deployment); + if q > 0 || is_namespace_running(ns, &guard.clusters) { + let replicas = scaler.run(ns, q, &guard.clusters); + for (k, num) in &replicas { + AUTOSCALER_METRICS.jobs + [&(scaler.deployment.clone(), k.clone(), ns.clone())] + .set(*num as u64); + } + scaler.diff(ns, replicas, &guard.clusters, &mut scale_requests); + } } } } // Unlock self.watcher.data. @@ -420,28 +714,21 @@ impl Task for Scaler { #[cfg(test)] mod tests { use super::*; - use crate::{ - cluster_types::{Deployment, Namespace, Pod}, - global::{queuer, watcher}, - }; + use crate::cluster_types::{Deployment, Namespace, Pod, ScaleEvent}; #[tracing_test::traced_test] #[test] fn test_run() { - let scaler = Scaler::new( - watcher::Watcher::default(), - queuer::Queuer::default(), - ProverAutoscalerScalerConfig { - cluster_priorities: [("foo".into(), 0), ("bar".into(), 10)].into(), - min_provers: [("prover-other".into(), 2)].into(), - max_provers: [ - ("foo".into(), [(Gpu::L4, 100)].into()), - ("bar".into(), [(Gpu::L4, 100)].into()), - ] - .into(), - ..Default::default() - }, - ); + let scaler = GpuScaler::new(ProverAutoscalerScalerConfig { + cluster_priorities: [("foo".into(), 0), ("bar".into(), 10)].into(), + min_provers: [("prover-other".into(), 2)].into(), + max_provers: [ + ("foo".into(), [(Gpu::L4, 100)].into()), + ("bar".into(), [(Gpu::L4, 100)].into()), + ] + .into(), + ..Default::default() + }); assert_eq!( scaler.run( @@ -570,20 +857,16 @@ mod tests { #[tracing_test::traced_test] #[test] fn test_run_min_provers() { - let scaler = Scaler::new( - watcher::Watcher::default(), - queuer::Queuer::default(), - ProverAutoscalerScalerConfig { - cluster_priorities: [("foo".into(), 0), ("bar".into(), 10)].into(), - min_provers: [("prover".into(), 2)].into(), - max_provers: [ - ("foo".into(), [(Gpu::L4, 100)].into()), - ("bar".into(), [(Gpu::L4, 100)].into()), - ] - .into(), - ..Default::default() - }, - ); + let scaler = GpuScaler::new(ProverAutoscalerScalerConfig { + cluster_priorities: [("foo".into(), 0), ("bar".into(), 10)].into(), + min_provers: [("prover".into(), 2)].into(), + max_provers: [ + ("foo".into(), [(Gpu::L4, 100)].into()), + ("bar".into(), [(Gpu::L4, 100)].into()), + ] + .into(), + ..Default::default() + }); assert_eq!( scaler.run( @@ -765,4 +1048,121 @@ mod tests { "Min 2 provers, 5 running" ); } + + #[tracing_test::traced_test] + #[test] + fn test_run_need_move() { + let scaler = GpuScaler::new(ProverAutoscalerScalerConfig { + cluster_priorities: [("foo".into(), 0), ("bar".into(), 10)].into(), + min_provers: [("prover".into(), 2)].into(), + max_provers: [ + ("foo".into(), [(Gpu::L4, 100)].into()), + ("bar".into(), [(Gpu::L4, 100)].into()), + ] + .into(), + long_pending_duration: ProverAutoscalerScalerConfig::default_long_pending_duration(), + ..Default::default() + }); + + assert_eq!( + scaler.run( + &"prover".into(), + 1400, + &Clusters { + clusters: [ + ( + "foo".into(), + Cluster { + name: "foo".into(), + namespaces: [( + "prover".into(), + Namespace { + deployments: [( + "circuit-prover-gpu".into(), + Deployment { + running: 3, + desired: 3, + }, + )] + .into(), + pods: [ + ( + "circuit-prover-gpu-7c5f8fc747-gmtcr".into(), + Pod { + status: "Running".into(), + changed: Utc::now(), + ..Default::default() + }, + ), + ( + "circuit-prover-gpu-7c5f8fc747-gmtc2".into(), + Pod { + status: "Pending".into(), + changed: Utc::now(), + ..Default::default() + }, + ), + ( + "circuit-prover-gpu-7c5f8fc747-gmtc3".into(), + Pod { + status: "Running".into(), + changed: Utc::now(), + ..Default::default() + }, + ) + ] + .into(), + scale_errors: vec![ScaleEvent { + name: "circuit-prover-gpu-7c5f8fc747-gmtc2.123456" + .into(), + time: Utc::now() - chrono::Duration::hours(1) + }], + }, + )] + .into(), + }, + ), + ( + "bar".into(), + Cluster { + name: "bar".into(), + namespaces: [( + "prover".into(), + Namespace { + deployments: [( + "circuit-prover-gpu".into(), + Deployment::default(), + )] + .into(), + ..Default::default() + }, + )] + .into(), + }, + ) + ] + .into(), + ..Default::default() + }, + ), + [ + ( + GPUPoolKey { + cluster: "foo".into(), + gpu: Gpu::L4, + }, + 2, + ), + ( + GPUPoolKey { + cluster: "bar".into(), + gpu: Gpu::L4, + }, + 1, + ) + ] + .into(), + "Move 1 prover to bar" + ); + } } diff --git a/prover/crates/bin/prover_autoscaler/src/global/watcher.rs b/prover/crates/bin/prover_autoscaler/src/global/watcher.rs index 6e02c0fe2fd..95b9e32cac5 100644 --- a/prover/crates/bin/prover_autoscaler/src/global/watcher.rs +++ b/prover/crates/bin/prover_autoscaler/src/global/watcher.rs @@ -38,11 +38,12 @@ pub fn check_is_ready(v: &Vec) -> Result<()> { pub struct Watcher { /// List of base URLs of all agents. pub cluster_agents: Vec>, + pub dry_run: bool, pub data: Arc>, } impl Watcher { - pub fn new(agent_urls: Vec) -> Self { + pub fn new(agent_urls: Vec, dry_run: bool) -> Self { let size = agent_urls.len(); Self { cluster_agents: agent_urls @@ -54,6 +55,7 @@ impl Watcher { ) }) .collect(), + dry_run, data: Arc::new(Mutex::new(WatchedData { clusters: Clusters::default(), is_ready: vec![false; size], @@ -80,6 +82,7 @@ impl Watcher { .collect(); } + let dry_run = self.dry_run; let handles: Vec<_> = id_requests .into_iter() .map(|(id, sr)| { @@ -92,6 +95,10 @@ impl Watcher { tokio::spawn(async move { let mut headers = HeaderMap::new(); headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); + if dry_run { + tracing::info!("Dry-run mode, not sending the request."); + return Ok((id, Ok(ScaleResponse::default()))); + } let response = send_request_with_retries( &url, MAX_RETRIES, diff --git a/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs b/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs index 5384db082bc..707ff04f183 100644 --- a/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs +++ b/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs @@ -134,6 +134,11 @@ impl Watcher { } pod.status = phase; + if pod.status == "Succeeded" || pod.status == "Failed" { + // Cleaning up list of pods. + v.pods.remove(&p.name_any()); + } + tracing::info!("Got pod: {}", p.name_any()) } Watched::Event(e) => { diff --git a/prover/crates/bin/prover_autoscaler/src/main.rs b/prover/crates/bin/prover_autoscaler/src/main.rs index 45e476079a5..ac5121dccd9 100644 --- a/prover/crates/bin/prover_autoscaler/src/main.rs +++ b/prover/crates/bin/prover_autoscaler/src/main.rs @@ -110,7 +110,8 @@ async fn main() -> anyhow::Result<()> { let interval = scaler_config.scaler_run_interval.unsigned_abs(); let exporter_config = PrometheusExporterConfig::pull(scaler_config.prometheus_port); tasks.push(tokio::spawn(exporter_config.run(stop_receiver.clone()))); - let watcher = global::watcher::Watcher::new(scaler_config.agents.clone()); + let watcher = + global::watcher::Watcher::new(scaler_config.agents.clone(), scaler_config.dry_run); let queuer = global::queuer::Queuer::new(scaler_config.prover_job_monitor_url.clone()); let scaler = global::scaler::Scaler::new(watcher.clone(), queuer, scaler_config); tasks.extend(get_tasks(watcher, scaler, interval, stop_receiver)?); diff --git a/prover/crates/bin/prover_autoscaler/src/metrics.rs b/prover/crates/bin/prover_autoscaler/src/metrics.rs index d94ac8b97e9..39860a9e8f0 100644 --- a/prover/crates/bin/prover_autoscaler/src/metrics.rs +++ b/prover/crates/bin/prover_autoscaler/src/metrics.rs @@ -10,10 +10,15 @@ pub(crate) struct AutoscalerMetrics { pub prover_protocol_version: LabeledFamily<(String, String), Gauge, 2>, #[metrics(labels = ["target_cluster", "target_namespace", "gpu"])] pub provers: LabeledFamily<(String, String, Gpu), Gauge, 3>, + #[metrics(labels = ["job", "target_cluster", "target_namespace"])] + pub jobs: LabeledFamily<(String, String, String), Gauge, 3>, pub clusters_not_ready: Counter, #[metrics(labels = ["target", "status"])] pub calls: LabeledFamily<(String, u16), Counter, 2>, - // TODO: count of command send succes/fail + #[metrics(labels = ["target_cluster"])] + pub scale_errors: LabeledFamily>, + #[metrics(labels = ["target_namespace", "job"])] + pub queue: LabeledFamily<(String, String), Gauge, 2>, } #[vise::register] diff --git a/prover/crates/bin/prover_job_monitor/src/main.rs b/prover/crates/bin/prover_job_monitor/src/main.rs index 9195b92882d..8511135225a 100644 --- a/prover/crates/bin/prover_job_monitor/src/main.rs +++ b/prover/crates/bin/prover_job_monitor/src/main.rs @@ -159,7 +159,7 @@ fn get_tasks( prover_jobs_archiver, ); - // job requeuers + // job re-queuers let proof_compressor_job_requeuer = ProofCompressorJobRequeuer::new( proof_compressor_config.max_attempts, proof_compressor_config.generation_timeout(), diff --git a/zkstack_cli/crates/config/src/contracts.rs b/zkstack_cli/crates/config/src/contracts.rs index 6d336b5cfc1..79044a59f3a 100644 --- a/zkstack_cli/crates/config/src/contracts.rs +++ b/zkstack_cli/crates/config/src/contracts.rs @@ -7,7 +7,7 @@ use crate::{ deploy_ecosystem::output::DeployL1Output, deploy_l2_contracts::output::{ ConsensusRegistryOutput, DefaultL2UpgradeOutput, InitializeBridgeOutput, - Multicall3Output, + Multicall3Output, TimestampAsserterOutput, }, register_chain::output::RegisterChainOutput, }, @@ -109,6 +109,14 @@ impl ContractsConfig { self.l2.multicall3 = Some(multicall3_output.multicall3); Ok(()) } + + pub fn set_timestamp_asserter_addr( + &mut self, + timestamp_asserter_output: &TimestampAsserterOutput, + ) -> anyhow::Result<()> { + self.l2.timestamp_asserter_addr = Some(timestamp_asserter_output.timestamp_asserter); + Ok(()) + } } impl FileConfigWithDefaultName for ContractsConfig { @@ -161,4 +169,5 @@ pub struct L2Contracts { pub consensus_registry: Option
, pub multicall3: Option
, pub legacy_shared_bridge_addr: Option
, + pub timestamp_asserter_addr: Option
, } diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs index 29be89b9101..7b2b56c8154 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs @@ -8,6 +8,8 @@ impl ZkStackConfig for DefaultL2UpgradeOutput {} impl ZkStackConfig for ConsensusRegistryOutput {} impl ZkStackConfig for Multicall3Output {} +impl ZkStackConfig for TimestampAsserterOutput {} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InitializeBridgeOutput { pub l2_shared_bridge_implementation: Address, @@ -29,3 +31,8 @@ pub struct ConsensusRegistryOutput { pub struct Multicall3Output { pub multicall3: Address, } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TimestampAsserterOutput { + pub timestamp_asserter: Address, +} diff --git a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh index 4df431754c8..f1cfc994673 100644 --- a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh +++ b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh @@ -15,7 +15,7 @@ _zkstack() { local context curcontext="$curcontext" state line _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -37,7 +37,7 @@ _arguments "${_arguments_options[@]}" : \ '--generate=[The shell to generate the autocomplete script for]:GENERATOR:(bash elvish fish powershell zsh)' \ '-o+[The out directory to write the autocomplete script to]:OUT:_files' \ '--out=[The out directory to write the autocomplete script to]:OUT:_files' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -47,7 +47,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (ecosystem) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -65,11 +65,11 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (create) _arguments "${_arguments_options[@]}" : \ -'--ecosystem-name=[]:ECOSYSTEM_NAME: ' \ +'--ecosystem-name=[]:ECOSYSTEM_NAME:_default' \ '--l1-network=[L1 Network]:L1_NETWORK:(localhost sepolia holesky mainnet)' \ '--link-to-code=[Code link]:LINK_TO_CODE:_files -/' \ -'--chain-name=[]:CHAIN_NAME: ' \ -'--chain-id=[Chain ID]:CHAIN_ID: ' \ +'--chain-name=[]:CHAIN_NAME:_default' \ +'--chain-id=[Chain ID]:CHAIN_ID:_default' \ '--prover-mode=[Prover options]:PROVER_MODE:(no-proofs gpu)' \ '--wallet-creation=[Wallet options]:WALLET_CREATION:((localhost\:"Load wallets from localhost mnemonic, they are funded for localhost env" random\:"Generate random wallets" @@ -77,13 +77,13 @@ empty\:"Generate placeholder wallets" in-file\:"Specify file with wallets"))' \ '--wallet-path=[Wallet path]:WALLET_PATH:_files' \ '--l1-batch-commit-data-generator-mode=[Commit data generation mode]:L1_BATCH_COMMIT_DATA_GENERATOR_MODE:(rollup validium)' \ -'--base-token-address=[Base token address]:BASE_TOKEN_ADDRESS: ' \ -'--base-token-price-nominator=[Base token nominator]:BASE_TOKEN_PRICE_NOMINATOR: ' \ -'--base-token-price-denominator=[Base token denominator]:BASE_TOKEN_PRICE_DENOMINATOR: ' \ +'--base-token-address=[Base token address]:BASE_TOKEN_ADDRESS:_default' \ +'--base-token-price-nominator=[Base token nominator]:BASE_TOKEN_PRICE_NOMINATOR:_default' \ +'--base-token-price-denominator=[Base token denominator]:BASE_TOKEN_PRICE_DENOMINATOR:_default' \ '--set-as-default=[Set as default chain]' \ '--evm-emulator=[Enable EVM emulator]' \ '--start-containers=[Start reth and postgres containers after creation]' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--legacy-bridge[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -94,17 +94,17 @@ in-file\:"Specify file with wallets"))' \ ;; (build-transactions) _arguments "${_arguments_options[@]}" : \ -'--sender=[Address of the transaction sender]:SENDER: ' \ -'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL: ' \ +'--sender=[Address of the transaction sender]:SENDER:_default' \ +'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ '-o+[Output directory for the generated files]:OUT:_files' \ '--out=[Output directory for the generated files]:OUT:_files' \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -118,19 +118,19 @@ _arguments "${_arguments_options[@]}" : \ '--deploy-erc20=[Deploy ERC20 contracts]' \ '--deploy-ecosystem=[Deploy ecosystem contracts]' \ '--ecosystem-contracts-path=[Path to ecosystem contracts]:ECOSYSTEM_CONTRACTS_PATH:_files' \ -'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL: ' \ +'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--deploy-paymaster=[Deploy Paymaster contract]' \ -'--server-db-url=[Server database url without database name]:SERVER_DB_URL: ' \ -'--server-db-name=[Server database name]:SERVER_DB_NAME: ' \ +'--server-db-url=[Server database url without database name]:SERVER_DB_URL:_default' \ +'--server-db-name=[Server database name]:SERVER_DB_NAME:_default' \ '-o+[Enable Grafana]' \ '--observability=[Enable Grafana]' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-d[]' \ '--dont-drop[]' \ @@ -146,18 +146,18 @@ _arguments "${_arguments_options[@]}" : \ ;; (change-default-chain) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ '-h[Print help]' \ '--help[Print help]' \ -'::name:' \ +'::name:_default' \ && ret=0 ;; (setup-observability) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -211,7 +211,7 @@ esac ;; (chain) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -229,8 +229,8 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (create) _arguments "${_arguments_options[@]}" : \ -'--chain-name=[]:CHAIN_NAME: ' \ -'--chain-id=[Chain ID]:CHAIN_ID: ' \ +'--chain-name=[]:CHAIN_NAME:_default' \ +'--chain-id=[Chain ID]:CHAIN_ID:_default' \ '--prover-mode=[Prover options]:PROVER_MODE:(no-proofs gpu)' \ '--wallet-creation=[Wallet options]:WALLET_CREATION:((localhost\:"Load wallets from localhost mnemonic, they are funded for localhost env" random\:"Generate random wallets" @@ -238,12 +238,12 @@ empty\:"Generate placeholder wallets" in-file\:"Specify file with wallets"))' \ '--wallet-path=[Wallet path]:WALLET_PATH:_files' \ '--l1-batch-commit-data-generator-mode=[Commit data generation mode]:L1_BATCH_COMMIT_DATA_GENERATOR_MODE:(rollup validium)' \ -'--base-token-address=[Base token address]:BASE_TOKEN_ADDRESS: ' \ -'--base-token-price-nominator=[Base token nominator]:BASE_TOKEN_PRICE_NOMINATOR: ' \ -'--base-token-price-denominator=[Base token denominator]:BASE_TOKEN_PRICE_DENOMINATOR: ' \ +'--base-token-address=[Base token address]:BASE_TOKEN_ADDRESS:_default' \ +'--base-token-price-nominator=[Base token nominator]:BASE_TOKEN_PRICE_NOMINATOR:_default' \ +'--base-token-price-denominator=[Base token denominator]:BASE_TOKEN_PRICE_DENOMINATOR:_default' \ '--set-as-default=[Set as default chain]' \ '--evm-emulator=[Enable EVM emulator]' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--legacy-bridge[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -258,12 +258,12 @@ _arguments "${_arguments_options[@]}" : \ '--out=[Output directory for the generated files]:OUT:_files' \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -276,15 +276,15 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--server-db-url=[Server database url without database name]:SERVER_DB_URL: ' \ -'--server-db-name=[Server database name]:SERVER_DB_NAME: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--server-db-url=[Server database url without database name]:SERVER_DB_URL:_default' \ +'--server-db-name=[Server database name]:SERVER_DB_NAME:_default' \ '--deploy-paymaster=[]' \ -'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-d[]' \ '--dont-drop[]' \ @@ -307,10 +307,10 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (configs) _arguments "${_arguments_options[@]}" : \ -'--server-db-url=[Server database url without database name]:SERVER_DB_URL: ' \ -'--server-db-name=[Server database name]:SERVER_DB_NAME: ' \ -'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--server-db-url=[Server database url without database name]:SERVER_DB_URL:_default' \ +'--server-db-name=[Server database name]:SERVER_DB_NAME:_default' \ +'--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-d[Use default database urls and names]' \ '--dev[Use default database urls and names]' \ '-d[]' \ @@ -353,9 +353,9 @@ esac ;; (genesis) _arguments "${_arguments_options[@]}" : \ -'--server-db-url=[Server database url without database name]:SERVER_DB_URL: ' \ -'--server-db-name=[Server database name]:SERVER_DB_NAME: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--server-db-url=[Server database url without database name]:SERVER_DB_URL:_default' \ +'--server-db-name=[Server database name]:SERVER_DB_NAME:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-d[Use default database urls and names]' \ '--dev[Use default database urls and names]' \ '-d[]' \ @@ -377,9 +377,9 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (init-database) _arguments "${_arguments_options[@]}" : \ -'--server-db-url=[Server database url without database name]:SERVER_DB_URL: ' \ -'--server-db-name=[Server database name]:SERVER_DB_NAME: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--server-db-url=[Server database url without database name]:SERVER_DB_URL:_default' \ +'--server-db-name=[Server database name]:SERVER_DB_NAME:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-d[Use default database urls and names]' \ '--dev[Use default database urls and names]' \ '-d[]' \ @@ -393,7 +393,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (server) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -437,11 +437,11 @@ esac _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -454,11 +454,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -471,11 +471,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -488,11 +488,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -505,11 +505,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -522,11 +522,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -539,11 +539,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -556,11 +556,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -573,11 +573,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--verify=[Verify deployed contracts]' \ '--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL: ' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY: ' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ +'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ +'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -700,7 +700,7 @@ esac ;; (dev) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -718,7 +718,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (database) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -738,11 +738,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '-p+[Prover database]' \ '--prover=[Prover database]' \ -'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL: ' \ +'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL:_default' \ '-c+[Core database]' \ '--core=[Core database]' \ -'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -754,11 +754,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '-p+[Prover database]' \ '--prover=[Prover database]' \ -'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL: ' \ +'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL:_default' \ '-c+[Core database]' \ '--core=[Core database]' \ -'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -770,11 +770,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '-p+[Prover database]' \ '--prover=[Prover database]' \ -'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL: ' \ +'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL:_default' \ '-c+[Core database]' \ '--core=[Core database]' \ -'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -785,8 +785,8 @@ _arguments "${_arguments_options[@]}" : \ (new-migration) _arguments "${_arguments_options[@]}" : \ '--database=[Database to create new migration for]:DATABASE:(prover core)' \ -'--name=[Migration name]:NAME: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--name=[Migration name]:NAME:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -798,11 +798,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '-p+[Prover database]' \ '--prover=[Prover database]' \ -'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL: ' \ +'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL:_default' \ '-c+[Core database]' \ '--core=[Core database]' \ -'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -814,11 +814,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '-p+[Prover database]' \ '--prover=[Prover database]' \ -'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL: ' \ +'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL:_default' \ '-c+[Core database]' \ '--core=[Core database]' \ -'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -830,11 +830,11 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '-p+[Prover database]' \ '--prover=[Prover database]' \ -'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL: ' \ +'--prover-url=[URL of the Prover database. If not specified, it is used from the current chain'\''s secrets]:PROVER_URL:_default' \ '-c+[Core database]' \ '--core=[Core database]' \ -'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--core-url=[URL of the Core database. If not specified, it is used from the current chain'\''s secrets.]:CORE_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -896,7 +896,7 @@ esac ;; (test) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -914,9 +914,9 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (integration) _arguments "${_arguments_options[@]}" : \ -'-t+[Run just the tests matching a pattern. Same as the -t flag on jest.]:TEST_PATTERN: ' \ -'--test-pattern=[Run just the tests matching a pattern. Same as the -t flag on jest.]:TEST_PATTERN: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'-t+[Run just the tests matching a pattern. Same as the -t flag on jest.]:TEST_PATTERN:_default' \ +'--test-pattern=[Run just the tests matching a pattern. Same as the -t flag on jest.]:TEST_PATTERN:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-e[Run tests for external node]' \ '--external-node[Run tests for external node]' \ '-n[Do not install or build dependencies]' \ @@ -930,7 +930,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (fees) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-n[Do not install or build dependencies]' \ '--no-deps[Do not install or build dependencies]' \ '--no-kill[The test will not kill all the nodes during execution]' \ @@ -943,7 +943,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (revert) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--enable-consensus[Enable consensus]' \ '-e[Run tests for external node]' \ '--external-node[Run tests for external node]' \ @@ -959,7 +959,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (recovery) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-s[Run recovery from a snapshot instead of genesis]' \ '--snapshot[Run recovery from a snapshot instead of genesis]' \ '-n[Do not install or build dependencies]' \ @@ -974,7 +974,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (upgrade) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-n[Do not install or build dependencies]' \ '--no-deps[Do not install or build dependencies]' \ '-v[Verbose mode]' \ @@ -986,7 +986,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (build) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -996,8 +996,8 @@ _arguments "${_arguments_options[@]}" : \ ;; (rust) _arguments "${_arguments_options[@]}" : \ -'--options=[Cargo test flags]:OPTIONS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--options=[Cargo test flags]:OPTIONS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1007,7 +1007,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (l1-contracts) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1017,7 +1017,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (prover) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1027,7 +1027,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (wallet) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1037,7 +1037,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (loadtest) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1115,7 +1115,7 @@ esac ;; (clean) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1133,7 +1133,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (all) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1143,7 +1143,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (containers) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1153,7 +1153,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (contracts-cache) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1199,7 +1199,7 @@ esac ;; (snapshot) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1217,7 +1217,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (create) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1257,7 +1257,7 @@ esac _arguments "${_arguments_options[@]}" : \ '*-t+[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ '*--targets=[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-c[]' \ '--check[]' \ '-v[Verbose mode]' \ @@ -1269,7 +1269,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (fmt) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-c[]' \ '--check[]' \ '-v[Verbose mode]' \ @@ -1289,7 +1289,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (rustfmt) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1299,7 +1299,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (contract) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1311,7 +1311,7 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '*-t+[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ '*--targets=[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1357,7 +1357,7 @@ esac ;; (prover) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1375,7 +1375,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (info) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1385,9 +1385,9 @@ _arguments "${_arguments_options[@]}" : \ ;; (insert-batch) _arguments "${_arguments_options[@]}" : \ -'--number=[]:NUMBER: ' \ -'--version=[]:VERSION: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--number=[]:NUMBER:_default' \ +'--version=[]:VERSION:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--default[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -1398,9 +1398,9 @@ _arguments "${_arguments_options[@]}" : \ ;; (insert-version) _arguments "${_arguments_options[@]}" : \ -'--version=[]:VERSION: ' \ -'--snark-wrapper=[]:SNARK_WRAPPER: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--version=[]:VERSION:_default' \ +'--snark-wrapper=[]:SNARK_WRAPPER:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--default[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -1451,7 +1451,7 @@ _arguments "${_arguments_options[@]}" : \ '--l2-contracts=[Build L2 contracts]' \ '--system-contracts=[Build system contracts]' \ '--test-contracts=[Build test contracts]' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1461,9 +1461,9 @@ _arguments "${_arguments_options[@]}" : \ ;; (config-writer) _arguments "${_arguments_options[@]}" : \ -'-p+[Path to the config file to override]:PATH: ' \ -'--path=[Path to the config file to override]:PATH: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'-p+[Path to the config file to override]:PATH:_default' \ +'--path=[Path to the config file to override]:PATH:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1474,10 +1474,10 @@ _arguments "${_arguments_options[@]}" : \ (send-transactions) _arguments "${_arguments_options[@]}" : \ '--file=[]:FILE:_files' \ -'--private-key=[]:PRIVATE_KEY: ' \ -'--l1-rpc-url=[]:L1_RPC_URL: ' \ -'--confirmations=[]:CONFIRMATIONS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--private-key=[]:PRIVATE_KEY:_default' \ +'--l1-rpc-url=[]:L1_RPC_URL:_default' \ +'--confirmations=[]:CONFIRMATIONS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1487,9 +1487,9 @@ _arguments "${_arguments_options[@]}" : \ ;; (status) _arguments "${_arguments_options[@]}" : \ -'-u+[URL of the health check endpoint]:URL: ' \ -'--url=[URL of the health check endpoint]:URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'-u+[URL of the health check endpoint]:URL:_default' \ +'--url=[URL of the health check endpoint]:URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1507,7 +1507,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (ports) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1545,7 +1545,7 @@ esac ;; (generate-genesis) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1827,7 +1827,7 @@ esac ;; (prover) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1845,35 +1845,35 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (init) _arguments "${_arguments_options[@]}" : \ -'--proof-store-dir=[]:PROOF_STORE_DIR: ' \ -'--bucket-base-url=[]:BUCKET_BASE_URL: ' \ -'--credentials-file=[]:CREDENTIALS_FILE: ' \ -'--bucket-name=[]:BUCKET_NAME: ' \ -'--location=[]:LOCATION: ' \ -'--project-id=[]:PROJECT_ID: ' \ +'--proof-store-dir=[]:PROOF_STORE_DIR:_default' \ +'--bucket-base-url=[]:BUCKET_BASE_URL:_default' \ +'--credentials-file=[]:CREDENTIALS_FILE:_default' \ +'--bucket-name=[]:BUCKET_NAME:_default' \ +'--location=[]:LOCATION:_default' \ +'--project-id=[]:PROJECT_ID:_default' \ '--shall-save-to-public-bucket=[]:SHALL_SAVE_TO_PUBLIC_BUCKET:(true false)' \ -'--public-store-dir=[]:PUBLIC_STORE_DIR: ' \ -'--public-bucket-base-url=[]:PUBLIC_BUCKET_BASE_URL: ' \ -'--public-credentials-file=[]:PUBLIC_CREDENTIALS_FILE: ' \ -'--public-bucket-name=[]:PUBLIC_BUCKET_NAME: ' \ -'--public-location=[]:PUBLIC_LOCATION: ' \ -'--public-project-id=[]:PUBLIC_PROJECT_ID: ' \ -'(--clone)--bellman-cuda-dir=[]:BELLMAN_CUDA_DIR: ' \ +'--public-store-dir=[]:PUBLIC_STORE_DIR:_default' \ +'--public-bucket-base-url=[]:PUBLIC_BUCKET_BASE_URL:_default' \ +'--public-credentials-file=[]:PUBLIC_CREDENTIALS_FILE:_default' \ +'--public-bucket-name=[]:PUBLIC_BUCKET_NAME:_default' \ +'--public-location=[]:PUBLIC_LOCATION:_default' \ +'--public-project-id=[]:PUBLIC_PROJECT_ID:_default' \ +'(--clone)--bellman-cuda-dir=[]:BELLMAN_CUDA_DIR:_default' \ '--bellman-cuda=[]' \ '--setup-compressor-key=[]' \ -'--path=[]:PATH: ' \ +'--path=[]:PATH:_default' \ '--region=[]:REGION:(us europe asia)' \ '--mode=[]:MODE:(download generate)' \ '--setup-keys=[]' \ '--setup-database=[]:SETUP_DATABASE:(true false)' \ -'--prover-db-url=[Prover database url without database name]:PROVER_DB_URL: ' \ -'--prover-db-name=[Prover database name]:PROVER_DB_NAME: ' \ +'--prover-db-url=[Prover database url without database name]:PROVER_DB_URL:_default' \ +'--prover-db-name=[Prover database name]:PROVER_DB_NAME:_default' \ '-u+[Use default database urls and names]:USE_DEFAULT:(true false)' \ '--use-default=[Use default database urls and names]:USE_DEFAULT:(true false)' \ '-d+[]:DONT_DROP:(true false)' \ '--dont-drop=[]:DONT_DROP:(true false)' \ '--cloud-type=[]:CLOUD_TYPE:(gcp local)' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--dev[]' \ '(--bellman-cuda-dir)--clone[]' \ '-v[Verbose mode]' \ @@ -1887,7 +1887,7 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--region=[]:REGION:(us europe asia)' \ '--mode=[]:MODE:(download generate)' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1899,13 +1899,13 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ '--component=[]:COMPONENT:(gateway witness-generator witness-vector-generator prover circuit-prover compressor prover-job-monitor)' \ '--round=[]:ROUND:(all-rounds basic-circuits leaf-aggregation node-aggregation recursion-tip scheduler)' \ -'--threads=[]:THREADS: ' \ -'--max-allocation=[Memory allocation limit in bytes (for prover component)]:MAX_ALLOCATION: ' \ -'--witness-vector-generator-count=[]:WITNESS_VECTOR_GENERATOR_COUNT: ' \ -'--max-allocation=[]:MAX_ALLOCATION: ' \ +'--threads=[]:THREADS:_default' \ +'--max-allocation=[Memory allocation limit in bytes (for prover component)]:MAX_ALLOCATION:_default' \ +'--witness-vector-generator-count=[]:WITNESS_VECTOR_GENERATOR_COUNT:_default' \ +'--max-allocation=[]:MAX_ALLOCATION:_default' \ '--docker=[]:DOCKER:(true false)' \ -'--tag=[]:TAG: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--tag=[]:TAG:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1915,8 +1915,8 @@ _arguments "${_arguments_options[@]}" : \ ;; (init-bellman-cuda) _arguments "${_arguments_options[@]}" : \ -'(--clone)--bellman-cuda-dir=[]:BELLMAN_CUDA_DIR: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'(--clone)--bellman-cuda-dir=[]:BELLMAN_CUDA_DIR:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '(--bellman-cuda-dir)--clone[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -1927,8 +1927,8 @@ _arguments "${_arguments_options[@]}" : \ ;; (compressor-keys) _arguments "${_arguments_options[@]}" : \ -'--path=[]:PATH: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--path=[]:PATH:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -1982,10 +1982,10 @@ esac ;; (server) _arguments "${_arguments_options[@]}" : \ -'*--components=[Components of server to run]:COMPONENTS: ' \ -'*-a+[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'*--components=[Components of server to run]:COMPONENTS:_default' \ +'*-a+[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--genesis[Run server in genesis mode]' \ '--build[Build server but don'\''t run it]' \ '--uring[Enables uring support for RocksDB]' \ @@ -1998,7 +1998,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (external-node) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2016,10 +2016,10 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (configs) _arguments "${_arguments_options[@]}" : \ -'--db-url=[]:DB_URL: ' \ -'--db-name=[]:DB_NAME: ' \ -'--l1-rpc-url=[]:L1_RPC_URL: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--db-url=[]:DB_URL:_default' \ +'--db-name=[]:DB_NAME:_default' \ +'--l1-rpc-url=[]:L1_RPC_URL:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-u[Use default database urls and names]' \ '--use-default[Use default database urls and names]' \ '-v[Verbose mode]' \ @@ -2031,7 +2031,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (init) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2041,11 +2041,11 @@ _arguments "${_arguments_options[@]}" : \ ;; (run) _arguments "${_arguments_options[@]}" : \ -'*--components=[Components of server to run]:COMPONENTS: ' \ +'*--components=[Components of server to run]:COMPONENTS:_default' \ '--enable-consensus=[Enable consensus]' \ -'*-a+[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'*--additional-args=[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'*-a+[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'*--additional-args=[Additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--reinit[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -2094,7 +2094,7 @@ esac _arguments "${_arguments_options[@]}" : \ '-o+[Enable Grafana]' \ '--observability=[Enable Grafana]' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2104,7 +2104,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (contract-verifier) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2122,7 +2122,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (run) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2132,12 +2132,12 @@ _arguments "${_arguments_options[@]}" : \ ;; (init) _arguments "${_arguments_options[@]}" : \ -'--zksolc-version=[Version of zksolc to install]:ZKSOLC_VERSION: ' \ -'--zkvyper-version=[Version of zkvyper to install]:ZKVYPER_VERSION: ' \ -'--solc-version=[Version of solc to install]:SOLC_VERSION: ' \ -'--era-vm-solc-version=[Version of era vm solc to install]:ERA_VM_SOLC_VERSION: ' \ -'--vyper-version=[Version of vyper to install]:VYPER_VERSION: ' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--zksolc-version=[Version of zksolc to install]:ZKSOLC_VERSION:_default' \ +'--zkvyper-version=[Version of zkvyper to install]:ZKVYPER_VERSION:_default' \ +'--solc-version=[Version of solc to install]:SOLC_VERSION:_default' \ +'--era-vm-solc-version=[Version of era vm solc to install]:ERA_VM_SOLC_VERSION:_default' \ +'--vyper-version=[Version of vyper to install]:VYPER_VERSION:_default' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--only[Install only provided compilers]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -2180,7 +2180,7 @@ esac ;; (portal) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2190,7 +2190,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (explorer) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2208,7 +2208,7 @@ _arguments "${_arguments_options[@]}" : \ case $line[1] in (init) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2218,7 +2218,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (run-backend) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2228,7 +2228,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (run) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2274,7 +2274,7 @@ esac ;; (consensus) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2293,7 +2293,7 @@ _arguments "${_arguments_options[@]}" : \ (set-attester-committee) _arguments "${_arguments_options[@]}" : \ '--from-file=[Sets the attester committee in the consensus registry contract to the committee in the yaml file. File format is definied in \`commands/consensus/proto/mod.proto\`]:FROM_FILE:_files' \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '--from-genesis[Sets the attester committee in the consensus registry contract to \`consensus.genesis_spec.attesters\` in general.yaml]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ @@ -2304,7 +2304,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (get-attester-committee) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -2346,7 +2346,7 @@ esac ;; (update) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-c[Update only the config files]' \ '--only-config[Update only the config files]' \ '-v[Verbose mode]' \ @@ -2358,7 +2358,7 @@ _arguments "${_arguments_options[@]}" : \ ;; (markdown) _arguments "${_arguments_options[@]}" : \ -'--chain=[Chain to use]:CHAIN: ' \ +'--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/common.rs b/zkstack_cli/crates/zkstack/src/commands/chain/common.rs index e0aa0b4e047..0c35b3ee4fe 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/common.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/common.rs @@ -27,6 +27,9 @@ pub async fn distribute_eth( if let Some(deployer) = chain_wallets.deployer { addresses.push(deployer.address) } + if let Some(setter) = chain_wallets.token_multiplier_setter { + addresses.push(setter.address) + } common::ethereum::distribute_eth( wallets.operator, addresses, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs index 8dbd5c371c8..091bef86d26 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs @@ -12,7 +12,7 @@ use config::{ input::DeployL2ContractsInput, output::{ ConsensusRegistryOutput, DefaultL2UpgradeOutput, InitializeBridgeOutput, - Multicall3Output, + Multicall3Output, TimestampAsserterOutput, }, }, script_params::DEPLOY_L2_CONTRACTS_SCRIPT_PARAMS, @@ -236,6 +236,8 @@ pub async fn deploy_l2_contracts( contracts_config.set_default_l2_upgrade(&DefaultL2UpgradeOutput::read(shell, out)?)?; contracts_config.set_consensus_registry(&ConsensusRegistryOutput::read(shell, out)?)?; contracts_config.set_multicall3(&Multicall3Output::read(shell, out)?)?; + contracts_config + .set_timestamp_asserter_addr(&TimestampAsserterOutput::read(shell, out)?)?; Ok(()) }, )