diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index b743cb11f59e..fa33d4c8d7a1 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { - "core": "25.0.0", + "core": "25.1.0", "prover": "17.0.0", "zkstack_cli": "0.1.2" } diff --git a/.github/workflows/build-tee-prover-template.yml b/.github/workflows/build-tee-prover-template.yml index 0e5b80d2e3a2..c55e06931247 100644 --- a/.github/workflows/build-tee-prover-template.yml +++ b/.github/workflows/build-tee-prover-template.yml @@ -76,4 +76,3 @@ jobs: docker push "${repo}/${tag}" done done - diff --git a/.github/workflows/ci-common-reusable.yml b/.github/workflows/ci-common-reusable.yml index ea91fc4a7cd6..d57630d3029a 100644 --- a/.github/workflows/ci-common-reusable.yml +++ b/.github/workflows/ci-common-reusable.yml @@ -28,7 +28,7 @@ jobs: run: | run_retried docker-compose -f ${RUNNER_COMPOSE_FILE} pull docker-compose -f ${RUNNER_COMPOSE_FILE} up --build -d zk postgres - + - name: Install zkstack run: | ci_run ./zkstack_cli/zkstackup/install -g --path ./zkstack_cli/zkstackup/zkstackup @@ -38,4 +38,3 @@ jobs: # `zk lint prover` = cargo clippy, which does cargo check behind the scenes, which is a lightweight version of cargo build - name: Lints run: ci_run zkstack dev lint -t rs --check - diff --git a/.github/workflows/ci-core-lint-reusable.yml b/.github/workflows/ci-core-lint-reusable.yml index 0babbd1c9db7..0d4db601c467 100644 --- a/.github/workflows/ci-core-lint-reusable.yml +++ b/.github/workflows/ci-core-lint-reusable.yml @@ -50,6 +50,7 @@ jobs: ci_run zkstack dev lint -t ts --check ci_run zkstack dev lint -t rs --check ci_run zkstack dev lint -t autocompletion --check + ci_run zkstack dev lint -t rust-toolchain - name: Check Database run: | diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index c245e7341d03..da3e2d5abb56 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -34,6 +34,7 @@ jobs: echo "SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com" >> .env echo "SCCACHE_GCS_RW_MODE=READ_WRITE" >> .env echo "RUSTC_WRAPPER=sccache" >> .env + echo RUN_CONTRACT_VERIFICATION_TEST=true >> .env # TODO: Remove when we after upgrade of hardhat-plugins - name: pre-download compilers @@ -73,6 +74,9 @@ jobs: - name: Contracts unit tests run: ci_run yarn l1-contracts test + - name: Download compilers for contract verifier tests + run: ci_run zkstack contract-verifier init --zksolc-version=v1.5.3 --zkvyper-version=v1.5.4 --solc-version=0.8.26 --vyper-version=v0.3.10 --era-vm-solc-version=0.8.26-1.0.1 --only --chain era + - name: Rust unit tests run: | ci_run zkstack dev test rust diff --git a/.github/workflows/ci-prover-e2e.yml b/.github/workflows/ci-prover-e2e.yml index b0b9caf888fc..6076874c3710 100644 --- a/.github/workflows/ci-prover-e2e.yml +++ b/.github/workflows/ci-prover-e2e.yml @@ -100,7 +100,7 @@ jobs: - name: Kill prover & start compressor run: | sudo ./bin/prover_checkers/kill_prover - + ci_run zkstack prover run --component=compressor --docker=false &>prover_logs/compressor.log & - name: Wait for batch to be executed on L1 env: diff --git a/.github/workflows/new-build-contract-verifier-template.yml b/.github/workflows/new-build-contract-verifier-template.yml index 9b23cda6f02a..7e48968a65c1 100644 --- a/.github/workflows/new-build-contract-verifier-template.yml +++ b/.github/workflows/new-build-contract-verifier-template.yml @@ -176,7 +176,11 @@ jobs: - name: Download setup key shell: bash run: | - run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key + if [ -f "/setup_2^26.key" ]; then + cp '/setup_2^26.key' './setup_2^26.key' + else + run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key + fi - name: Set env vars shell: bash diff --git a/.github/workflows/new-build-core-template.yml b/.github/workflows/new-build-core-template.yml index c4aeb9180fda..350d689c4572 100644 --- a/.github/workflows/new-build-core-template.yml +++ b/.github/workflows/new-build-core-template.yml @@ -187,7 +187,11 @@ jobs: - name: Download setup key shell: bash run: | - run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key + if [ -f "/setup_2^26.key" ]; then + cp '/setup_2^26.key' './setup_2^26.key' + else + run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key + fi - name: Set env vars shell: bash diff --git a/.github/workflows/new-build-prover-template.yml b/.github/workflows/new-build-prover-template.yml index 5d42696c0b2a..046711d679e8 100644 --- a/.github/workflows/new-build-prover-template.yml +++ b/.github/workflows/new-build-prover-template.yml @@ -127,7 +127,6 @@ jobs: if: matrix.components == 'proof-fri-gpu-compressor' run: | run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^24.key - # We need to run this only when ERA_BELLMAN_CUDA_RELEASE is not available # In our case it happens only when PR is created from fork - name: Wait for runner IP to be not rate-limited against GH API diff --git a/.github/workflows/secrets_scanner.yaml b/.github/workflows/secrets_scanner.yaml index fa896bf10561..9bb1ad0a2722 100644 --- a/.github/workflows/secrets_scanner.yaml +++ b/.github/workflows/secrets_scanner.yaml @@ -11,7 +11,7 @@ jobs: with: fetch-depth: 0 - name: TruffleHog OSS - uses: trufflesecurity/trufflehog@0c66d30c1f4075cee1aada2e1ab46dabb1b0071a + uses: trufflesecurity/trufflehog@781157ae368b2218a0a56b889387dd26faa20f97 with: path: ./ base: ${{ github.event.repository.default_branch }} diff --git a/Cargo.lock b/Cargo.lock index eb2a72eb8c4a..16152c6bf698 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,16 +101,580 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "alloy" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367891bf380210abb0d6aa30c5f85a9080cb4a066c4d5c5acadad630823751b" +dependencies = [ + "alloy-consensus", + "alloy-contract", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-network", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-signer", + "alloy-signer-local", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", +] + +[[package]] +name = "alloy-chains" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836cf02383d9ebb35502d379bcd1ae803155094077eaab9c29131d888cd5fa3e" +dependencies = [ + "alloy-primitives", + "num_enum 0.7.3", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eefe64fd344cffa9cf9e3435ec4e93e6e9c3481bc37269af988bf497faf4a6a" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-provider", + "alloy-pubsub", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures 0.3.31", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-core" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72bf30967a232bec83809bea1623031f6285a013096229330c68c406192a4ca" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-rlp", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5228b189b18b85761340dc9eaac0141148a8503657b36f9bc3a869413d987ca" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow 0.6.20", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "k256 0.13.4", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f923dd5fca5f67a43d81ed3ebad0880bd41f6dd0ada930030353ac356c54cd0f" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more 1.0.0", + "once_cell", + "serde", + "sha2 0.10.8", +] + +[[package]] +name = "alloy-genesis" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31a0f0d51db8a1a30a4d98a9f90e090a94c8f44cb4d9eafc7e03aa6d00aae984" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c717b5298fad078cd3a418335b266eba91b511383ca9bd497f742d5975d5ab" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more 1.0.0", + "foldhash", + "hashbrown 0.15.0", + "hex-literal", + "indexmap 2.6.0", + "itoa", + "k256 0.13.4", + "keccak-asm", + "paste", + "proptest", + "rand 0.8.5", + "ruint", + "rustc-hash 2.0.0", + "serde", + "sha3 0.10.8", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "alloy-provider" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-pubsub", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "async-stream", + "async-trait", + "auto_impl", + "dashmap 6.1.0", + "futures 0.3.31", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest 0.12.9", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-pubsub" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d05f63677e210d758cd5d6d1ce10f20c980c3560ccfbe79ba1997791862a04f" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-transport", + "bimap", + "futures 0.3.31", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", +] + [[package]] name = "alloy-rlp" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" dependencies = [ + "alloy-rlp-derive", "arrayvec 0.7.6", "bytes", ] +[[package]] +name = "alloy-rlp-derive" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5" +dependencies = [ + "alloy-json-rpc", + "alloy-primitives", + "alloy-pubsub", + "alloy-transport", + "alloy-transport-http", + "alloy-transport-ipc", + "alloy-transport-ws", + "futures 0.3.31", + "pin-project", + "reqwest 0.12.9", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" +dependencies = [ + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more 1.0.0", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "cfg-if", + "derive_more 1.0.0", + "hashbrown 0.14.5", + "itertools 0.13.0", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-serde" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307324cca94354cd654d6713629f0383ec037e1ff9e3e3d547212471209860c0" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve 0.13.8", + "k256 0.13.4", + "thiserror", +] + +[[package]] +name = "alloy-signer-local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fabe917ab1778e760b4701628d1cae8e028ee9d52ac6307de4e1e9286ab6b5f" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-signer", + "async-trait", + "k256 0.13.4", + "rand 0.8.5", + "thiserror", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841eabaa4710f719fddbc24c95d386eae313f07e6da4babc25830ee37945be0c" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6672337f19d837b9f7073c45853aeb528ed9f7dd6a4154ce683e9e5cb7794014" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-input", + "const-hex", + "heck 0.5.0", + "indexmap 2.6.0", + "proc-macro-error2", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", + "syn-solidity", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dff37dd20bfb118b777c96eda83b2067f4226d2644c5cfa00187b3bc01770ba" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2 1.0.89", + "quote 1.0.37", + "serde_json", + "syn 2.0.85", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b853d42292dbb159671a3edae3b2750277ff130f32b726fe07dc2b17aa6f2b5" +dependencies = [ + "serde", + "winnow 0.6.20", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa828bb1b9a6dc52208fbb18084fb9ce2c30facc2bfda6a5d922349b4990354f" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9" +dependencies = [ + "alloy-json-rpc", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest 0.12.9", + "serde_json", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-ipc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fd8491249f74d16ec979b1f5672377b12ebb818e6056478ffa386954dbd350" +dependencies = [ + "alloy-json-rpc", + "alloy-pubsub", + "alloy-transport", + "bytes", + "futures 0.3.31", + "interprocess", + "pin-project", + "serde_json", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "alloy-transport-ws" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9704761f6297fe482276bee7f77a93cb42bd541c2bd6c1c560b6f3a9ece672e" +dependencies = [ + "alloy-pubsub", + "alloy-transport", + "futures 0.3.31", + "http 1.1.0", + "rustls 0.23.16", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "ws_stream_wasm", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -196,6 +760,39 @@ version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash 0.8.11", + "ark-ff 0.5.0", + "ark-poly", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.0", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "rayon", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -226,11 +823,32 @@ dependencies = [ "ark-std 0.4.0", "derivative", "digest 0.10.7", - "itertools 0.10.5", + "itertools 0.10.5", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec 0.7.6", + "digest 0.10.7", + "educe", + "itertools 0.13.0", "num-bigint 0.4.6", "num-traits", "paste", - "rustc_version 0.4.1", + "rayon", "zeroize", ] @@ -254,6 +872,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "ark-ff-macros" version = "0.3.0" @@ -279,6 +907,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash 0.8.11", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.0", + "rayon", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -300,6 +957,31 @@ dependencies = [ "num-bigint 0.4.6", ] +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.5.0", + "arrayvec 0.7.6", + "digest 0.10.7", + "num-bigint 0.4.6", + "rayon", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "ark-std" version = "0.3.0" @@ -320,6 +1002,17 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", + "rayon", +] + [[package]] name = "arr_macro" version = "0.1.3" @@ -644,6 +1337,17 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures 0.3.31", + "pharos", + "rustc_version 0.4.1", +] + [[package]] name = "atoi" version = "2.0.0" @@ -935,6 +1639,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + [[package]] name = "bincode" version = "1.3.3" @@ -960,7 +1670,7 @@ dependencies = [ "proc-macro2 1.0.89", "quote 1.0.37", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.85", ] @@ -982,7 +1692,7 @@ dependencies = [ "proc-macro2 1.0.89", "quote 1.0.37", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.85", "which", @@ -1057,16 +1767,28 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty 1.1.0", + "radium 0.6.2", + "tap", + "wyz 0.2.0", +] + [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", + "funty 2.0.0", + "radium 0.7.0", "tap", - "wyz", + "wyz 0.5.1", ] [[package]] @@ -1236,7 +1958,7 @@ dependencies = [ "crossbeam", "crypto-bigint 0.5.5", "derivative", - "ethereum-types", + "ethereum-types 0.14.1", "firestorm", "itertools 0.10.5", "lazy_static", @@ -1364,6 +2086,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c-kzg" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + [[package]] name = "camino" version = "1.1.9" @@ -1892,6 +2629,19 @@ dependencies = [ "compile-fmt", ] +[[package]] +name = "const-hex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -2486,6 +3236,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -2496,6 +3255,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -2507,6 +3278,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "dotenvy" version = "0.15.7" @@ -2610,6 +3387,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "either" version = "1.13.0" @@ -2692,6 +3481,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -2770,13 +3579,28 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ethabi" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c98847055d934070b90e806e12d3936b787d0a115068981c1d8dfd5dfef5a5" +dependencies = [ + "ethereum-types 0.12.1", + "hex", + "serde", + "serde_json", + "sha3 0.9.1", + "thiserror", + "uint", +] + [[package]] name = "ethabi" version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" dependencies = [ - "ethereum-types", + "ethereum-types 0.14.1", "hex", "once_cell", "regex", @@ -2787,6 +3611,19 @@ dependencies = [ "uint", ] +[[package]] +name = "ethbloom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" +dependencies = [ + "crunchy", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "tiny-keccak 2.0.2", +] + [[package]] name = "ethbloom" version = "0.13.0" @@ -2794,23 +3631,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", - "fixed-hash", + "fixed-hash 0.8.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "tiny-keccak 2.0.2", ] +[[package]] +name = "ethereum-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" +dependencies = [ + "ethbloom 0.11.1", + "fixed-hash 0.7.0", + "impl-rlp", + "impl-serde 0.3.2", + "primitive-types 0.10.1", + "uint", +] + [[package]] name = "ethereum-types" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ - "ethbloom", - "fixed-hash", + "ethbloom 0.13.0", + "fixed-hash 0.8.0", "impl-rlp", - "impl-serde", - "primitive-types", + "impl-serde 0.4.0", + "primitive-types 0.12.2", "uint", ] @@ -2922,6 +3773,18 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c5f6c2c942da57e2aaaa84b8a521489486f14e75e7fa91dab70aba913975f98" +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -3024,7 +3887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" dependencies = [ "cfg-if", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "scale-info", ] @@ -3035,7 +3898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cf1549fba25a6fcac22785b61698317d958e96cac72a59102ea45b9ae64692" dependencies = [ "cfg-if", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "scale-info", "serde", ] @@ -3085,6 +3948,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "funty" version = "2.0.0" @@ -3200,7 +4069,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" dependencies = [ "gloo-timers 0.2.6", - "send_wrapper", + "send_wrapper 0.4.0", ] [[package]] @@ -3222,6 +4091,12 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "generic-array" version = "0.14.7" @@ -3531,7 +4406,7 @@ dependencies = [ "log", "pest", "pest_derive", - "quick-error", + "quick-error 2.0.1", "serde", "serde_json", ] @@ -3565,6 +4440,7 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash", + "serde", ] [[package]] @@ -3623,6 +4499,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-conservative" @@ -3630,6 +4509,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hkdf" version = "0.12.4" @@ -3996,13 +4881,22 @@ dependencies = [ "version_check", ] +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec 2.3.1", +] + [[package]] name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 3.6.12", ] [[package]] @@ -4014,6 +4908,15 @@ dependencies = [ "rlp", ] +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + [[package]] name = "impl-serde" version = "0.4.0" @@ -4058,6 +4961,7 @@ checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown 0.15.0", + "serde", ] [[package]] @@ -4097,6 +5001,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "interprocess" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" +dependencies = [ + "doctest-file", + "futures-core", + "libc", + "recvmsg", + "tokio", + "widestring", + "windows-sys 0.52.0", +] + [[package]] name = "ipnet" version = "2.10.1" @@ -4289,7 +5208,7 @@ dependencies = [ "hyper 0.14.31", "jsonrpsee-types 0.21.0", "pin-project", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "thiserror", @@ -4317,7 +5236,7 @@ dependencies = [ "parking_lot", "pin-project", "rand 0.8.5", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "serde_json", "thiserror", @@ -4513,6 +5432,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -4522,6 +5451,14 @@ dependencies = [ "log", ] +[[package]] +name = "kzgpad-rs" +version = "0.1.0" +source = "git+https://github.com/Layr-Labs/kzgpad-rs.git?tag=v0.1.0#b5f8c8d3d6482407dc118cb1f51597a017a1cc89" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "lalrpop" version = "0.20.2" @@ -5572,6 +6509,12 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "2.10.1" @@ -5616,6 +6559,20 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec 0.7.6", + "bitvec 0.20.4", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive 2.3.1", + "serde", +] + [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -5623,13 +6580,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec 0.7.6", - "bitvec", + "bitvec 1.0.1", "byte-slice-cast", "impl-trait-for-tuples", - "parity-scale-codec-derive", + "parity-scale-codec-derive 3.6.12", "serde", ] +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 1.0.109", +] + [[package]] name = "parity-scale-codec-derive" version = "3.6.12" @@ -5809,6 +6778,16 @@ dependencies = [ "indexmap 2.6.0", ] +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures 0.3.31", + "rustc_version 0.4.1", +] + [[package]] name = "phf_shared" version = "0.10.0" @@ -6020,16 +6999,29 @@ dependencies = [ "elliptic-curve 0.13.8", ] +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash 0.7.0", + "impl-codec 0.5.1", + "impl-rlp", + "impl-serde 0.3.2", + "uint", +] + [[package]] name = "primitive-types" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ - "fixed-hash", - "impl-codec", + "fixed-hash 0.8.0", + "impl-codec 0.6.0", "impl-rlp", - "impl-serde", + "impl-serde 0.4.0", "scale-info", "uint", ] @@ -6077,6 +7069,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -6130,6 +7144,8 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ + "bit-set", + "bit-vec", "bitflags 2.6.0", "lazy_static", "num-traits", @@ -6137,6 +7153,8 @@ dependencies = [ "rand_chacha", "rand_xorshift", "regex-syntax 0.8.5", + "rusty-fork", + "tempfile", "unarray", ] @@ -6346,6 +7364,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-error" version = "2.0.1" @@ -6379,6 +7403,12 @@ dependencies = [ "proc-macro2 1.0.89", ] +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + [[package]] name = "radium" version = "0.7.0" @@ -6407,6 +7437,7 @@ dependencies = [ "libc", "rand_chacha", "rand_core 0.6.4", + "serde", ] [[package]] @@ -6499,6 +7530,12 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_syscall" version = "0.5.7" @@ -6751,7 +7788,7 @@ version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ - "bitvec", + "bitvec 1.0.1", "bytecheck", "bytes", "hashbrown 0.12.3", @@ -6833,8 +7870,8 @@ dependencies = [ "fastrlp", "num-bigint 0.4.6", "num-traits", - "parity-scale-codec", - "primitive-types", + "parity-scale-codec 3.6.12", + "primitive-types 0.12.2", "proptest", "rand 0.8.5", "rlp", @@ -6850,6 +7887,31 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rust-kzg-bn254" +version = "0.2.0" +source = "git+https://github.com/lambdaclass/rust-kzg-bn254?branch=bump-ark#18590ed7f2cf85c9b9cd4fb92b2d7d44a30588f3" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "ark-poly", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "byteorder", + "crossbeam-channel", + "directories", + "hex-literal", + "num-bigint 0.4.6", + "num-traits", + "num_cpus", + "rand 0.8.5", + "rayon", + "sha2 0.10.8", + "sys-info", + "ureq", +] + [[package]] name = "rust_decimal" version = "1.36.0" @@ -6878,6 +7940,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -7061,6 +8129,18 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "ruzstd" version = "0.5.0" @@ -7093,7 +8173,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "036575c29af9b6e4866ffb7fa055dbf623fe7a9cc159b33786de6013a6969d89" dependencies = [ - "parity-scale-codec", + "parity-scale-codec 3.6.12", "scale-info", "serde", ] @@ -7105,8 +8185,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7caaf753f8ed1ab4752c6afb20174f03598c664724e0e32628e161c21000ff76" dependencies = [ "derive_more 0.99.18", - "parity-scale-codec", - "primitive-types", + "parity-scale-codec 3.6.12", + "primitive-types 0.12.2", "scale-bits", "scale-decode-derive", "scale-info", @@ -7133,8 +8213,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d70cb4b29360105483fac1ed567ff95d65224a14dd275b6303ed0a654c78de5" dependencies = [ "derive_more 0.99.18", - "parity-scale-codec", - "primitive-types", + "parity-scale-codec 3.6.12", + "primitive-types 0.12.2", "scale-bits", "scale-encode-derive", "scale-info", @@ -7160,10 +8240,10 @@ version = "2.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aa7ffc1c0ef49b0452c6e2986abf2b07743320641ffd5fc63d552458e3b779b" dependencies = [ - "bitvec", + "bitvec 1.0.1", "cfg-if", "derive_more 1.0.0", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "scale-info-derive", "serde", ] @@ -7204,7 +8284,7 @@ dependencies = [ "derive_more 0.99.18", "either", "frame-metadata 15.1.0", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "scale-bits", "scale-decode", "scale-encode", @@ -7213,6 +8293,15 @@ dependencies = [ "yap", ] +[[package]] +name = "scc" +version = "2.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d25269dd3a12467afe2e510f69fb0b46b698e5afb296b59f2145259deaf8e8" +dependencies = [ + "sdd", +] + [[package]] name = "schannel" version = "0.1.26" @@ -7257,6 +8346,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sdd" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" + [[package]] name = "seahash" version = "4.1.0" @@ -7349,7 +8444,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap 4.5.20", - "ethabi", + "ethabi 18.0.0", "glob", "hex", "serde", @@ -7390,6 +8485,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "sentry" version = "0.31.8" @@ -7644,6 +8745,31 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "serial_test" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" +dependencies = [ + "futures 0.3.31", + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" +dependencies = [ + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -7725,6 +8851,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "sha3_ce" version = "0.10.6" @@ -8446,11 +9582,11 @@ dependencies = [ "frame-metadata 16.0.0", "futures 0.3.31", "hex", - "impl-serde", + "impl-serde 0.4.0", "instant", "jsonrpsee 0.21.0", - "parity-scale-codec", - "primitive-types", + "parity-scale-codec 3.6.12", + "primitive-types 0.12.2", "scale-bits", "scale-decode", "scale-encode", @@ -8478,7 +9614,7 @@ dependencies = [ "heck 0.4.1", "hex", "jsonrpsee 0.21.0", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "proc-macro2 1.0.89", "quote 1.0.37", "scale-info", @@ -8513,7 +9649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "365251668613323064803427af8c7c7bc366cd8b28e33639640757669dafebd5" dependencies = [ "darling 0.20.10", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "proc-macro-error", "quote 1.0.37", "scale-typegen", @@ -8528,7 +9664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c02aca8d39a1f6c55fff3a8fd81557d30a610fedc1cef03f889a81bc0f8f0b52" dependencies = [ "frame-metadata 16.0.0", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "scale-info", "sp-core-hashing", "thiserror", @@ -8543,7 +9679,7 @@ dependencies = [ "bip39", "hex", "hmac 0.12.1", - "parity-scale-codec", + "parity-scale-codec 3.6.12", "pbkdf2", "regex", "schnorrkel", @@ -8588,6 +9724,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16320d4a2021ba1a32470b3759676114a918885e9800e68ad60f2c67969fba62" +dependencies = [ + "paste", + "proc-macro2 1.0.89", + "quote 1.0.37", + "syn 2.0.85", +] + [[package]] name = "syn_derive" version = "0.1.8" @@ -8615,6 +9763,16 @@ dependencies = [ "futures-core", ] +[[package]] +name = "sys-info" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -9028,6 +10186,22 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.16", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tungstenite", + "webpki-roots", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -9337,6 +10511,26 @@ dependencies = [ "toml", ] +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand 0.8.5", + "rustls 0.23.16", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "twox-hash" version = "1.6.3" @@ -9501,10 +10695,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" dependencies = [ "base64 0.22.1", + "flate2", "log", "native-tls", "once_cell", + "rustls 0.23.16", + "rustls-pki-types", "url", + "webpki-roots", ] [[package]] @@ -9525,6 +10723,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.2" @@ -9640,6 +10844,15 @@ dependencies = [ "zksync_vm2", ] +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -9848,6 +11061,12 @@ dependencies = [ "wasite", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -10094,6 +11313,31 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures 0.3.31", + "js-sys", + "log", + "pharos", + "rustc_version 0.4.1", + "send_wrapper 0.6.0", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "wyz" version = "0.5.1" @@ -10371,7 +11615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49e0154bd4ae8202c96c52b29dd44f944bfd08c1c233fef843744463964de957" dependencies = [ "bitflags 1.3.2", - "ethereum-types", + "ethereum-types 0.14.1", "lazy_static", "sha2 0.10.8", ] @@ -10384,7 +11628,7 @@ checksum = "e0769f7b27d8fb06e715da3290c575cac5d04d10a557faef180e847afce50ac4" dependencies = [ "bitflags 2.6.0", "blake2 0.10.6", - "ethereum-types", + "ethereum-types 0.14.1", "k256 0.11.6", "lazy_static", "sha2_ce", @@ -10399,7 +11643,7 @@ checksum = "6be7bd5f0e0b61211f544147289640b4712715589d7f2fe5229d92a7a3ac64c0" dependencies = [ "bitflags 2.6.0", "blake2 0.10.6", - "ethereum-types", + "ethereum-types 0.14.1", "k256 0.13.4", "lazy_static", "sha2 0.10.8", @@ -10414,7 +11658,7 @@ checksum = "b83f3b279248af4ca86dec20a54127f02110b45570f3f6c1d13df49ba75c28a5" dependencies = [ "bitflags 2.6.0", "blake2 0.10.6", - "ethereum-types", + "ethereum-types 0.14.1", "k256 0.13.4", "lazy_static", "p256", @@ -10453,7 +11697,7 @@ dependencies = [ "anyhow", "bincode", "chrono", - "ethabi", + "ethabi 18.0.0", "hex", "num_enum 0.7.3", "secrecy", @@ -10589,12 +11833,7 @@ dependencies = [ "secrecy", "serde", "serde_json", - "strum", - "strum_macros", - "time", "tracing", - "url", - "vise", "zksync_basic_types", "zksync_concurrency", "zksync_consensus_utils", @@ -10789,15 +12028,18 @@ version = "0.1.0" dependencies = [ "anyhow", "axum 0.7.7", - "serde", + "http-body-util", "serde_json", + "test-casing", "tokio", + "tower 0.4.13", "tower-http", "tracing", "vise", - "zksync_config", "zksync_dal", + "zksync_node_test_utils", "zksync_types", + "zksync_utils", ] [[package]] @@ -10805,16 +12047,13 @@ name = "zksync_contract_verifier" version = "0.1.0" dependencies = [ "anyhow", - "ctrlc", - "futures 0.3.31", - "structopt", + "clap 4.5.20", "tokio", "tracing", "zksync_config", "zksync_contract_verifier_lib", "zksync_core_leftovers", "zksync_dal", - "zksync_env_config", "zksync_queued_job_processor", "zksync_utils", "zksync_vlog", @@ -10826,24 +12065,25 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "ethabi", + "ethabi 18.0.0", "hex", - "lazy_static", "regex", "semver 1.0.23", "serde", "serde_json", "tempfile", + "test-casing", "thiserror", "tokio", "tracing", "vise", - "zksync_config", "zksync_contracts", "zksync_dal", + "zksync_node_test_utils", "zksync_queued_job_processor", "zksync_types", "zksync_utils", + "zksync_vm_interface", ] [[package]] @@ -10851,7 +12091,7 @@ name = "zksync_contracts" version = "0.1.0" dependencies = [ "envy", - "ethabi", + "ethabi 18.0.0", "hex", "once_cell", "serde", @@ -10915,7 +12155,9 @@ dependencies = [ name = "zksync_da_clients" version = "0.1.0" dependencies = [ + "alloy", "anyhow", + "ark-bn254", "async-trait", "backon", "base58", @@ -10925,23 +12167,32 @@ dependencies = [ "blake2b_simd", "bytes", "celestia-types", + "ethabi 16.0.0", "flate2", "futures 0.3.31", "hex", "http 1.1.0", "jsonrpsee 0.23.2", - "parity-scale-codec", + "kzgpad-rs", + "num-bigint 0.4.6", + "parity-scale-codec 3.6.12", "pbjson-types", "prost 0.12.6", + "rand 0.8.5", "reqwest 0.12.9", "ripemd", + "rlp", + "rust-kzg-bn254", "scale-encode", "secp256k1", "serde", "serde_json", + "serial_test", "sha2 0.10.8", + "sha3 0.10.8", "subxt-metadata", "subxt-signer", + "tiny-keccak 2.0.2", "tokio", "tokio-stream", "tonic 0.11.0", @@ -11124,7 +12375,7 @@ dependencies = [ [[package]] name = "zksync_external_node" -version = "25.0.0" +version = "25.1.0" dependencies = [ "anyhow", "assert_matches", @@ -11417,11 +12668,12 @@ dependencies = [ "circuit_sequencer_api 0.141.2", "circuit_sequencer_api 0.142.2", "circuit_sequencer_api 0.150.7", - "ethabi", + "ethabi 18.0.0", "hex", "itertools 0.10.5", "once_cell", "pretty_assertions", + "rand 0.8.5", "test-casing", "thiserror", "tracing", @@ -11451,7 +12703,6 @@ dependencies = [ "async-trait", "axum 0.7.7", "chrono", - "const-decoder", "futures 0.3.31", "governor", "hex", @@ -11733,11 +12984,10 @@ dependencies = [ "zksync_contracts", "zksync_dal", "zksync_merkle_tree", - "zksync_multivm", - "zksync_node_genesis", "zksync_system_constants", "zksync_types", "zksync_utils", + "zksync_vm_interface", ] [[package]] @@ -11853,7 +13103,6 @@ dependencies = [ "secrecy", "serde_json", "serde_yaml", - "time", "tracing", "zksync_basic_types", "zksync_config", @@ -11874,9 +13123,9 @@ dependencies = [ "serde_with", "strum", "tokio", - "zksync_multivm", "zksync_object_store", "zksync_types", + "zksync_vm_interface", ] [[package]] @@ -11983,7 +13232,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b310ab8a21681270e73f177ddf7974cabb7a96f0624ab8b008fd6ee1f9b4f687" dependencies = [ - "ethereum-types", + "ethereum-types 0.14.1", "franklin-crypto", "handlebars", "hex", @@ -12129,7 +13378,7 @@ dependencies = [ name = "zksync_test_account" version = "0.1.0" dependencies = [ - "ethabi", + "ethabi 18.0.0", "hex", "rand 0.8.5", "zksync_contracts", @@ -12182,6 +13431,7 @@ dependencies = [ "assert_matches", "bigdecimal", "bincode", + "const-decoder", "futures 0.3.31", "hex", "num", @@ -12226,10 +13476,10 @@ dependencies = [ [[package]] name = "zksync_vm2" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=df5bec3d04d64d434f9b0ccb285ba4681008f7b3#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "enum_dispatch", - "primitive-types", + "primitive-types 0.12.2", "zk_evm_abstractions 0.150.7", "zkevm_opcode_defs 0.150.7", "zksync_vm2_interface", @@ -12238,9 +13488,9 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=df5bec3d04d64d434f9b0ccb285ba4681008f7b3#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ - "primitive-types", + "primitive-types 0.12.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e7cce4c4c421..e491c64605bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,12 +130,12 @@ google-cloud-storage = "0.20.0" governor = "0.4.2" hex = "0.4" http = "1.1" +http-body-util = "0.1.2" httpmock = "0.7.0" hyper = "1.3" insta = "1.29.0" itertools = "0.10" jsonrpsee = { version = "0.23", default-features = false } -lazy_static = "1.4" leb128 = "0.2.5" lru = { version = "0.12.1", default-features = false } mini-moka = "0.10.0" @@ -173,7 +173,6 @@ sqlx = "0.8.1" static_assertions = "1.1" structopt = "0.3.20" strum = "0.26" -strum_macros = "0.26.4" tempfile = "3.0.2" test-casing = "0.1.2" test-log = "0.2.15" @@ -240,7 +239,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 = "df5bec3d04d64d434f9b0ccb285ba4681008f7b3" } +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 84d5e3716f64..43cc01e430dd 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 84d5e3716f645909e8144c7d50af9dd6dd9ded62 +Subproject commit 43cc01e430dd24e35f80a5ae9b1ee708c1075df2 diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 56239303cd4e..3ccd261273b1 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,41 @@ # Changelog +## [25.1.0](https://github.com/matter-labs/zksync-era/compare/core-v25.0.0...core-v25.1.0) (2024-11-04) + + +### Features + +* add `block.timestamp` asserter for AA ([#3031](https://github.com/matter-labs/zksync-era/issues/3031)) ([069d38d](https://github.com/matter-labs/zksync-era/commit/069d38d6c9ddd8b6c404596c479f94b9fc86db40)) +* allow vm2 tracers to stop execution ([#3183](https://github.com/matter-labs/zksync-era/issues/3183)) ([9dae839](https://github.com/matter-labs/zksync-era/commit/9dae839935d82a1e73be220d17567f3382131039)) +* **api:** get rid of tx receipt root ([#3187](https://github.com/matter-labs/zksync-era/issues/3187)) ([6c034f6](https://github.com/matter-labs/zksync-era/commit/6c034f6e180cc92e99766f14c8840c90efa56cec)) +* **api:** Integrate new VM into API server (no tracers) ([#3033](https://github.com/matter-labs/zksync-era/issues/3033)) ([8e75d4b](https://github.com/matter-labs/zksync-era/commit/8e75d4b812b21bc26e2c38ceeb711a8a530d7bc2)) +* base token integration tests ([#2509](https://github.com/matter-labs/zksync-era/issues/2509)) ([8db7e93](https://github.com/matter-labs/zksync-era/commit/8db7e9306e5fa23f066be106363e6455531bbc09)) +* **consensus:** enabled syncing pregenesis blocks over p2p ([#3192](https://github.com/matter-labs/zksync-era/issues/3192)) ([6adb224](https://github.com/matter-labs/zksync-era/commit/6adb2249ff0946ec6d02f25437c9f71b1079ad79)) +* **da-clients:** add Celestia client ([#2983](https://github.com/matter-labs/zksync-era/issues/2983)) ([d88b875](https://github.com/matter-labs/zksync-era/commit/d88b875464ec5ac7e54aba0cc7c0a68c01969782)) +* **da-clients:** add EigenDA client ([#3155](https://github.com/matter-labs/zksync-era/issues/3155)) ([5161eed](https://github.com/matter-labs/zksync-era/commit/5161eeda5905d33f4d038a2a04ced3e06f39d593)) +* gateway preparation ([#3006](https://github.com/matter-labs/zksync-era/issues/3006)) ([16f2757](https://github.com/matter-labs/zksync-era/commit/16f275756cd28024a6b11ac1ac327eb5b8b446e1)) +* Implement gas relay mode and inclusion data for data attestation ([#3070](https://github.com/matter-labs/zksync-era/issues/3070)) ([561fc1b](https://github.com/matter-labs/zksync-era/commit/561fc1bddfc79061dab9d8d150baa06acfa90692)) +* **metadata-calculator:** Add debug endpoints for tree API ([#3167](https://github.com/matter-labs/zksync-era/issues/3167)) ([3815252](https://github.com/matter-labs/zksync-era/commit/3815252790fd0e9094f308b58dfde3a8b1a82277)) +* **proof-data-handler:** add first processed batch option ([#3112](https://github.com/matter-labs/zksync-era/issues/3112)) ([1eb69d4](https://github.com/matter-labs/zksync-era/commit/1eb69d467802d07f3fc6502de97ff04a69f952fc)) +* **proof-data-handler:** add tee_proof_generation_timeout_in_secs param ([#3128](https://github.com/matter-labs/zksync-era/issues/3128)) ([f3724a7](https://github.com/matter-labs/zksync-era/commit/f3724a71c7466451d380981b05d68d8afd70cdca)) +* **prover:** Add queue metric to report autoscaler view of the queue. ([#3206](https://github.com/matter-labs/zksync-era/issues/3206)) ([2721396](https://github.com/matter-labs/zksync-era/commit/272139690e028d3bdebdb6bcb1824fec23cefd0f)) +* **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)) +* **vm:** Support EVM emulation in fast VM ([#3163](https://github.com/matter-labs/zksync-era/issues/3163)) ([9ad1f0d](https://github.com/matter-labs/zksync-era/commit/9ad1f0d77e5a5b411f7866ef6a1819373c07f91b)) + + +### Bug Fixes + +* **consensus:** better logging of errors ([#3170](https://github.com/matter-labs/zksync-era/issues/3170)) ([a5028da](https://github.com/matter-labs/zksync-era/commit/a5028da65608898ad41c6a4fd5c6ec4c28a45703)) +* **consensus:** made attestation controller non-critical ([#3180](https://github.com/matter-labs/zksync-era/issues/3180)) ([6ee9f1f](https://github.com/matter-labs/zksync-era/commit/6ee9f1f431f95514d58db87a4562e09df9d09f86)) +* **consensus:** payload encoding protected by protocol_version ([#3168](https://github.com/matter-labs/zksync-era/issues/3168)) ([8089b78](https://github.com/matter-labs/zksync-era/commit/8089b78b3f2cdbe8d0a23e9b8412a8022d78ada2)) +* **da-clients:** add padding to the data within EigenDA blob ([#3203](https://github.com/matter-labs/zksync-era/issues/3203)) ([8ae06b2](https://github.com/matter-labs/zksync-era/commit/8ae06b237647715937fb3656d881c0fd460f2a07)) +* **da-clients:** enable tls-roots feature for tonic ([#3201](https://github.com/matter-labs/zksync-era/issues/3201)) ([42f177a](https://github.com/matter-labs/zksync-era/commit/42f177ac43b86cd24321ad9222121fc8a91c49e0)) +* extend allowed storage slots for validation as per EIP-7562 ([#3166](https://github.com/matter-labs/zksync-era/issues/3166)) ([c76da16](https://github.com/matter-labs/zksync-era/commit/c76da16efc769243a02c6e859376182d95ab941d)) +* **merkle-tree:** Fix tree truncation ([#3178](https://github.com/matter-labs/zksync-era/issues/3178)) ([9654097](https://github.com/matter-labs/zksync-era/commit/96540975d917761d8e464ebbdf52704955bcd898)) +* **tee_prover:** add prometheus pull listener ([#3169](https://github.com/matter-labs/zksync-era/issues/3169)) ([1ffd22f](https://github.com/matter-labs/zksync-era/commit/1ffd22ffbe710469de0e7f27c6aae29453ec6d3e)) +* update logging in cbt l1 behaviour ([#3149](https://github.com/matter-labs/zksync-era/issues/3149)) ([d0f61b0](https://github.com/matter-labs/zksync-era/commit/d0f61b0552dcacc2e8e33fdbcae6f1e5fbb43820)) + ## [25.0.0](https://github.com/matter-labs/zksync-era/compare/core-v24.29.0...core-v25.0.0) (2024-10-23) diff --git a/core/bin/contract-verifier/Cargo.toml b/core/bin/contract-verifier/Cargo.toml index f088c2337e71..5e9a9efc6e7e 100644 --- a/core/bin/contract-verifier/Cargo.toml +++ b/core/bin/contract-verifier/Cargo.toml @@ -12,7 +12,6 @@ publish = false [dependencies] zksync_dal.workspace = true -zksync_env_config.workspace = true zksync_config = { workspace = true, features = ["observability_ext"] } zksync_contract_verifier_lib.workspace = true zksync_queued_job_processor.workspace = true @@ -21,8 +20,6 @@ zksync_vlog.workspace = true zksync_core_leftovers.workspace = true anyhow.workspace = true +clap = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["full"] } -futures.workspace = true -ctrlc.workspace = true -structopt.workspace = true tracing.workspace = true diff --git a/core/bin/contract-verifier/src/main.rs b/core/bin/contract-verifier/src/main.rs index a8162de13e9d..88f25256c40d 100644 --- a/core/bin/contract-verifier/src/main.rs +++ b/core/bin/contract-verifier/src/main.rs @@ -1,132 +1,41 @@ -use std::{cell::RefCell, time::Duration}; +use std::{path::PathBuf, time::Duration}; -use anyhow::Context; -use futures::{channel::mpsc, executor::block_on, SinkExt, StreamExt}; -use structopt::StructOpt; +use anyhow::Context as _; +use clap::Parser; use tokio::sync::watch; use zksync_config::configs::PrometheusConfig; use zksync_contract_verifier_lib::ContractVerifier; use zksync_core_leftovers::temp_config_store::{load_database_secrets, load_general_config}; -use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_dal::{ConnectionPool, Core}; use zksync_queued_job_processor::JobProcessor; -use zksync_utils::{env::Workspace, wait_for_tasks::ManagedTasks}; +use zksync_utils::wait_for_tasks::ManagedTasks; use zksync_vlog::prometheus::PrometheusExporterConfig; -async fn update_compiler_versions(connection_pool: &ConnectionPool) { - let mut storage = connection_pool.connection().await.unwrap(); - let mut transaction = storage.start_transaction().await.unwrap(); - - let zksync_home = Workspace::locate().core(); - - let zksolc_path = zksync_home.join("etc/zksolc-bin/"); - let zksolc_versions: Vec = std::fs::read_dir(zksolc_path) - .unwrap() - .filter_map(|file| { - let file = file.unwrap(); - let Ok(file_type) = file.file_type() else { - return None; - }; - if file_type.is_dir() { - file.file_name().into_string().ok() - } else { - None - } - }) - .collect(); - transaction - .contract_verification_dal() - .set_zksolc_versions(zksolc_versions) - .await - .unwrap(); - - let solc_path = zksync_home.join("etc/solc-bin/"); - let solc_versions: Vec = std::fs::read_dir(solc_path) - .unwrap() - .filter_map(|file| { - let file = file.unwrap(); - let Ok(file_type) = file.file_type() else { - return None; - }; - if file_type.is_dir() { - file.file_name().into_string().ok() - } else { - None - } - }) - .collect(); - transaction - .contract_verification_dal() - .set_solc_versions(solc_versions) - .await - .unwrap(); - - let zkvyper_path = zksync_home.join("etc/zkvyper-bin/"); - let zkvyper_versions: Vec = std::fs::read_dir(zkvyper_path) - .unwrap() - .filter_map(|file| { - let file = file.unwrap(); - let Ok(file_type) = file.file_type() else { - return None; - }; - if file_type.is_dir() { - file.file_name().into_string().ok() - } else { - None - } - }) - .collect(); - transaction - .contract_verification_dal() - .set_zkvyper_versions(zkvyper_versions) - .await - .unwrap(); - - let vyper_path = zksync_home.join("etc/vyper-bin/"); - let vyper_versions: Vec = std::fs::read_dir(vyper_path) - .unwrap() - .filter_map(|file| { - let file = file.unwrap(); - let Ok(file_type) = file.file_type() else { - return None; - }; - if file_type.is_dir() { - file.file_name().into_string().ok() - } else { - None - } - }) - .collect(); - - transaction - .contract_verification_dal() - .set_vyper_versions(vyper_versions) - .await - .unwrap(); - - transaction.commit().await.unwrap(); -} - -#[derive(StructOpt)] -#[structopt(name = "ZKsync contract code verifier", author = "Matter Labs")] +#[derive(Debug, Parser)] +#[command(name = "ZKsync contract code verifier", author = "Matter Labs")] struct Opt { /// Number of jobs to process. If None, runs indefinitely. - #[structopt(long)] + #[arg(long)] jobs_number: Option, /// Path to the configuration file. - #[structopt(long)] - config_path: Option, + #[arg(long)] + config_path: Option, /// Path to the secrets file. - #[structopt(long)] - secrets_path: Option, + #[arg(long)] + secrets_path: Option, } #[tokio::main] async fn main() -> anyhow::Result<()> { - let opt = Opt::from_args(); + let opt = Opt::parse(); let general_config = load_general_config(opt.config_path).context("general config")?; - let database_secrets = load_database_secrets(opt.secrets_path).context("database secrets")?; + let observability_config = general_config + .observability + .context("ObservabilityConfig")?; + let _observability_guard = observability_config.install()?; + let database_secrets = load_database_secrets(opt.secrets_path).context("database secrets")?; let verifier_config = general_config .contract_verifier .context("ContractVerifierConfig")?; @@ -140,33 +49,13 @@ async fn main() -> anyhow::Result<()> { .context("Master DB URL is absent")?, ) .build() - .await - .unwrap(); - - let observability_config = general_config - .observability - .context("ObservabilityConfig")?; - - let _observability_guard = observability_config.install()?; + .await?; let (stop_sender, stop_receiver) = watch::channel(false); - let (stop_signal_sender, mut stop_signal_receiver) = mpsc::channel(256); - { - let stop_signal_sender = RefCell::new(stop_signal_sender.clone()); - ctrlc::set_handler(move || { - let mut sender = stop_signal_sender.borrow_mut(); - block_on(sender.send(true)).expect("Ctrl+C signal send"); - }) - .expect("Error setting Ctrl+C handler"); - } - - update_compiler_versions(&pool).await; - - let contract_verifier = ContractVerifier::new(verifier_config, pool); + let contract_verifier = ContractVerifier::new(verifier_config.compilation_timeout(), pool) + .await + .context("failed initializing contract verifier")?; let tasks = vec![ - // TODO PLA-335: Leftovers after the prover DB split. - // The prover connection pool is not used by the contract verifier, but we need to pass it - // since `JobProcessor` trait requires it. tokio::spawn(contract_verifier.run(stop_receiver.clone(), opt.jobs_number)), tokio::spawn( PrometheusExporterConfig::pull(prometheus_config.listener_port).run(stop_receiver), @@ -176,7 +65,7 @@ async fn main() -> anyhow::Result<()> { let mut tasks = ManagedTasks::new(tasks); tokio::select! { () = tasks.wait_single() => {}, - _ = stop_signal_receiver.next() => { + _ = tokio::signal::ctrl_c() => { tracing::info!("Stop signal received, shutting down"); }, }; diff --git a/core/bin/external_node/Cargo.toml b/core/bin/external_node/Cargo.toml index 4e3dc548cf89..4c8f73eda94d 100644 --- a/core/bin/external_node/Cargo.toml +++ b/core/bin/external_node/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_external_node" description = "Non-validator ZKsync node" -version = "25.0.0" # x-release-please-version +version = "25.1.0" # x-release-please-version edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index 70803a663110..0a94f993656a 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 a32be3eff725..dc74d124b18e 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 855f50df1419..51e7b409c9a0 100644 --- a/core/bin/zksync_server/src/main.rs +++ b/core/bin/zksync_server/src/main.rs @@ -7,7 +7,7 @@ use zksync_config::{ api::{HealthCheckConfig, MerkleTreeApiConfig, Web3JsonRpcConfig}, chain::{ CircuitBreakerConfig, MempoolConfig, NetworkConfig, OperationsManagerConfig, - StateKeeperConfig, + StateKeeperConfig, TimestampAsserterConfig, }, fri_prover_group::FriProverGroupConfig, house_keeper::HouseKeeperConfig, @@ -195,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 e7a3dca77f15..32478ede5bf8 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -1,6 +1,8 @@ //! This module provides a "builder" for the main node, //! as well as an interface to run the node with the specified components. +use std::time::Duration; + use anyhow::{bail, Context}; use zksync_config::{ configs::{ @@ -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::{ @@ -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(), diff --git a/core/lib/config/Cargo.toml b/core/lib/config/Cargo.toml index af39e5159ba8..46c0b27d4b03 100644 --- a/core/lib/config/Cargo.toml +++ b/core/lib/config/Cargo.toml @@ -18,15 +18,10 @@ zksync_concurrency.workspace = true zksync_vlog = { workspace = true, optional = true } tracing = { workspace = true, optional = true } -url.workspace = true anyhow.workspace = true rand.workspace = true secrecy.workspace = true serde = { workspace = true, features = ["derive"] } -time = { workspace = true, features = ["serde-human-readable"] } -strum.workspace = true -strum_macros.workspace = true -vise.workspace = true [dev-dependencies] serde_json.workspace = true diff --git a/core/lib/config/src/configs/chain.rs b/core/lib/config/src/configs/chain.rs index c117064dbc40..d73dce81b13a 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/contract_verifier.rs b/core/lib/config/src/configs/contract_verifier.rs index 0016e1255de1..1dac0b17227e 100644 --- a/core/lib/config/src/configs/contract_verifier.rs +++ b/core/lib/config/src/configs/contract_verifier.rs @@ -9,13 +9,9 @@ use serde::Deserialize; pub struct ContractVerifierConfig { /// Max time of a single compilation (in s). pub compilation_timeout: u64, - /// Interval between polling db for verification requests (in ms). - pub polling_interval: Option, /// Port to which the Prometheus exporter server is listening. pub prometheus_port: u16, - pub threads_per_server: Option, pub port: u16, - pub url: String, } impl ContractVerifierConfig { @@ -23,9 +19,6 @@ impl ContractVerifierConfig { Duration::from_secs(self.compilation_timeout) } - pub fn polling_interval(&self) -> Duration { - Duration::from_millis(self.polling_interval.unwrap_or(1000)) - } pub fn bind_addr(&self) -> SocketAddr { SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), self.port) } diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index 0bf7aab3bcab..38576833fa3e 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/eigen.rs b/core/lib/config/src/configs/da_client/eigen.rs index f2c05a0f61ef..f94675871463 100644 --- a/core/lib/config/src/configs/da_client/eigen.rs +++ b/core/lib/config/src/configs/da_client/eigen.rs @@ -1,10 +1,53 @@ 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, +pub const EIGEN_MEMSTORE_CLIENT_NAME: &str = "MemStore"; +pub const EIGEN_DISPERSER_CLIENT_NAME: &str = "Disperser"; + +#[derive(Clone, Debug, PartialEq, Deserialize)] +pub enum EigenConfig { + MemStore(MemStoreConfig), + Disperser(DisperserConfig), +} + +/// Configuration for the EigenDA in-memory client. +#[derive(Clone, Debug, PartialEq, Deserialize, Default)] +pub struct MemStoreConfig { + pub max_blob_size_bytes: u64, + /// Blob expiration time in seconds + pub blob_expiration: u64, + /// Latency in milliseconds for get operations + pub get_latency: u64, + /// Latency in milliseconds for put operations + pub put_latency: u64, +} + +/// Configuration for the EigenDA remote disperser client. +#[derive(Clone, Debug, PartialEq, Deserialize, Default)] +pub struct DisperserConfig { + /// URL of the Disperser RPC server + pub disperser_rpc: String, + /// Block height needed to reach in order to consider the blob finalized + /// a value less or equal to 0 means that the disperser will not wait for finalization + pub eth_confirmation_depth: i32, + /// URL of the Ethereum RPC server + pub eigenda_eth_rpc: String, + /// Address of the service manager contract + pub eigenda_svc_manager_address: String, + /// Maximum size permitted for a blob in bytes + pub blob_size_limit: u32, + /// Maximun amount of time in milliseconds to wait for a status query response + pub status_query_timeout: u64, + /// Interval in milliseconds to query the status of a blob + pub status_query_interval: u64, + /// Wait for the blob to be finalized before returning the response + pub wait_for_finalization: bool, + /// Authenticated dispersal + pub authenticated: bool, + /// Verify the certificate of dispatched blobs + pub verify_cert: bool, + /// Path to the file containing the points used for KZG + pub path_to_points: String, } #[derive(Clone, Debug, PartialEq)] diff --git a/core/lib/config/src/configs/general.rs b/core/lib/config/src/configs/general.rs index bb733510f77d..dfb81af1cf8c 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 2b848030d719..5bf9a49e0acf 100644 --- a/core/lib/config/src/configs/mod.rs +++ b/core/lib/config/src/configs/mod.rs @@ -60,7 +60,6 @@ pub mod house_keeper; pub mod object_store; pub mod observability; pub mod proof_data_handler; -pub mod prover_autoscaler; pub mod prover_job_monitor; pub mod pruning; pub mod secrets; diff --git a/core/lib/config/src/configs/observability.rs b/core/lib/config/src/configs/observability.rs index 42363cbcb4ff..f7ad2a97b91c 100644 --- a/core/lib/config/src/configs/observability.rs +++ b/core/lib/config/src/configs/observability.rs @@ -1,6 +1,8 @@ +use serde::Deserialize; + /// Configuration for the essential observability stack, like /// logging and sentry integration. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub struct ObservabilityConfig { /// URL of the Sentry instance to send events to. pub sentry_url: Option, @@ -15,7 +17,7 @@ pub struct ObservabilityConfig { pub log_directives: Option, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub struct OpentelemetryConfig { /// Enables export of span data of specified level (and above) using opentelemetry exporters. pub level: String, diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 49c5cff1dca0..93d502cc4e8a 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -18,6 +18,7 @@ use zksync_crypto_primitives::K256PrivateKey; use crate::{ configs::{ self, + chain::TimestampAsserterConfig, da_client::{ avail::{AvailClientConfig, AvailDefaultConfig}, DAClientConfig::Avail, @@ -240,11 +241,8 @@ impl Distribution for EncodeDist { fn sample(&self, rng: &mut R) -> configs::ContractVerifierConfig { configs::ContractVerifierConfig { compilation_timeout: self.sample(rng), - polling_interval: self.sample(rng), prometheus_port: self.sample(rng), - threads_per_server: self.sample(rng), port: self.sample(rng), - url: self.sample(rng), } } } @@ -265,6 +263,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()), @@ -1181,6 +1180,15 @@ 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/contract_verifier/Cargo.toml b/core/lib/contract_verifier/Cargo.toml index 580982c9a700..bdbfa90bf76a 100644 --- a/core/lib/contract_verifier/Cargo.toml +++ b/core/lib/contract_verifier/Cargo.toml @@ -13,7 +13,6 @@ categories.workspace = true [dependencies] zksync_types.workspace = true zksync_dal.workspace = true -zksync_config.workspace = true zksync_contracts.workspace = true zksync_queued_job_processor.workspace = true zksync_utils.workspace = true @@ -27,8 +26,12 @@ ethabi.workspace = true vise.workspace = true hex.workspace = true serde = { workspace = true, features = ["derive"] } -lazy_static.workspace = true tempfile.workspace = true regex.workspace = true tracing.workspace = true semver.workspace = true + +[dev-dependencies] +zksync_node_test_utils.workspace = true +zksync_vm_interface.workspace = true +test-casing.workspace = true diff --git a/core/lib/contract_verifier/src/compilers/mod.rs b/core/lib/contract_verifier/src/compilers/mod.rs new file mode 100644 index 000000000000..a56b4e32d1a1 --- /dev/null +++ b/core/lib/contract_verifier/src/compilers/mod.rs @@ -0,0 +1,87 @@ +use anyhow::Context as _; +use serde::{Deserialize, Serialize}; +use zksync_types::contract_verification_api::CompilationArtifacts; + +pub(crate) use self::{ + solc::{Solc, SolcInput}, + zksolc::{ZkSolc, ZkSolcInput}, + zkvyper::{ZkVyper, ZkVyperInput}, +}; +use crate::error::ContractVerifierError; + +mod solc; +mod zksolc; +mod zkvyper; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct Source { + /// The source code file content. + pub content: String, +} + +/// Parsing logic shared between `solc` and `zksolc`. +fn parse_standard_json_output( + output: &serde_json::Value, + contract_name: String, + file_name: String, + get_deployed_bytecode: bool, +) -> Result { + if let Some(errors) = output.get("errors") { + let errors = errors.as_array().unwrap().clone(); + if errors + .iter() + .any(|err| err["severity"].as_str().unwrap() == "error") + { + let error_messages = errors + .into_iter() + .map(|err| err["formattedMessage"].clone()) + .collect(); + return Err(ContractVerifierError::CompilationError( + serde_json::Value::Array(error_messages), + )); + } + } + + let contracts = output["contracts"] + .get(&file_name) + .ok_or(ContractVerifierError::MissingSource(file_name))?; + let Some(contract) = contracts.get(&contract_name) else { + return Err(ContractVerifierError::MissingContract(contract_name)); + }; + + let Some(bytecode_str) = contract + .pointer("/evm/bytecode/object") + .context("missing bytecode in solc / zksolc output")? + .as_str() + else { + return Err(ContractVerifierError::AbstractContract(contract_name)); + }; + let bytecode = hex::decode(bytecode_str).context("invalid bytecode")?; + + let deployed_bytecode = if get_deployed_bytecode { + let bytecode_str = contract + .pointer("/evm/deployedBytecode/object") + .context("missing deployed bytecode in solc output")? + .as_str() + .ok_or(ContractVerifierError::AbstractContract(contract_name))?; + Some(hex::decode(bytecode_str).context("invalid deployed bytecode")?) + } else { + None + }; + + let abi = contract["abi"].clone(); + if !abi.is_array() { + let err = anyhow::anyhow!( + "unexpected value for ABI: {}", + serde_json::to_string_pretty(&abi).unwrap() + ); + return Err(err.into()); + } + + Ok(CompilationArtifacts { + bytecode, + deployed_bytecode, + abi, + }) +} diff --git a/core/lib/contract_verifier/src/compilers/solc.rs b/core/lib/contract_verifier/src/compilers/solc.rs new file mode 100644 index 000000000000..bb453cb729c2 --- /dev/null +++ b/core/lib/contract_verifier/src/compilers/solc.rs @@ -0,0 +1,168 @@ +use std::{collections::HashMap, path::PathBuf, process::Stdio}; + +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use tokio::io::AsyncWriteExt; +use zksync_queued_job_processor::async_trait; +use zksync_types::contract_verification_api::{ + CompilationArtifacts, SourceCodeData, VerificationIncomingRequest, +}; + +use super::{parse_standard_json_output, Source}; +use crate::{error::ContractVerifierError, resolver::Compiler}; + +// Here and below, fields are public for testing purposes. +#[derive(Debug)] +pub(crate) struct SolcInput { + pub standard_json: StandardJson, + pub contract_name: String, + pub file_name: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct StandardJson { + pub language: String, + pub sources: HashMap, + settings: Settings, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct Settings { + /// The output selection filters. + output_selection: Option, + /// Other settings (only filled when parsing `StandardJson` input from the request). + #[serde(flatten)] + other: serde_json::Value, +} + +#[derive(Debug)] +pub(crate) struct Solc { + path: PathBuf, +} + +impl Solc { + pub fn new(path: PathBuf) -> Self { + Self { path } + } + + pub fn build_input( + req: VerificationIncomingRequest, + ) -> Result { + // Users may provide either just contract name or + // source file name and contract name joined with ":". + let (file_name, contract_name) = + if let Some((file_name, contract_name)) = req.contract_name.rsplit_once(':') { + (file_name.to_string(), contract_name.to_string()) + } else { + ( + format!("{}.sol", req.contract_name), + req.contract_name.clone(), + ) + }; + let default_output_selection = serde_json::json!({ + "*": { + "*": [ "abi", "evm.bytecode", "evm.deployedBytecode" ], + "": [ "abi", "evm.bytecode", "evm.deployedBytecode" ], + } + }); + + let standard_json = match req.source_code_data { + SourceCodeData::SolSingleFile(source_code) => { + let source = Source { + content: source_code, + }; + let sources = HashMap::from([(file_name.clone(), source)]); + let settings = Settings { + output_selection: Some(default_output_selection), + other: serde_json::json!({ + "optimizer": { + "enabled": req.optimization_used, + }, + }), + }; + + StandardJson { + language: "Solidity".to_owned(), + sources, + settings, + } + } + SourceCodeData::StandardJsonInput(map) => { + let mut compiler_input: StandardJson = + serde_json::from_value(serde_json::Value::Object(map)) + .map_err(|_| ContractVerifierError::FailedToDeserializeInput)?; + // Set default output selection even if it is different in request. + compiler_input.settings.output_selection = Some(default_output_selection); + compiler_input + } + SourceCodeData::YulSingleFile(source_code) => { + let source = Source { + content: source_code, + }; + let sources = HashMap::from([(file_name.clone(), source)]); + let settings = Settings { + output_selection: Some(default_output_selection), + other: serde_json::json!({ + "optimizer": { + "enabled": req.optimization_used, + }, + }), + }; + StandardJson { + language: "Yul".to_owned(), + sources, + settings, + } + } + other => unreachable!("Unexpected `SourceCodeData` variant: {other:?}"), + }; + + Ok(SolcInput { + standard_json, + contract_name, + file_name, + }) + } +} + +#[async_trait] +impl Compiler for Solc { + async fn compile( + self: Box, + input: SolcInput, + ) -> Result { + let mut command = tokio::process::Command::new(&self.path); + let mut child = command + .arg("--standard-json") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("failed spawning solc")?; + let stdin = child.stdin.as_mut().unwrap(); + let content = serde_json::to_vec(&input.standard_json) + .context("cannot encode standard JSON input for solc")?; + stdin + .write_all(&content) + .await + .context("failed writing standard JSON to solc stdin")?; + stdin + .flush() + .await + .context("failed flushing standard JSON to solc")?; + + let output = child.wait_with_output().await.context("solc failed")?; + if output.status.success() { + let output = serde_json::from_slice(&output.stdout) + .context("zksolc output is not valid JSON")?; + parse_standard_json_output(&output, input.contract_name, input.file_name, true) + } else { + Err(ContractVerifierError::CompilerError( + "solc", + String::from_utf8_lossy(&output.stderr).to_string(), + )) + } + } +} diff --git a/core/lib/contract_verifier/src/compilers/zksolc.rs b/core/lib/contract_verifier/src/compilers/zksolc.rs new file mode 100644 index 000000000000..0d6b5828e31c --- /dev/null +++ b/core/lib/contract_verifier/src/compilers/zksolc.rs @@ -0,0 +1,329 @@ +use std::{collections::HashMap, io::Write, process::Stdio}; + +use anyhow::Context as _; +use regex::Regex; +use semver::Version; +use serde::{Deserialize, Serialize}; +use tokio::io::AsyncWriteExt; +use zksync_queued_job_processor::async_trait; +use zksync_types::contract_verification_api::{ + CompilationArtifacts, SourceCodeData, VerificationIncomingRequest, +}; + +use super::{parse_standard_json_output, Source}; +use crate::{ + error::ContractVerifierError, + resolver::{Compiler, CompilerPaths}, +}; + +#[derive(Debug)] +pub(crate) enum ZkSolcInput { + StandardJson { + input: StandardJson, + contract_name: String, + file_name: String, + }, + YulSingleFile { + source_code: String, + is_system: bool, + }, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct StandardJson { + /// The input language. + pub language: String, + /// The input source code files hashmap. + pub sources: HashMap, + /// The compiler settings. + pub settings: Settings, +} + +/// Compiler settings. +/// There are fields like `output_selection`, `is_system`, `force_evmla` which are accessed by contract verifier explicitly. +/// Other fields are accumulated in `other`, this way every field that was in the original request will be passed to a compiler. +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct Settings { + /// The output selection filters. + pub output_selection: Option, + /// Flag for system compilation mode. + #[serde(default)] + pub is_system: bool, + /// Flag to force `evmla` IR. + #[serde(default)] + pub force_evmla: bool, + /// Other settings (only filled when parsing `StandardJson` input from the request). + #[serde(flatten)] + pub other: serde_json::Value, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct Optimizer { + /// Whether the optimizer is enabled. + pub enabled: bool, + /// The optimization mode string. + pub mode: Option, +} + +#[derive(Debug)] +pub(crate) struct ZkSolc { + paths: CompilerPaths, + zksolc_version: String, +} + +impl ZkSolc { + pub fn new(paths: CompilerPaths, zksolc_version: String) -> Self { + ZkSolc { + paths, + zksolc_version, + } + } + + pub fn build_input( + req: VerificationIncomingRequest, + ) -> Result { + // Users may provide either just contract name or + // source file name and contract name joined with ":". + let (file_name, contract_name) = + if let Some((file_name, contract_name)) = req.contract_name.rsplit_once(':') { + (file_name.to_string(), contract_name.to_string()) + } else { + ( + format!("{}.sol", req.contract_name), + req.contract_name.clone(), + ) + }; + let default_output_selection = serde_json::json!({ + "*": { + "*": [ "abi" ], + "": [ "abi" ] + } + }); + + match req.source_code_data { + SourceCodeData::SolSingleFile(source_code) => { + let source = Source { + content: source_code, + }; + let sources = HashMap::from([(file_name.clone(), source)]); + let settings = Settings { + output_selection: Some(default_output_selection), + is_system: req.is_system, + force_evmla: req.force_evmla, + other: serde_json::json!({ + "optimizer": Optimizer { + enabled: req.optimization_used, + mode: req.optimizer_mode.and_then(|s| s.chars().next()), + }, + }), + }; + + Ok(ZkSolcInput::StandardJson { + input: StandardJson { + language: "Solidity".to_string(), + sources, + settings, + }, + contract_name, + file_name, + }) + } + SourceCodeData::StandardJsonInput(map) => { + let mut compiler_input: StandardJson = + serde_json::from_value(serde_json::Value::Object(map)) + .map_err(|_| ContractVerifierError::FailedToDeserializeInput)?; + // Set default output selection even if it is different in request. + compiler_input.settings.output_selection = Some(default_output_selection); + Ok(ZkSolcInput::StandardJson { + input: compiler_input, + contract_name, + file_name, + }) + } + SourceCodeData::YulSingleFile(source_code) => Ok(ZkSolcInput::YulSingleFile { + source_code, + is_system: req.is_system, + }), + other => unreachable!("Unexpected `SourceCodeData` variant: {other:?}"), + } + } + + fn parse_single_file_yul_output( + output: &str, + ) -> Result { + let re = Regex::new(r"Contract `.*` bytecode: 0x([\da-f]+)").unwrap(); + let cap = re + .captures(output) + .context("Yul output doesn't match regex")?; + let bytecode_str = cap.get(1).context("no matches in Yul output")?.as_str(); + let bytecode = hex::decode(bytecode_str).context("invalid Yul output bytecode")?; + Ok(CompilationArtifacts { + bytecode, + deployed_bytecode: None, + abi: serde_json::Value::Array(Vec::new()), + }) + } + + fn is_post_1_5_0(&self) -> bool { + // Special case + if &self.zksolc_version == "vm-1.5.0-a167aa3" { + false + } else if let Some(version) = self.zksolc_version.strip_prefix("v") { + if let Ok(semver) = Version::parse(version) { + let target = Version::new(1, 5, 0); + semver >= target + } else { + true + } + } else { + true + } + } +} + +#[async_trait] +impl Compiler for ZkSolc { + async fn compile( + self: Box, + input: ZkSolcInput, + ) -> Result { + let mut command = tokio::process::Command::new(&self.paths.zk); + command.stdout(Stdio::piped()).stderr(Stdio::piped()); + + match &input { + ZkSolcInput::StandardJson { input, .. } => { + if !self.is_post_1_5_0() { + if input.settings.is_system { + command.arg("--system-mode"); + } + if input.settings.force_evmla { + command.arg("--force-evmla"); + } + } + + command.arg("--solc").arg(&self.paths.base); + } + ZkSolcInput::YulSingleFile { is_system, .. } => { + if self.is_post_1_5_0() { + if *is_system { + command.arg("--enable-eravm-extensions"); + } else { + command.arg("--solc").arg(&self.paths.base); + } + } else { + if *is_system { + command.arg("--system-mode"); + } + command.arg("--solc").arg(&self.paths.base); + } + } + } + match input { + ZkSolcInput::StandardJson { + input, + contract_name, + file_name, + } => { + let mut child = command + .arg("--standard-json") + .stdin(Stdio::piped()) + .spawn() + .context("failed spawning zksolc")?; + let stdin = child.stdin.as_mut().unwrap(); + let content = serde_json::to_vec(&input) + .context("cannot encode standard JSON input for zksolc")?; + stdin + .write_all(&content) + .await + .context("failed writing standard JSON to zksolc stdin")?; + stdin + .flush() + .await + .context("failed flushing standard JSON to zksolc")?; + + let output = child.wait_with_output().await.context("zksolc failed")?; + if output.status.success() { + let output = serde_json::from_slice(&output.stdout) + .context("zksolc output is not valid JSON")?; + parse_standard_json_output(&output, contract_name, file_name, false) + } else { + Err(ContractVerifierError::CompilerError( + "zksolc", + String::from_utf8_lossy(&output.stderr).to_string(), + )) + } + } + ZkSolcInput::YulSingleFile { source_code, .. } => { + let mut file = tempfile::Builder::new() + .prefix("input") + .suffix(".yul") + .rand_bytes(0) + .tempfile() + .context("cannot create temporary Yul file")?; + file.write_all(source_code.as_bytes()) + .context("failed writing Yul file")?; + let child = command + .arg(file.path().to_str().unwrap()) + .arg("--optimization") + .arg("3") + .arg("--yul") + .arg("--bin") + .spawn() + .context("failed spawning zksolc")?; + let output = child.wait_with_output().await.context("zksolc failed")?; + if output.status.success() { + let output = + String::from_utf8(output.stdout).context("zksolc output is not UTF-8")?; + Self::parse_single_file_yul_output(&output) + } else { + Err(ContractVerifierError::CompilerError( + "zksolc", + String::from_utf8_lossy(&output.stderr).to_string(), + )) + } + } + } + } +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::*; + + #[test] + fn check_is_post_1_5_0() { + // Special case. + let compiler_paths = CompilerPaths { + base: PathBuf::default(), + zk: PathBuf::default(), + }; + let mut zksolc = ZkSolc::new(compiler_paths, "vm-1.5.0-a167aa3".to_string()); + assert!(!zksolc.is_post_1_5_0(), "vm-1.5.0-a167aa3"); + + zksolc.zksolc_version = "v1.5.0".to_string(); + assert!(zksolc.is_post_1_5_0(), "v1.5.0"); + + zksolc.zksolc_version = "v1.5.1".to_string(); + assert!(zksolc.is_post_1_5_0(), "v1.5.1"); + + zksolc.zksolc_version = "v1.10.1".to_string(); + assert!(zksolc.is_post_1_5_0(), "v1.10.1"); + + zksolc.zksolc_version = "v2.0.0".to_string(); + assert!(zksolc.is_post_1_5_0(), "v2.0.0"); + + zksolc.zksolc_version = "v1.4.15".to_string(); + assert!(!zksolc.is_post_1_5_0(), "v1.4.15"); + + zksolc.zksolc_version = "v1.3.21".to_string(); + assert!(!zksolc.is_post_1_5_0(), "v1.3.21"); + + zksolc.zksolc_version = "v0.5.1".to_string(); + assert!(!zksolc.is_post_1_5_0(), "v0.5.1"); + } +} diff --git a/core/lib/contract_verifier/src/compilers/zkvyper.rs b/core/lib/contract_verifier/src/compilers/zkvyper.rs new file mode 100644 index 000000000000..b3dacce64e77 --- /dev/null +++ b/core/lib/contract_verifier/src/compilers/zkvyper.rs @@ -0,0 +1,130 @@ +use std::{collections::HashMap, fs::File, io::Write, path::Path, process::Stdio}; + +use anyhow::Context as _; +use zksync_queued_job_processor::async_trait; +use zksync_types::contract_verification_api::{ + CompilationArtifacts, SourceCodeData, VerificationIncomingRequest, +}; + +use crate::{ + error::ContractVerifierError, + resolver::{Compiler, CompilerPaths}, +}; + +#[derive(Debug)] +pub(crate) struct ZkVyperInput { + pub contract_name: String, + pub sources: HashMap, + pub optimizer_mode: Option, +} + +#[derive(Debug)] +pub(crate) struct ZkVyper { + paths: CompilerPaths, +} + +impl ZkVyper { + pub fn new(paths: CompilerPaths) -> Self { + Self { paths } + } + + pub fn build_input( + req: VerificationIncomingRequest, + ) -> Result { + // Users may provide either just contract name or + // source file name and contract name joined with ":". + let contract_name = if let Some((_, contract_name)) = req.contract_name.rsplit_once(':') { + contract_name.to_owned() + } else { + req.contract_name.clone() + }; + + let sources = match req.source_code_data { + SourceCodeData::VyperMultiFile(s) => s, + other => unreachable!("unexpected `SourceCodeData` variant: {other:?}"), + }; + Ok(ZkVyperInput { + contract_name, + sources, + optimizer_mode: req.optimizer_mode, + }) + } + + fn parse_output( + output: &serde_json::Value, + contract_name: String, + ) -> Result { + let file_name = format!("{contract_name}.vy"); + let object = output + .as_object() + .context("Vyper output is not an object")?; + for (path, artifact) in object { + let path = Path::new(&path); + if path.file_name().unwrap().to_str().unwrap() == file_name { + let bytecode_str = artifact["bytecode"] + .as_str() + .context("bytecode is not a string")?; + let bytecode_without_prefix = + bytecode_str.strip_prefix("0x").unwrap_or(bytecode_str); + let bytecode = + hex::decode(bytecode_without_prefix).context("failed decoding bytecode")?; + return Ok(CompilationArtifacts { + abi: artifact["abi"].clone(), + bytecode, + deployed_bytecode: None, + }); + } + } + Err(ContractVerifierError::MissingContract(contract_name)) + } +} + +#[async_trait] +impl Compiler for ZkVyper { + async fn compile( + self: Box, + input: ZkVyperInput, + ) -> Result { + let mut command = tokio::process::Command::new(&self.paths.zk); + if let Some(o) = input.optimizer_mode.as_ref() { + command.arg("-O").arg(o); + } + command + .arg("--vyper") + .arg(&self.paths.base) + .arg("-f") + .arg("combined_json") + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + let temp_dir = tempfile::tempdir().context("failed creating temporary dir")?; + for (mut name, content) in input.sources { + if !name.ends_with(".vy") { + name += ".vy"; + } + let path = temp_dir.path().join(&name); + if let Some(prefix) = path.parent() { + std::fs::create_dir_all(prefix) + .with_context(|| format!("failed creating parent dir for `{name}`"))?; + } + let mut file = File::create(&path) + .with_context(|| format!("failed creating file for `{name}`"))?; + file.write_all(content.as_bytes()) + .with_context(|| format!("failed writing to `{name}`"))?; + command.arg(path.into_os_string()); + } + + let child = command.spawn().context("cannot spawn zkvyper")?; + let output = child.wait_with_output().await.context("zkvyper failed")?; + if output.status.success() { + let output = serde_json::from_slice(&output.stdout) + .context("zkvyper output is not valid JSON")?; + Self::parse_output(&output, input.contract_name) + } else { + Err(ContractVerifierError::CompilerError( + "zkvyper", + String::from_utf8_lossy(&output.stderr).to_string(), + )) + } + } +} diff --git a/core/lib/contract_verifier/src/error.rs b/core/lib/contract_verifier/src/error.rs index c66756d1f121..c777df24e226 100644 --- a/core/lib/contract_verifier/src/error.rs +++ b/core/lib/contract_verifier/src/error.rs @@ -1,19 +1,23 @@ -#[derive(Debug, Clone, thiserror::Error)] +use zksync_dal::DalError; + +#[derive(Debug, thiserror::Error)] pub enum ContractVerifierError { #[error("Internal error")] - InternalError, + Internal(#[from] anyhow::Error), #[error("Deployed bytecode is not equal to generated one from given source")] BytecodeMismatch, + #[error("Creation bytecode is not equal to generated one from given source")] + CreationBytecodeMismatch, #[error("Constructor arguments are not correct")] IncorrectConstructorArguments, #[error("Compilation takes too much time")] CompilationTimeout, #[error("{0} error: {1}")] - CompilerError(String, String), + CompilerError(&'static str, String), #[error("Compilation error")] CompilationError(serde_json::Value), #[error("Unknown {0} version: {1}")] - UnknownCompilerVersion(String, String), + UnknownCompilerVersion(&'static str, String), #[error("Contract with {0} name is missing in sources")] MissingContract(String), #[error("There is no {0} source file")] @@ -23,3 +27,9 @@ pub enum ContractVerifierError { #[error("Failed to deserialize standard JSON input")] FailedToDeserializeInput, } + +impl From for ContractVerifierError { + fn from(err: DalError) -> Self { + Self::Internal(err.generalize()) + } +} diff --git a/core/lib/contract_verifier/src/lib.rs b/core/lib/contract_verifier/src/lib.rs index c8d9b89d834c..686bb0d7bdc3 100644 --- a/core/lib/contract_verifier/src/lib.rs +++ b/core/lib/contract_verifier/src/lib.rs @@ -1,101 +1,259 @@ +//! Contract verifier able to verify contracts created with `zksolc` or `zkvyper` toolchains. + use std::{ - collections::HashMap, - path::{Path, PathBuf}, + fmt, + sync::Arc, time::{Duration, Instant}, }; use anyhow::Context as _; use chrono::Utc; use ethabi::{Contract, Token}; -use lazy_static::lazy_static; -use regex::Regex; use tokio::time; -use zksync_config::ContractVerifierConfig; -use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; +use zksync_dal::{contract_verification_dal::DeployedContractData, ConnectionPool, Core, CoreDal}; use zksync_queued_job_processor::{async_trait, JobProcessor}; use zksync_types::{ contract_verification_api::{ - CompilationArtifacts, CompilerType, DeployContractCalldata, SourceCodeData, - VerificationInfo, VerificationRequest, + self as api, CompilationArtifacts, VerificationIncomingRequest, VerificationInfo, + VerificationRequest, }, - Address, + Address, CONTRACT_DEPLOYER_ADDRESS, }; -use zksync_utils::env::Workspace; +use zksync_utils::bytecode::{prepare_evm_bytecode, BytecodeMarker}; use crate::{ + compilers::{Solc, ZkSolc, ZkVyper}, error::ContractVerifierError, metrics::API_CONTRACT_VERIFIER_METRICS, - zksolc_utils::{Optimizer, Settings, Source, StandardJson, ZkSolc, ZkSolcInput, ZkSolcOutput}, - zkvyper_utils::{ZkVyper, ZkVyperInput}, + resolver::{CompilerResolver, EnvCompilerResolver}, }; +mod compilers; pub mod error; mod metrics; -mod zksolc_utils; -mod zkvyper_utils; +mod resolver; +#[cfg(test)] +mod tests; -lazy_static! { - static ref DEPLOYER_CONTRACT: Contract = zksync_contracts::deployer_contract(); +#[derive(Debug)] +struct ZkCompilerVersions { + /// Version of the base / non-ZK compiler. + pub base: String, + /// Version of the ZK compiler. + pub zk: String, } -fn home_path() -> PathBuf { - Workspace::locate().core() +/// Internal counterpart of `ContractVersions` from API that encompasses all supported compilation modes. +#[derive(Debug)] +enum VersionedCompiler { + Solc(String), + #[allow(dead_code)] // TODO (EVM-864): add vyper support + Vyper(String), + ZkSolc(ZkCompilerVersions), + ZkVyper(ZkCompilerVersions), +} + +impl From for VersionedCompiler { + fn from(versions: api::CompilerVersions) -> Self { + match versions { + api::CompilerVersions::Solc { + compiler_solc_version, + compiler_zksolc_version: None, + } => Self::Solc(compiler_solc_version), + + api::CompilerVersions::Solc { + compiler_solc_version, + compiler_zksolc_version: Some(zk), + } => Self::ZkSolc(ZkCompilerVersions { + base: compiler_solc_version, + zk, + }), + + api::CompilerVersions::Vyper { + compiler_vyper_version, + compiler_zkvyper_version: None, + } => Self::Vyper(compiler_vyper_version), + + api::CompilerVersions::Vyper { + compiler_vyper_version, + compiler_zkvyper_version: Some(zk), + } => Self::ZkVyper(ZkCompilerVersions { + base: compiler_vyper_version, + zk, + }), + } + } +} + +impl VersionedCompiler { + fn expected_bytecode_kind(&self) -> BytecodeMarker { + match self { + Self::Solc(_) | Self::Vyper(_) => BytecodeMarker::Evm, + Self::ZkSolc(_) | Self::ZkVyper(_) => BytecodeMarker::EraVm, + } + } } -#[derive(Debug)] enum ConstructorArgs { Check(Vec), Ignore, } -#[derive(Debug)] +impl fmt::Debug for ConstructorArgs { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Check(args) => write!(formatter, "0x{}", hex::encode(args)), + Self::Ignore => formatter.write_str("(ignored)"), + } + } +} + +#[derive(Debug, Clone)] pub struct ContractVerifier { - config: ContractVerifierConfig, + compilation_timeout: Duration, + contract_deployer: Contract, connection_pool: ConnectionPool, + compiler_resolver: Arc, } impl ContractVerifier { - pub fn new(config: ContractVerifierConfig, connection_pool: ConnectionPool) -> Self { - Self { - config, + /// Creates a new verifier instance. + pub async fn new( + compilation_timeout: Duration, + connection_pool: ConnectionPool, + ) -> anyhow::Result { + Self::with_resolver( + compilation_timeout, + connection_pool, + Arc::::default(), + ) + .await + } + + async fn with_resolver( + compilation_timeout: Duration, + connection_pool: ConnectionPool, + compiler_resolver: Arc, + ) -> anyhow::Result { + let this = Self { + compilation_timeout, + contract_deployer: zksync_contracts::deployer_contract(), connection_pool, + compiler_resolver, + }; + this.sync_compiler_versions().await?; + Ok(this) + } + + /// Synchronizes compiler versions. + #[tracing::instrument(level = "debug", skip_all)] + async fn sync_compiler_versions(&self) -> anyhow::Result<()> { + let supported_versions = self + .compiler_resolver + .supported_versions() + .await + .context("cannot get supported compilers")?; + if supported_versions.lacks_any_compiler() { + tracing::warn!( + ?supported_versions, + "contract verifier lacks support of at least one compiler entirely; it may be incorrectly set up" + ); } + tracing::info!( + ?supported_versions, + "persisting supported compiler versions" + ); + + let mut storage = self + .connection_pool + .connection_tagged("contract_verifier") + .await?; + let mut transaction = storage.start_transaction().await?; + transaction + .contract_verification_dal() + .set_zksolc_versions(&supported_versions.zksolc) + .await?; + transaction + .contract_verification_dal() + .set_solc_versions(&supported_versions.solc) + .await?; + transaction + .contract_verification_dal() + .set_zkvyper_versions(&supported_versions.zkvyper) + .await?; + transaction + .contract_verification_dal() + .set_vyper_versions(&supported_versions.vyper) + .await?; + transaction.commit().await?; + Ok(()) } + #[tracing::instrument( + level = "debug", + skip_all, + err, + fields(id = request.id, addr = ?request.req.contract_address) + )] async fn verify( - storage: &mut Connection<'_, Core>, + &self, mut request: VerificationRequest, - config: ContractVerifierConfig, ) -> Result { - let artifacts = Self::compile(request.clone(), config).await?; - // Bytecode should be present because it is checked when accepting request. - let (deployed_bytecode, creation_tx_calldata) = storage + let mut storage = self + .connection_pool + .connection_tagged("contract_verifier") + .await?; + let deployed_contract = storage .contract_verification_dal() - .get_contract_info_for_verification(request.req.contract_address).await - .unwrap() - .ok_or_else(|| { - tracing::warn!("Contract is missing in DB for already accepted verification request. Contract address: {:#?}", request.req.contract_address); - ContractVerifierError::InternalError + .get_contract_info_for_verification(request.req.contract_address) + .await? + .with_context(|| { + format!( + "Contract is missing in DB for already accepted verification request. Contract address: {:#?}", + request.req.contract_address + ) })?; - let constructor_args = Self::decode_constructor_arguments_from_calldata( - creation_tx_calldata, - request.req.contract_address, - ); + drop(storage); + + let bytecode_marker = BytecodeMarker::new(deployed_contract.bytecode_hash) + .context("unknown bytecode kind")?; + let artifacts = self.compile(request.req.clone(), bytecode_marker).await?; + let constructor_args = match bytecode_marker { + BytecodeMarker::EraVm => self + .decode_era_vm_constructor_args(&deployed_contract, request.req.contract_address)?, + BytecodeMarker::Evm => Self::decode_evm_constructor_args( + request.id, + &deployed_contract, + &artifacts.bytecode, + )?, + }; - if artifacts.bytecode != deployed_bytecode { + let deployed_bytecode = match bytecode_marker { + BytecodeMarker::EraVm => deployed_contract.bytecode.as_slice(), + BytecodeMarker::Evm => prepare_evm_bytecode(&deployed_contract.bytecode) + .context("invalid stored EVM bytecode")?, + }; + + if artifacts.deployed_bytecode() != deployed_bytecode { tracing::info!( - "Bytecode mismatch req {}, deployed: 0x{}, compiled 0x{}", - request.id, - hex::encode(deployed_bytecode), - hex::encode(artifacts.bytecode) + request_id = request.id, + deployed = hex::encode(deployed_bytecode), + compiled = hex::encode(artifacts.deployed_bytecode()), + "Deployed (runtime) bytecode mismatch", ); return Err(ContractVerifierError::BytecodeMismatch); } match constructor_args { ConstructorArgs::Check(args) => { - if request.req.constructor_arguments.0 != args { + let provided_constructor_args = &request.req.constructor_arguments.0; + if *provided_constructor_args != args { + tracing::trace!( + "Constructor args mismatch, deployed: 0x{}, provided in request: 0x{}", + hex::encode(&args), + hex::encode(provided_constructor_args) + ); return Err(ContractVerifierError::IncorrectConstructorArguments); } } @@ -104,360 +262,271 @@ impl ContractVerifier { } } + let verified_at = Utc::now(); + tracing::trace!(%verified_at, "verified request"); Ok(VerificationInfo { request, artifacts, - verified_at: Utc::now(), + verified_at, }) } async fn compile_zksolc( - request: VerificationRequest, - config: ContractVerifierConfig, + &self, + version: &ZkCompilerVersions, + req: VerificationIncomingRequest, ) -> Result { - // Users may provide either just contract name or - // source file name and contract name joined with ":". - let (file_name, contract_name) = - if let Some((file_name, contract_name)) = request.req.contract_name.rsplit_once(':') { - (file_name.to_string(), contract_name.to_string()) - } else { - ( - format!("{}.sol", request.req.contract_name), - request.req.contract_name.clone(), - ) - }; - let input = Self::build_zksolc_input(request.clone(), file_name.clone())?; - - let zksolc_path = Path::new(&home_path()) - .join("etc") - .join("zksolc-bin") - .join(request.req.compiler_versions.zk_compiler_version()) - .join("zksolc"); - if !zksolc_path.exists() { - return Err(ContractVerifierError::UnknownCompilerVersion( - "zksolc".to_string(), - request.req.compiler_versions.zk_compiler_version(), - )); - } + let zksolc = self.compiler_resolver.resolve_zksolc(version).await?; + tracing::debug!(?zksolc, ?version, "resolved compiler"); + let input = ZkSolc::build_input(req)?; - let solc_path = Path::new(&home_path()) - .join("etc") - .join("solc-bin") - .join(request.req.compiler_versions.compiler_version()) - .join("solc"); - if !solc_path.exists() { - return Err(ContractVerifierError::UnknownCompilerVersion( - "solc".to_string(), - request.req.compiler_versions.compiler_version(), - )); - } - - let zksolc = ZkSolc::new( - zksolc_path, - solc_path, - request.req.compiler_versions.zk_compiler_version(), - ); + time::timeout(self.compilation_timeout, zksolc.compile(input)) + .await + .map_err(|_| ContractVerifierError::CompilationTimeout)? + } - let output = time::timeout(config.compilation_timeout(), zksolc.async_compile(input)) + async fn compile_zkvyper( + &self, + version: &ZkCompilerVersions, + req: VerificationIncomingRequest, + ) -> Result { + let zkvyper = self.compiler_resolver.resolve_zkvyper(version).await?; + tracing::debug!(?zkvyper, ?version, "resolved compiler"); + let input = ZkVyper::build_input(req)?; + time::timeout(self.compilation_timeout, zkvyper.compile(input)) .await - .map_err(|_| ContractVerifierError::CompilationTimeout)??; - - match output { - ZkSolcOutput::StandardJson(output) => { - if let Some(errors) = output.get("errors") { - let errors = errors.as_array().unwrap().clone(); - if errors - .iter() - .any(|err| err["severity"].as_str().unwrap() == "error") - { - let error_messages = errors - .into_iter() - .map(|err| err["formattedMessage"].clone()) - .collect(); - return Err(ContractVerifierError::CompilationError( - serde_json::Value::Array(error_messages), - )); - } - } + .map_err(|_| ContractVerifierError::CompilationTimeout)? + } - let contracts = output["contracts"] - .get(file_name.as_str()) - .cloned() - .ok_or(ContractVerifierError::MissingSource(file_name))?; - let contract = contracts - .get(&contract_name) - .cloned() - .ok_or(ContractVerifierError::MissingContract(contract_name))?; - let bytecode_str = contract["evm"]["bytecode"]["object"].as_str().ok_or( - ContractVerifierError::AbstractContract(request.req.contract_name), - )?; - let bytecode = hex::decode(bytecode_str).unwrap(); - let abi = contract["abi"].clone(); - if !abi.is_array() { - tracing::error!( - "zksolc returned unexpected value for ABI: {}", - serde_json::to_string_pretty(&abi).unwrap() - ); - return Err(ContractVerifierError::InternalError); - } + async fn compile_solc( + &self, + version: &str, + req: VerificationIncomingRequest, + ) -> Result { + let solc = self.compiler_resolver.resolve_solc(version).await?; + tracing::debug!(?solc, ?req.compiler_versions, "resolved compiler"); + let input = Solc::build_input(req)?; - Ok(CompilationArtifacts { bytecode, abi }) - } - ZkSolcOutput::YulSingleFile(output) => { - let re = Regex::new(r"Contract `.*` bytecode: 0x([\da-f]+)").unwrap(); - let cap = re.captures(&output).unwrap(); - let bytecode_str = cap.get(1).unwrap().as_str(); - let bytecode = hex::decode(bytecode_str).unwrap(); - Ok(CompilationArtifacts { - bytecode, - abi: serde_json::Value::Array(Vec::new()), - }) - } - } + time::timeout(self.compilation_timeout, solc.compile(input)) + .await + .map_err(|_| ContractVerifierError::CompilationTimeout)? } - async fn compile_zkvyper( - request: VerificationRequest, - config: ContractVerifierConfig, + #[tracing::instrument(level = "debug", skip_all)] + async fn compile( + &self, + req: VerificationIncomingRequest, + bytecode_marker: BytecodeMarker, ) -> Result { - // Users may provide either just contract name or - // source file name and contract name joined with ":". - let contract_name = - if let Some((_file_name, contract_name)) = request.req.contract_name.rsplit_once(':') { - contract_name.to_string() - } else { - request.req.contract_name.clone() - }; - let input = Self::build_zkvyper_input(request.clone())?; - - let zkvyper_path = Path::new(&home_path()) - .join("etc") - .join("zkvyper-bin") - .join(request.req.compiler_versions.zk_compiler_version()) - .join("zkvyper"); - if !zkvyper_path.exists() { - return Err(ContractVerifierError::UnknownCompilerVersion( - "zkvyper".to_string(), - request.req.compiler_versions.zk_compiler_version(), - )); + let compiler_type = req.source_code_data.compiler_type(); + let compiler_type_by_versions = req.compiler_versions.compiler_type(); + if compiler_type != compiler_type_by_versions { + // Should be checked when receiving a request, so here it's more of a sanity check + let err = anyhow::anyhow!( + "specified compiler versions {:?} belong to a differing toolchain than source code ({compiler_type:?})", + req.compiler_versions + ); + return Err(err.into()); } - let vyper_path = Path::new(&home_path()) - .join("etc") - .join("vyper-bin") - .join(request.req.compiler_versions.compiler_version()) - .join("vyper"); - if !vyper_path.exists() { - return Err(ContractVerifierError::UnknownCompilerVersion( - "vyper".to_string(), - request.req.compiler_versions.compiler_version(), - )); + let compiler = VersionedCompiler::from(req.compiler_versions.clone()); + if compiler.expected_bytecode_kind() != bytecode_marker { + let err = anyhow::anyhow!( + "bytecode kind expected by compiler {compiler:?} differs from the actual bytecode kind \ + of the verified contract ({bytecode_marker:?})", + ); + return Err(err.into()); } - let zkvyper = ZkVyper::new(zkvyper_path, vyper_path); - - let output = time::timeout(config.compilation_timeout(), zkvyper.async_compile(input)) - .await - .map_err(|_| ContractVerifierError::CompilationTimeout)??; - - let file_name = format!("{contract_name}.vy"); - let object = output - .as_object() - .cloned() - .ok_or(ContractVerifierError::InternalError)?; - for (path, artifact) in object { - let path = Path::new(&path); - if path.file_name().unwrap().to_str().unwrap() == file_name { - let bytecode_str = artifact["bytecode"] - .as_str() - .ok_or(ContractVerifierError::InternalError)?; - let bytecode_without_prefix = - bytecode_str.strip_prefix("0x").unwrap_or(bytecode_str); - let bytecode = hex::decode(bytecode_without_prefix).unwrap(); - return Ok(CompilationArtifacts { - abi: artifact["abi"].clone(), - bytecode, - }); + match &compiler { + VersionedCompiler::Solc(version) => self.compile_solc(version, req).await, + VersionedCompiler::Vyper(_) => { + // TODO (EVM-864): add vyper support + let err = anyhow::anyhow!("vyper toolchain is not yet supported for EVM contracts"); + return Err(err.into()); } + VersionedCompiler::ZkSolc(version) => self.compile_zksolc(version, req).await, + VersionedCompiler::ZkVyper(version) => self.compile_zkvyper(version, req).await, } - - Err(ContractVerifierError::MissingContract(contract_name)) } - pub async fn compile( - request: VerificationRequest, - config: ContractVerifierConfig, - ) -> Result { - match request.req.source_code_data.compiler_type() { - CompilerType::Solc => Self::compile_zksolc(request, config).await, - CompilerType::Vyper => Self::compile_zkvyper(request, config).await, + /// All returned errors are internal. + #[tracing::instrument(level = "trace", skip_all, ret, err)] + fn decode_era_vm_constructor_args( + &self, + contract: &DeployedContractData, + contract_address_to_verify: Address, + ) -> anyhow::Result { + let Some(calldata) = &contract.calldata else { + return Ok(ConstructorArgs::Ignore); + }; + + if contract.contract_address == Some(CONTRACT_DEPLOYER_ADDRESS) { + self.decode_contract_deployer_call(calldata, contract_address_to_verify) + } else { + Ok(ConstructorArgs::Ignore) } } - fn build_zksolc_input( - request: VerificationRequest, - file_name: String, - ) -> Result { - let default_output_selection = serde_json::json!( - { - "*": { - "*": [ "abi" ], - "": [ "abi" ] - } - } + fn decode_contract_deployer_call( + &self, + calldata: &[u8], + contract_address_to_verify: Address, + ) -> anyhow::Result { + anyhow::ensure!( + calldata.len() >= 4, + "calldata doesn't include Solidity function selector" ); - match request.req.source_code_data { - SourceCodeData::SolSingleFile(source_code) => { - let source = Source { - content: source_code, - }; - let sources: HashMap = - vec![(file_name, source)].into_iter().collect(); - let optimizer = Optimizer { - enabled: request.req.optimization_used, - mode: request.req.optimizer_mode.and_then(|s| s.chars().next()), - }; - let optimizer_value = serde_json::to_value(optimizer).unwrap(); - - let settings = Settings { - output_selection: Some(default_output_selection), - is_system: request.req.is_system, - force_evmla: request.req.force_evmla, - other: serde_json::Value::Object( - vec![("optimizer".to_string(), optimizer_value)] - .into_iter() - .collect(), - ), - }; - - Ok(ZkSolcInput::StandardJson(StandardJson { - language: "Solidity".to_string(), - sources, - settings, - })) + let contract_deployer = &self.contract_deployer; + let create = contract_deployer + .function("create") + .context("no `create` in contract deployer ABI")?; + let create2 = contract_deployer + .function("create2") + .context("no `create2` in contract deployer ABI")?; + let create_acc = contract_deployer + .function("createAccount") + .context("no `createAccount` in contract deployer ABI")?; + let create2_acc = contract_deployer + .function("create2Account") + .context("no `create2Account` in contract deployer ABI")?; + let force_deploy = contract_deployer + .function("forceDeployOnAddresses") + .context("no `forceDeployOnAddresses` in contract deployer ABI")?; + + let (selector, token_data) = calldata.split_at(4); + // It's assumed that `create` and `create2` methods have the same parameters + // and the same for `createAccount` and `create2Account`. + Ok(match selector { + selector + if selector == create.short_signature() + || selector == create2.short_signature() => + { + let tokens = create + .decode_input(token_data) + .context("failed to decode `create` / `create2` input")?; + // Constructor arguments are in the third parameter. + ConstructorArgs::Check( + tokens[2] + .clone() + .into_bytes() + .context("third parameter of `create/create2` should be of type `bytes`")?, + ) } - SourceCodeData::StandardJsonInput(map) => { - let mut compiler_input: StandardJson = - serde_json::from_value(serde_json::Value::Object(map)) - .map_err(|_| ContractVerifierError::FailedToDeserializeInput)?; - // Set default output selection even if it is different in request. - compiler_input.settings.output_selection = Some(default_output_selection); - Ok(ZkSolcInput::StandardJson(compiler_input)) + selector + if selector == create_acc.short_signature() + || selector == create2_acc.short_signature() => + { + let tokens = create + .decode_input(token_data) + .context("failed to decode `createAccount` / `create2Account` input")?; + // Constructor arguments are in the third parameter. + ConstructorArgs::Check(tokens[2].clone().into_bytes().context( + "third parameter of `createAccount/create2Account` should be of type `bytes`", + )?) } - SourceCodeData::YulSingleFile(source_code) => Ok(ZkSolcInput::YulSingleFile { - source_code, - is_system: request.req.is_system, - }), - _ => panic!("Unexpected SourceCode variant"), - } - } - - fn build_zkvyper_input( - request: VerificationRequest, - ) -> Result { - let sources = match request.req.source_code_data { - SourceCodeData::VyperMultiFile(s) => s, - _ => panic!("Unexpected SourceCode variant"), - }; - Ok(ZkVyperInput { - sources, - optimizer_mode: request.req.optimizer_mode, + selector if selector == force_deploy.short_signature() => { + Self::decode_force_deployment(token_data, force_deploy, contract_address_to_verify) + .context("failed decoding force deployment")? + } + _ => ConstructorArgs::Ignore, }) } - fn decode_constructor_arguments_from_calldata( - calldata: DeployContractCalldata, + fn decode_force_deployment( + token_data: &[u8], + force_deploy: ðabi::Function, contract_address_to_verify: Address, - ) -> ConstructorArgs { - match calldata { - DeployContractCalldata::Deploy(calldata) => { - let create = DEPLOYER_CONTRACT.function("create").unwrap(); - let create2 = DEPLOYER_CONTRACT.function("create2").unwrap(); - - let create_acc = DEPLOYER_CONTRACT.function("createAccount").unwrap(); - let create2_acc = DEPLOYER_CONTRACT.function("create2Account").unwrap(); - - let force_deploy = DEPLOYER_CONTRACT - .function("forceDeployOnAddresses") - .unwrap(); - // It's assumed that `create` and `create2` methods have the same parameters - // and the same for `createAccount` and `create2Account`. - match &calldata[0..4] { - selector - if selector == create.short_signature() - || selector == create2.short_signature() => - { - let tokens = create - .decode_input(&calldata[4..]) - .expect("Failed to decode input"); - // Constructor arguments are in the third parameter. - ConstructorArgs::Check(tokens[2].clone().into_bytes().expect( - "The third parameter of `create/create2` should be of type `bytes`", - )) + ) -> anyhow::Result { + let tokens = force_deploy + .decode_input(token_data) + .context("failed to decode `forceDeployOnAddresses` input")?; + let deployments = tokens[0] + .clone() + .into_array() + .context("first parameter of `forceDeployOnAddresses` is not an array")?; + for deployment in deployments { + match deployment { + Token::Tuple(tokens) => { + let address = tokens[1] + .clone() + .into_address() + .context("unexpected `address`")?; + if address == contract_address_to_verify { + let call_constructor = tokens[2] + .clone() + .into_bool() + .context("unexpected `call_constructor`")?; + return Ok(if call_constructor { + let input = tokens[4] + .clone() + .into_bytes() + .context("unexpected constructor input")?; + ConstructorArgs::Check(input) + } else { + ConstructorArgs::Ignore + }); } - selector - if selector == create_acc.short_signature() - || selector == create2_acc.short_signature() => - { - let tokens = create - .decode_input(&calldata[4..]) - .expect("Failed to decode input"); - // Constructor arguments are in the third parameter. - ConstructorArgs::Check( - tokens[2].clone().into_bytes().expect( - "The third parameter of `createAccount/create2Account` should be of type `bytes`", - ), - ) - } - selector if selector == force_deploy.short_signature() => { - let tokens = force_deploy - .decode_input(&calldata[4..]) - .expect("Failed to decode input"); - let deployments = tokens[0].clone().into_array().unwrap(); - for deployment in deployments { - match deployment { - Token::Tuple(tokens) => { - let address = tokens[1].clone().into_address().unwrap(); - if address == contract_address_to_verify { - let call_constructor = - tokens[2].clone().into_bool().unwrap(); - return if call_constructor { - let input = tokens[4].clone().into_bytes().unwrap(); - ConstructorArgs::Check(input) - } else { - ConstructorArgs::Ignore - }; - } - } - _ => panic!("Expected `deployment` to be a tuple"), - } - } - panic!("Couldn't find force deployment for given address"); - } - _ => ConstructorArgs::Ignore, } + _ => anyhow::bail!("expected `deployment` to be a tuple"), } - DeployContractCalldata::Ignore => ConstructorArgs::Ignore, } + anyhow::bail!("couldn't find force deployment for address {contract_address_to_verify:?}"); } + fn decode_evm_constructor_args( + request_id: usize, + contract: &DeployedContractData, + creation_bytecode: &[u8], + ) -> Result { + let Some(calldata) = &contract.calldata else { + return Ok(ConstructorArgs::Ignore); + }; + if contract.contract_address.is_some() { + // Not an EVM deployment transaction + return Ok(ConstructorArgs::Ignore); + } + + let args = calldata.strip_prefix(creation_bytecode).ok_or_else(|| { + tracing::info!( + request_id, + calldata = hex::encode(calldata), + compiled = hex::encode(creation_bytecode), + "Creation bytecode mismatch" + ); + ContractVerifierError::CreationBytecodeMismatch + })?; + Ok(ConstructorArgs::Check(args.to_vec())) + } + + #[tracing::instrument(level = "debug", skip_all, err, fields(id = request_id))] async fn process_result( - storage: &mut Connection<'_, Core>, + &self, request_id: usize, verification_result: Result, - ) { + ) -> anyhow::Result<()> { + let mut storage = self + .connection_pool + .connection_tagged("contract_verifier") + .await?; match verification_result { Ok(info) => { storage .contract_verification_dal() .save_verification_info(info) - .await - .unwrap(); - tracing::info!("Successfully processed request with id = {}", request_id); + .await?; + tracing::info!("Successfully processed request with id = {request_id}"); } Err(error) => { - let error_message = error.to_string(); + let error_message = match &error { + ContractVerifierError::Internal(err) => { + // Do not expose the error externally, but log it. + tracing::warn!(request_id, "internal error processing request: {err}"); + "internal error".to_owned() + } + _ => error.to_string(), + }; let compilation_errors = match error { ContractVerifierError::CompilationError(compilation_errors) => { compilation_errors @@ -466,12 +535,12 @@ impl ContractVerifier { }; storage .contract_verification_dal() - .save_verification_error(request_id, error_message, compilation_errors, None) - .await - .unwrap(); - tracing::info!("Request with id = {} was failed", request_id); + .save_verification_error(request_id, &error_message, &compilation_errors, None) + .await?; + tracing::info!("Request with id = {request_id} was failed"); } } + Ok(()) } } @@ -485,33 +554,37 @@ impl JobProcessor for ContractVerifier { const BACKOFF_MULTIPLIER: u64 = 1; async fn get_next_job(&self) -> anyhow::Result> { - let mut connection = self.connection_pool.connection().await.unwrap(); - - // Time overhead for all operations except for compilation. + /// Time overhead for all operations except for compilation. const TIME_OVERHEAD: Duration = Duration::from_secs(10); + let mut connection = self + .connection_pool + .connection_tagged("contract_verifier") + .await?; // Considering that jobs that reach compilation timeout will be executed in // `compilation_timeout` + `non_compilation_time_overhead` (which is significantly less than `compilation_timeout`), // we re-pick up jobs that are being executed for a bit more than `compilation_timeout`. let job = connection .contract_verification_dal() - .get_next_queued_verification_request(self.config.compilation_timeout() + TIME_OVERHEAD) - .await - .context("get_next_queued_verification_request()")?; - + .get_next_queued_verification_request(self.compilation_timeout + TIME_OVERHEAD) + .await?; Ok(job.map(|job| (job.id, job))) } async fn save_failure(&self, job_id: usize, _started_at: Instant, error: String) { - let mut connection = self.connection_pool.connection().await.unwrap(); + let mut connection = self + .connection_pool + .connection_tagged("contract_verifier") + .await + .unwrap(); connection .contract_verification_dal() .save_verification_error( job_id, - "Internal error".to_string(), - serde_json::Value::Array(Vec::new()), - Some(error), + "Internal error", + &serde_json::Value::Array(Vec::new()), + Some(&error), ) .await .unwrap(); @@ -524,16 +597,13 @@ impl JobProcessor for ContractVerifier { job: VerificationRequest, started_at: Instant, ) -> tokio::task::JoinHandle> { - let connection_pool = self.connection_pool.clone(); - let config = self.config.clone(); + let this = self.clone(); tokio::task::spawn(async move { tracing::info!("Started to process request with id = {}", job.id); - let mut connection = connection_pool.connection().await.unwrap(); - let job_id = job.id; - let verification_result = Self::verify(&mut connection, job, config).await; - Self::process_result(&mut connection, job_id, verification_result).await; + let verification_result = this.verify(job).await; + this.process_result(job_id, verification_result).await?; API_CONTRACT_VERIFIER_METRICS .request_processing_time diff --git a/core/lib/contract_verifier/src/metrics.rs b/core/lib/contract_verifier/src/metrics.rs index fd98f51cd560..1c6796cd7f38 100644 --- a/core/lib/contract_verifier/src/metrics.rs +++ b/core/lib/contract_verifier/src/metrics.rs @@ -5,6 +5,7 @@ use vise::{Buckets, Histogram, Metrics}; #[derive(Debug, Metrics)] #[metrics(prefix = "api_contract_verifier")] pub(crate) struct ApiContractVerifierMetrics { + /// Latency of processing a single request. #[metrics(buckets = Buckets::LATENCIES)] pub request_processing_time: Histogram, } diff --git a/core/lib/contract_verifier/src/resolver.rs b/core/lib/contract_verifier/src/resolver.rs new file mode 100644 index 000000000000..34a70b759797 --- /dev/null +++ b/core/lib/contract_verifier/src/resolver.rs @@ -0,0 +1,237 @@ +use std::{ + fmt, + path::{Path, PathBuf}, +}; + +use anyhow::Context as _; +use tokio::fs; +use zksync_queued_job_processor::async_trait; +use zksync_types::contract_verification_api::CompilationArtifacts; +use zksync_utils::env::Workspace; + +use crate::{ + compilers::{Solc, SolcInput, ZkSolc, ZkSolcInput, ZkVyper, ZkVyperInput}, + error::ContractVerifierError, + ZkCompilerVersions, +}; + +#[derive(Debug, Clone, Copy)] +enum CompilerType { + Solc, + ZkSolc, + Vyper, + ZkVyper, +} + +impl CompilerType { + fn as_str(self) -> &'static str { + match self { + Self::Solc => "solc", + Self::ZkSolc => "zksolc", + Self::Vyper => "vyper", + Self::ZkVyper => "zkvyper", + } + } + + /// Returns the absolute path to the compiler binary. + fn bin_path_unchecked(self, home_dir: &Path, version: &str) -> PathBuf { + let compiler_dir = match self { + Self::Solc => "solc-bin", + Self::ZkSolc => "zksolc-bin", + Self::Vyper => "vyper-bin", + Self::ZkVyper => "zkvyper-bin", + }; + home_dir + .join("etc") + .join(compiler_dir) + .join(version) + .join(self.as_str()) + } + + async fn bin_path( + self, + home_dir: &Path, + version: &str, + ) -> Result { + let path = self.bin_path_unchecked(home_dir, version); + if !fs::try_exists(&path) + .await + .with_context(|| format!("failed accessing `{}`", self.as_str()))? + { + return Err(ContractVerifierError::UnknownCompilerVersion( + self.as_str(), + version.to_owned(), + )); + } + Ok(path) + } +} + +/// Compiler versions supported by a [`CompilerResolver`]. +#[derive(Debug)] +pub(crate) struct SupportedCompilerVersions { + pub solc: Vec, + pub zksolc: Vec, + pub vyper: Vec, + pub zkvyper: Vec, +} + +impl SupportedCompilerVersions { + pub fn lacks_any_compiler(&self) -> bool { + self.solc.is_empty() + || self.zksolc.is_empty() + || self.vyper.is_empty() + || self.zkvyper.is_empty() + } +} + +#[derive(Debug, Clone)] +pub(crate) struct CompilerPaths { + /// Path to the base (non-zk) compiler. + pub base: PathBuf, + /// Path to the zk compiler. + pub zk: PathBuf, +} + +/// Encapsulates compiler paths resolution. +#[async_trait] +pub(crate) trait CompilerResolver: fmt::Debug + Send + Sync { + /// Returns compiler versions supported by this resolver. + /// + /// # Errors + /// + /// Returned errors are assumed to be fatal. + async fn supported_versions(&self) -> anyhow::Result; + + /// Resolves a `solc` compiler. + async fn resolve_solc( + &self, + version: &str, + ) -> Result>, ContractVerifierError>; + + /// Resolves a `zksolc` compiler. + async fn resolve_zksolc( + &self, + version: &ZkCompilerVersions, + ) -> Result>, ContractVerifierError>; + + /// Resolves a `zkvyper` compiler. + async fn resolve_zkvyper( + &self, + version: &ZkCompilerVersions, + ) -> Result>, ContractVerifierError>; +} + +/// Encapsulates a one-off compilation process. +#[async_trait] +pub(crate) trait Compiler: Send + fmt::Debug { + /// Performs compilation. + async fn compile( + self: Box, + input: In, + ) -> Result; +} + +/// Default [`CompilerResolver`] using pre-downloaded compilers in the `/etc` subdirectories (relative to the workspace). +#[derive(Debug)] +pub(crate) struct EnvCompilerResolver { + home_dir: PathBuf, +} + +impl Default for EnvCompilerResolver { + fn default() -> Self { + Self { + home_dir: Workspace::locate().core(), + } + } +} + +impl EnvCompilerResolver { + async fn read_dir(&self, dir: &str) -> anyhow::Result> { + let mut dir_entries = fs::read_dir(self.home_dir.join(dir)) + .await + .context("failed reading dir")?; + let mut versions = vec![]; + while let Some(entry) = dir_entries.next_entry().await? { + let Ok(file_type) = entry.file_type().await else { + continue; + }; + if file_type.is_dir() { + if let Ok(name) = entry.file_name().into_string() { + versions.push(name); + } + } + } + Ok(versions) + } +} + +#[async_trait] +impl CompilerResolver for EnvCompilerResolver { + async fn supported_versions(&self) -> anyhow::Result { + Ok(SupportedCompilerVersions { + solc: self + .read_dir("etc/solc-bin") + .await + .context("failed reading solc dir")?, + zksolc: self + .read_dir("etc/zksolc-bin") + .await + .context("failed reading zksolc dir")?, + vyper: self + .read_dir("etc/vyper-bin") + .await + .context("failed reading vyper dir")?, + zkvyper: self + .read_dir("etc/zkvyper-bin") + .await + .context("failed reading zkvyper dir")?, + }) + } + + async fn resolve_solc( + &self, + version: &str, + ) -> Result>, ContractVerifierError> { + let solc_path = CompilerType::Solc.bin_path(&self.home_dir, version).await?; + Ok(Box::new(Solc::new(solc_path))) + } + + async fn resolve_zksolc( + &self, + version: &ZkCompilerVersions, + ) -> Result>, ContractVerifierError> { + let zksolc_version = &version.zk; + let zksolc_path = CompilerType::ZkSolc + .bin_path(&self.home_dir, zksolc_version) + .await?; + let solc_path = CompilerType::Solc + .bin_path(&self.home_dir, &version.base) + .await?; + let compiler_paths = CompilerPaths { + base: solc_path, + zk: zksolc_path, + }; + Ok(Box::new(ZkSolc::new( + compiler_paths, + zksolc_version.to_owned(), + ))) + } + + async fn resolve_zkvyper( + &self, + version: &ZkCompilerVersions, + ) -> Result>, ContractVerifierError> { + let zkvyper_path = CompilerType::ZkVyper + .bin_path(&self.home_dir, &version.zk) + .await?; + let vyper_path = CompilerType::Vyper + .bin_path(&self.home_dir, &version.base) + .await?; + let compiler_paths = CompilerPaths { + base: vyper_path, + zk: zkvyper_path, + }; + Ok(Box::new(ZkVyper::new(compiler_paths))) + } +} diff --git a/core/lib/contract_verifier/src/tests/mod.rs b/core/lib/contract_verifier/src/tests/mod.rs new file mode 100644 index 000000000000..7caa5f32c991 --- /dev/null +++ b/core/lib/contract_verifier/src/tests/mod.rs @@ -0,0 +1,748 @@ +//! Tests for the contract verifier. + +use std::{collections::HashMap, iter}; + +use test_casing::{test_casing, Product}; +use tokio::sync::watch; +use zksync_dal::Connection; +use zksync_node_test_utils::{create_l1_batch, create_l2_block}; +use zksync_types::{ + contract_verification_api::{CompilerVersions, SourceCodeData, VerificationIncomingRequest}, + get_code_key, get_known_code_key, + l2::L2Tx, + tx::IncludedTxLocation, + Execute, L1BatchNumber, L2BlockNumber, ProtocolVersion, StorageLog, CONTRACT_DEPLOYER_ADDRESS, + H256, U256, +}; +use zksync_utils::{ + address_to_h256, + bytecode::{hash_bytecode, hash_evm_bytecode}, +}; +use zksync_vm_interface::{tracer::ValidationTraces, TransactionExecutionMetrics, VmEvent}; + +use super::*; +use crate::{ + compilers::{SolcInput, ZkSolcInput, ZkVyperInput}, + resolver::{Compiler, SupportedCompilerVersions}, +}; + +mod real; + +const SOLC_VERSION: &str = "0.8.27"; +const ZKSOLC_VERSION: &str = "1.5.4"; + +const BYTECODE_KINDS: [BytecodeMarker; 2] = [BytecodeMarker::EraVm, BytecodeMarker::Evm]; + +const COUNTER_CONTRACT: &str = r#" + contract Counter { + uint256 value; + + function increment(uint256 x) external { + value += x; + } + } +"#; +const COUNTER_CONTRACT_WITH_CONSTRUCTOR: &str = r#" + contract Counter { + uint256 value; + + constructor(uint256 _value) { + value = _value; + } + + function increment(uint256 x) external { + value += x; + } + } +"#; + +#[derive(Debug, Clone, Copy)] +enum TestContract { + Counter, + CounterWithConstructor, +} + +impl TestContract { + const ALL: [Self; 2] = [Self::Counter, Self::CounterWithConstructor]; + + fn source(self) -> &'static str { + match self { + Self::Counter => COUNTER_CONTRACT, + Self::CounterWithConstructor => COUNTER_CONTRACT_WITH_CONSTRUCTOR, + } + } + + fn constructor_args(self) -> &'static [Token] { + match self { + Self::Counter => &[], + Self::CounterWithConstructor => &[Token::Uint(U256([42, 0, 0, 0]))], + } + } +} + +/// Pads an EVM bytecode in the same ways it's done by system contracts. +fn pad_evm_bytecode(deployed_bytecode: &[u8]) -> Vec { + let mut padded = Vec::with_capacity(deployed_bytecode.len() + 32); + let len = U256::from(deployed_bytecode.len()); + padded.extend_from_slice(&[0; 32]); + len.to_big_endian(&mut padded); + padded.extend_from_slice(deployed_bytecode); + + // Pad to the 32-byte word boundary. + if padded.len() % 32 != 0 { + padded.extend(iter::repeat(0).take(32 - padded.len() % 32)); + } + assert_eq!(padded.len() % 32, 0); + + // Pad to contain the odd number of words. + if (padded.len() / 32) % 2 != 1 { + padded.extend_from_slice(&[0; 32]); + } + assert_eq!((padded.len() / 32) % 2, 1); + padded +} + +async fn mock_deployment( + storage: &mut Connection<'_, Core>, + address: Address, + bytecode: Vec, + constructor_args: &[Token], +) { + let bytecode_hash = hash_bytecode(&bytecode); + let deployment = Execute::for_deploy(H256::zero(), bytecode.clone(), constructor_args); + mock_deployment_inner(storage, address, bytecode_hash, bytecode, deployment).await; +} + +async fn mock_evm_deployment( + storage: &mut Connection<'_, Core>, + address: Address, + creation_bytecode: Vec, + deployed_bytecode: &[u8], + constructor_args: &[Token], +) { + let mut calldata = creation_bytecode; + calldata.extend_from_slice(ðabi::encode(constructor_args)); + let deployment = Execute { + contract_address: None, + calldata, // FIXME: check + value: 0.into(), + factory_deps: vec![], + }; + let bytecode = pad_evm_bytecode(deployed_bytecode); + let bytecode_hash = hash_evm_bytecode(&bytecode); + mock_deployment_inner(storage, address, bytecode_hash, bytecode, deployment).await; +} + +async fn mock_deployment_inner( + storage: &mut Connection<'_, Core>, + address: Address, + bytecode_hash: H256, + bytecode: Vec, + execute: Execute, +) { + let logs = [ + StorageLog::new_write_log(get_code_key(&address), bytecode_hash), + StorageLog::new_write_log(get_known_code_key(&bytecode_hash), H256::from_low_u64_be(1)), + ]; + storage + .storage_logs_dal() + .append_storage_logs(L2BlockNumber(0), &logs) + .await + .unwrap(); + storage + .factory_deps_dal() + .insert_factory_deps( + L2BlockNumber(0), + &HashMap::from([(bytecode_hash, bytecode.clone())]), + ) + .await + .unwrap(); + + let mut deploy_tx = L2Tx { + execute, + common_data: Default::default(), + received_timestamp_ms: 0, + raw_bytes: Some(vec![0; 128].into()), + }; + deploy_tx.set_input(vec![0; 128], H256::repeat_byte(0x23)); + storage + .transactions_dal() + .insert_transaction_l2( + &deploy_tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) + .await + .unwrap(); + + let deployer_address = Address::repeat_byte(0xff); + let location = IncludedTxLocation { + tx_hash: deploy_tx.hash(), + tx_index_in_l2_block: 0, + tx_initiator_address: deployer_address, + }; + let deploy_event = VmEvent { + location: (L1BatchNumber(0), 0), + address: CONTRACT_DEPLOYER_ADDRESS, + indexed_topics: vec![ + VmEvent::DEPLOY_EVENT_SIGNATURE, + address_to_h256(&deployer_address), + bytecode_hash, + address_to_h256(&address), + ], + value: vec![], + }; + storage + .events_dal() + .save_events(L2BlockNumber(0), &[(location, vec![&deploy_event])]) + .await + .unwrap(); +} + +type SharedMockFn = + Arc Result + Send + Sync>; + +#[derive(Clone)] +struct MockCompilerResolver { + zksolc: SharedMockFn, + solc: SharedMockFn, +} + +impl fmt::Debug for MockCompilerResolver { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("MockCompilerResolver") + .finish_non_exhaustive() + } +} + +impl MockCompilerResolver { + fn zksolc( + zksolc: impl Fn(ZkSolcInput) -> CompilationArtifacts + 'static + Send + Sync, + ) -> Self { + Self { + zksolc: Arc::new(move |input| Ok(zksolc(input))), + solc: Arc::new(|input| panic!("unexpected solc call: {input:?}")), + } + } + + fn solc(solc: impl Fn(SolcInput) -> CompilationArtifacts + 'static + Send + Sync) -> Self { + Self { + solc: Arc::new(move |input| Ok(solc(input))), + zksolc: Arc::new(|input| panic!("unexpected zksolc call: {input:?}")), + } + } +} + +#[async_trait] +impl Compiler for MockCompilerResolver { + async fn compile( + self: Box, + input: ZkSolcInput, + ) -> Result { + (self.zksolc)(input) + } +} + +#[async_trait] +impl Compiler for MockCompilerResolver { + async fn compile( + self: Box, + input: SolcInput, + ) -> Result { + (self.solc)(input) + } +} + +#[async_trait] +impl CompilerResolver for MockCompilerResolver { + async fn supported_versions(&self) -> anyhow::Result { + Ok(SupportedCompilerVersions { + solc: vec![SOLC_VERSION.to_owned()], + zksolc: vec![ZKSOLC_VERSION.to_owned()], + vyper: vec![], + zkvyper: vec![], + }) + } + + async fn resolve_solc( + &self, + version: &str, + ) -> Result>, ContractVerifierError> { + if version != SOLC_VERSION { + return Err(ContractVerifierError::UnknownCompilerVersion( + "solc", + version.to_owned(), + )); + } + Ok(Box::new(self.clone())) + } + + async fn resolve_zksolc( + &self, + version: &ZkCompilerVersions, + ) -> Result>, ContractVerifierError> { + if version.base != SOLC_VERSION { + return Err(ContractVerifierError::UnknownCompilerVersion( + "solc", + version.base.clone(), + )); + } + if version.zk != ZKSOLC_VERSION { + return Err(ContractVerifierError::UnknownCompilerVersion( + "zksolc", + version.zk.clone(), + )); + } + Ok(Box::new(self.clone())) + } + + async fn resolve_zkvyper( + &self, + _version: &ZkCompilerVersions, + ) -> Result>, ContractVerifierError> { + unreachable!("not tested") + } +} + +fn test_request(address: Address, source: &str) -> VerificationIncomingRequest { + VerificationIncomingRequest { + contract_address: address, + source_code_data: SourceCodeData::SolSingleFile(source.into()), + contract_name: "Counter".to_owned(), + compiler_versions: CompilerVersions::Solc { + compiler_zksolc_version: Some(ZKSOLC_VERSION.to_owned()), + compiler_solc_version: SOLC_VERSION.to_owned(), + }, + optimization_used: true, + optimizer_mode: None, + constructor_arguments: Default::default(), + is_system: false, + force_evmla: false, + } +} + +fn counter_contract_abi() -> serde_json::Value { + serde_json::json!([{ + "inputs": [{ + "internalType": "uint256", + "name": "x", + "type": "uint256", + }], + "name": "increment", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function", + }]) +} + +async fn prepare_storage(storage: &mut Connection<'_, Core>) { + // Storage must contain at least 1 block / batch for verifier-related queries to work correctly. + storage + .protocol_versions_dal() + .save_protocol_version_with_tx(&ProtocolVersion::default()) + .await + .unwrap(); + storage + .blocks_dal() + .insert_l2_block(&create_l2_block(0)) + .await + .unwrap(); + storage + .blocks_dal() + .insert_mock_l1_batch(&create_l1_batch(0)) + .await + .unwrap(); +} + +#[test_casing(2, TestContract::ALL)] +#[tokio::test] +async fn contract_verifier_basics(contract: TestContract) { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + let expected_bytecode = vec![0_u8; 32]; + + prepare_storage(&mut storage).await; + let address = Address::repeat_byte(1); + mock_deployment( + &mut storage, + address, + expected_bytecode.clone(), + contract.constructor_args(), + ) + .await; + let mut req = test_request(address, contract.source()); + req.constructor_arguments = ethabi::encode(contract.constructor_args()).into(); + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let mock_resolver = MockCompilerResolver::zksolc(|input| { + let ZkSolcInput::StandardJson { input, .. } = &input else { + panic!("unexpected input"); + }; + assert_eq!(input.language, "Solidity"); + assert_eq!(input.sources.len(), 1); + let source = input.sources.values().next().unwrap(); + assert!(source.content.contains("contract Counter"), "{source:?}"); + + CompilationArtifacts { + bytecode: vec![0; 32], + deployed_bytecode: None, + abi: counter_contract_abi(), + } + }); + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(mock_resolver), + ) + .await + .unwrap(); + + // Check that the compiler versions are synced. + let solc_versions = storage + .contract_verification_dal() + .get_solc_versions() + .await + .unwrap(); + assert_eq!(solc_versions, [SOLC_VERSION]); + let zksolc_versions = storage + .contract_verification_dal() + .get_zksolc_versions() + .await + .unwrap(); + assert_eq!(zksolc_versions, [ZKSOLC_VERSION]); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + assert_request_success(&mut storage, request_id, address, &expected_bytecode).await; +} + +async fn assert_request_success( + storage: &mut Connection<'_, Core>, + request_id: usize, + address: Address, + expected_bytecode: &[u8], +) -> VerificationInfo { + let status = storage + .contract_verification_dal() + .get_verification_request_status(request_id) + .await + .unwrap() + .expect("no status"); + assert_eq!(status.error, None); + assert_eq!(status.compilation_errors, None); + assert_eq!(status.status, "successful"); + + let verification_info = storage + .contract_verification_dal() + .get_contract_verification_info(address) + .await + .unwrap() + .expect("no verification info"); + assert_eq!(verification_info.artifacts.bytecode, *expected_bytecode); + assert_eq!(verification_info.artifacts.abi, counter_contract_abi()); + verification_info +} + +#[test_casing(2, TestContract::ALL)] +#[tokio::test] +async fn verifying_evm_bytecode(contract: TestContract) { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + let creation_bytecode = vec![3_u8; 20]; + let deployed_bytecode = vec![5_u8; 10]; + + prepare_storage(&mut storage).await; + let address = Address::repeat_byte(1); + mock_evm_deployment( + &mut storage, + address, + creation_bytecode.clone(), + &deployed_bytecode, + contract.constructor_args(), + ) + .await; + let mut req = test_request(address, contract.source()); + req.compiler_versions = CompilerVersions::Solc { + compiler_solc_version: SOLC_VERSION.to_owned(), + compiler_zksolc_version: None, + }; + req.constructor_arguments = ethabi::encode(contract.constructor_args()).into(); + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let artifacts = CompilationArtifacts { + bytecode: creation_bytecode.clone(), + deployed_bytecode: Some(deployed_bytecode), + abi: counter_contract_abi(), + }; + let mock_resolver = MockCompilerResolver::solc(move |input| { + assert_eq!(input.standard_json.language, "Solidity"); + assert_eq!(input.standard_json.sources.len(), 1); + let source = input.standard_json.sources.values().next().unwrap(); + assert!(source.content.contains("contract Counter"), "{source:?}"); + + artifacts.clone() + }); + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(mock_resolver), + ) + .await + .unwrap(); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + assert_request_success(&mut storage, request_id, address, &creation_bytecode).await; +} + +#[tokio::test] +async fn bytecode_mismatch_error() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let address = Address::repeat_byte(1); + mock_deployment(&mut storage, address, vec![0xff; 32], &[]).await; + let req = test_request(address, COUNTER_CONTRACT); + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let mock_resolver = MockCompilerResolver::zksolc(|_| CompilationArtifacts { + bytecode: vec![0; 32], + deployed_bytecode: None, + abi: counter_contract_abi(), + }); + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(mock_resolver), + ) + .await + .unwrap(); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + let status = storage + .contract_verification_dal() + .get_verification_request_status(request_id) + .await + .unwrap() + .expect("no status"); + assert_eq!(status.status, "failed"); + assert!(status.compilation_errors.is_none(), "{status:?}"); + let err = status.error.unwrap(); + assert_eq!(err, ContractVerifierError::BytecodeMismatch.to_string()); +} + +#[test_casing(4, Product((TestContract::ALL, BYTECODE_KINDS)))] +#[tokio::test] +async fn args_mismatch_error(contract: TestContract, bytecode_kind: BytecodeMarker) { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + + prepare_storage(&mut storage).await; + let address = Address::repeat_byte(1); + let bytecode = vec![0_u8; 32]; + match bytecode_kind { + BytecodeMarker::EraVm => { + mock_deployment( + &mut storage, + address, + bytecode.clone(), + contract.constructor_args(), + ) + .await; + } + BytecodeMarker::Evm => { + let creation_bytecode = vec![3_u8; 48]; + mock_evm_deployment( + &mut storage, + address, + creation_bytecode, + &bytecode, + contract.constructor_args(), + ) + .await; + } + } + + let mut req = test_request(address, contract.source()); + if matches!(bytecode_kind, BytecodeMarker::Evm) { + req.compiler_versions = CompilerVersions::Solc { + compiler_zksolc_version: None, + compiler_solc_version: SOLC_VERSION.to_owned(), + }; + } + + // Intentionally encode incorrect constructor args + req.constructor_arguments = match contract { + TestContract::Counter => ethabi::encode(&[Token::Bool(true)]).into(), + TestContract::CounterWithConstructor => ethabi::encode(&[]).into(), + }; + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let mock_resolver = match bytecode_kind { + BytecodeMarker::EraVm => MockCompilerResolver::zksolc(move |_| CompilationArtifacts { + bytecode: bytecode.clone(), + deployed_bytecode: None, + abi: counter_contract_abi(), + }), + BytecodeMarker::Evm => MockCompilerResolver::solc(move |_| CompilationArtifacts { + bytecode: vec![3_u8; 48], + deployed_bytecode: Some(bytecode.clone()), + abi: counter_contract_abi(), + }), + }; + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(mock_resolver), + ) + .await + .unwrap(); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + assert_constructor_args_mismatch(&mut storage, request_id).await; +} + +async fn assert_constructor_args_mismatch(storage: &mut Connection<'_, Core>, request_id: usize) { + let status = storage + .contract_verification_dal() + .get_verification_request_status(request_id) + .await + .unwrap() + .expect("no status"); + assert_eq!(status.status, "failed"); + assert_eq!(status.compilation_errors, None); + let err = status.error.unwrap(); + assert_eq!( + err, + ContractVerifierError::IncorrectConstructorArguments.to_string() + ); +} + +#[tokio::test] +async fn creation_bytecode_mismatch() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let address = Address::repeat_byte(1); + let creation_bytecode = vec![3; 20]; + let deployed_bytecode = vec![5; 10]; + mock_evm_deployment( + &mut storage, + address, + creation_bytecode, + &deployed_bytecode, + &[], + ) + .await; + let mut req = test_request(address, COUNTER_CONTRACT); + req.compiler_versions = CompilerVersions::Solc { + compiler_zksolc_version: None, + compiler_solc_version: SOLC_VERSION.to_owned(), + }; + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let mock_resolver = MockCompilerResolver::solc(move |_| CompilationArtifacts { + bytecode: vec![4; 20], // differs from `creation_bytecode` + deployed_bytecode: Some(deployed_bytecode.clone()), + abi: counter_contract_abi(), + }); + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(mock_resolver), + ) + .await + .unwrap(); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + let status = storage + .contract_verification_dal() + .get_verification_request_status(request_id) + .await + .unwrap() + .expect("no status"); + assert_eq!(status.status, "failed"); + assert!(status.compilation_errors.is_none(), "{status:?}"); + let err = status.error.unwrap(); + assert_eq!( + err, + ContractVerifierError::CreationBytecodeMismatch.to_string() + ); +} + +#[tokio::test] +async fn no_compiler_version() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let address = Address::repeat_byte(1); + mock_deployment(&mut storage, address, vec![0xff; 32], &[]).await; + let req = VerificationIncomingRequest { + compiler_versions: CompilerVersions::Solc { + compiler_zksolc_version: Some(ZKSOLC_VERSION.to_owned()), + compiler_solc_version: "1.0.0".to_owned(), // a man can dream + }, + ..test_request(address, COUNTER_CONTRACT) + }; + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let mock_resolver = + MockCompilerResolver::zksolc(|_| unreachable!("should reject unknown solc version")); + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(mock_resolver), + ) + .await + .unwrap(); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + let status = storage + .contract_verification_dal() + .get_verification_request_status(request_id) + .await + .unwrap() + .expect("no status"); + assert_eq!(status.status, "failed"); + assert!(status.compilation_errors.is_none(), "{status:?}"); + let error = status.error.unwrap(); + assert!(error.contains("solc version"), "{error}"); +} diff --git a/core/lib/contract_verifier/src/tests/real.rs b/core/lib/contract_verifier/src/tests/real.rs new file mode 100644 index 000000000000..a7113044b405 --- /dev/null +++ b/core/lib/contract_verifier/src/tests/real.rs @@ -0,0 +1,237 @@ +//! Tests using real compiler toolchains. Should be prepared by calling `zkstack contract-verifier init` +//! with at least one `solc` and `zksolc` version. If there are no compilers, the tests will be ignored +//! unless the `RUN_CONTRACT_VERIFICATION_TEST` env var is set to `true`, in which case the tests will fail. + +use std::{env, sync::Arc, time::Duration}; + +use zksync_utils::bytecode::validate_bytecode; + +use super::*; + +#[derive(Debug, Clone)] +struct TestCompilerVersions { + solc: String, + zksolc: String, +} + +impl TestCompilerVersions { + fn new(mut versions: SupportedCompilerVersions) -> Option { + let solc = versions + .solc + .into_iter() + .find(|ver| !ver.starts_with("zkVM"))?; + Some(Self { + solc, + zksolc: versions.zksolc.pop()?, + }) + } + + fn zksolc(self) -> ZkCompilerVersions { + ZkCompilerVersions { + base: self.solc, + zk: self.zksolc, + } + } + + fn solc_for_api(self, bytecode_kind: BytecodeMarker) -> CompilerVersions { + CompilerVersions::Solc { + compiler_solc_version: self.solc, + compiler_zksolc_version: match bytecode_kind { + BytecodeMarker::Evm => None, + BytecodeMarker::EraVm => Some(self.zksolc), + }, + } + } +} + +async fn checked_env_resolver() -> Option<(EnvCompilerResolver, TestCompilerVersions)> { + let compiler_resolver = EnvCompilerResolver::default(); + let supported_compilers = compiler_resolver.supported_versions().await.ok()?; + Some(( + compiler_resolver, + TestCompilerVersions::new(supported_compilers)?, + )) +} + +fn assert_no_compilers_expected() { + assert_ne!( + env::var("RUN_CONTRACT_VERIFICATION_TEST").ok().as_deref(), + Some("true"), + "Expected pre-installed compilers since `RUN_CONTRACT_VERIFICATION_TEST=true`, but they are not installed. \ + Use `zkstack contract-verifier init` to install compilers" + ); + println!("No compilers found, skipping the test"); +} + +/// Simplifies initializing real compiler resolver in tests. +macro_rules! real_resolver { + () => { + match checked_env_resolver().await { + Some(resolver_and_versions) => resolver_and_versions, + None => { + assert_no_compilers_expected(); + return; + } + } + }; +} + +#[tokio::test] +async fn using_real_compiler() { + let (compiler_resolver, supported_compilers) = real_resolver!(); + + let compiler = compiler_resolver + .resolve_zksolc(&supported_compilers.clone().zksolc()) + .await + .unwrap(); + let req = VerificationIncomingRequest { + compiler_versions: supported_compilers.solc_for_api(BytecodeMarker::EraVm), + ..test_request(Address::repeat_byte(1), COUNTER_CONTRACT) + }; + let input = ZkSolc::build_input(req).unwrap(); + let output = compiler.compile(input).await.unwrap(); + + validate_bytecode(&output.bytecode).unwrap(); + assert_eq!(output.abi, counter_contract_abi()); +} + +#[tokio::test] +async fn using_standalone_solc() { + let (compiler_resolver, supported_compilers) = real_resolver!(); + + let version = &supported_compilers.solc; + let compiler = compiler_resolver.resolve_solc(version).await.unwrap(); + let req = VerificationIncomingRequest { + compiler_versions: CompilerVersions::Solc { + compiler_solc_version: version.clone(), + compiler_zksolc_version: None, + }, + ..test_request(Address::repeat_byte(1), COUNTER_CONTRACT) + }; + let input = Solc::build_input(req).unwrap(); + let output = compiler.compile(input).await.unwrap(); + + assert!(output.deployed_bytecode.is_some()); + assert_eq!(output.abi, counter_contract_abi()); +} + +#[test_casing(2, BYTECODE_KINDS)] +#[tokio::test] +async fn using_real_compiler_in_verifier(bytecode_kind: BytecodeMarker) { + let (compiler_resolver, supported_compilers) = real_resolver!(); + + let req = VerificationIncomingRequest { + compiler_versions: supported_compilers.clone().solc_for_api(bytecode_kind), + ..test_request(Address::repeat_byte(1), COUNTER_CONTRACT) + }; + let address = Address::repeat_byte(1); + let output = match bytecode_kind { + BytecodeMarker::EraVm => { + let compiler = compiler_resolver + .resolve_zksolc(&supported_compilers.zksolc()) + .await + .unwrap(); + let input = ZkSolc::build_input(req.clone()).unwrap(); + compiler.compile(input).await.unwrap() + } + BytecodeMarker::Evm => { + let solc_version = &supported_compilers.solc; + let compiler = compiler_resolver.resolve_solc(solc_version).await.unwrap(); + let input = Solc::build_input(req.clone()).unwrap(); + compiler.compile(input).await.unwrap() + } + }; + + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + match bytecode_kind { + BytecodeMarker::EraVm => { + mock_deployment(&mut storage, address, output.bytecode.clone(), &[]).await; + } + BytecodeMarker::Evm => { + mock_evm_deployment( + &mut storage, + address, + output.bytecode.clone(), + output.deployed_bytecode(), + &[], + ) + .await; + } + } + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(compiler_resolver), + ) + .await + .unwrap(); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + assert_request_success(&mut storage, request_id, address, &output.bytecode).await; +} + +#[test_casing(2, BYTECODE_KINDS)] +#[tokio::test] +async fn compilation_errors(bytecode_kind: BytecodeMarker) { + let (compiler_resolver, supported_compilers) = real_resolver!(); + + let address = Address::repeat_byte(1); + let req = VerificationIncomingRequest { + compiler_versions: supported_compilers.solc_for_api(bytecode_kind), + source_code_data: SourceCodeData::SolSingleFile("contract ???".to_owned()), + ..test_request(Address::repeat_byte(1), COUNTER_CONTRACT) + }; + + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + match bytecode_kind { + BytecodeMarker::EraVm => { + mock_deployment(&mut storage, address, vec![0; 32], &[]).await; + } + BytecodeMarker::Evm => { + mock_evm_deployment(&mut storage, address, vec![3; 20], &[5; 10], &[]).await; + } + } + + let request_id = storage + .contract_verification_dal() + .add_contract_verification_request(&req) + .await + .unwrap(); + + let verifier = ContractVerifier::with_resolver( + Duration::from_secs(60), + pool.clone(), + Arc::new(compiler_resolver), + ) + .await + .unwrap(); + + let (_stop_sender, stop_receiver) = watch::channel(false); + verifier.run(stop_receiver, Some(1)).await.unwrap(); + + let status = storage + .contract_verification_dal() + .get_verification_request_status(request_id) + .await + .unwrap() + .expect("no status"); + assert_eq!(status.status, "failed"); + let compilation_errors = status.compilation_errors.unwrap(); + assert!(!compilation_errors.is_empty()); + let has_parser_error = compilation_errors + .iter() + .any(|err| err.contains("ParserError") && err.contains("Counter.sol")); + assert!(has_parser_error, "{compilation_errors:?}"); +} diff --git a/core/lib/contract_verifier/src/zksolc_utils.rs b/core/lib/contract_verifier/src/zksolc_utils.rs deleted file mode 100644 index 08004632bcec..000000000000 --- a/core/lib/contract_verifier/src/zksolc_utils.rs +++ /dev/null @@ -1,250 +0,0 @@ -use std::{collections::HashMap, io::Write, path::PathBuf, process::Stdio}; - -use semver::Version; -use serde::{Deserialize, Serialize}; - -use crate::error::ContractVerifierError; - -#[derive(Debug)] -pub enum ZkSolcInput { - StandardJson(StandardJson), - YulSingleFile { - source_code: String, - is_system: bool, - }, -} - -#[derive(Debug)] -pub enum ZkSolcOutput { - StandardJson(serde_json::Value), - YulSingleFile(String), -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct StandardJson { - /// The input language. - pub language: String, - /// The input source code files hashmap. - pub sources: HashMap, - /// The compiler settings. - pub settings: Settings, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Source { - /// The source code file content. - pub content: String, -} - -/// Compiler settings. -/// There are fields like `output_selection`, `is_system`, `force_evmla` which are accessed by contract verifier explicitly. -/// Other fields are accumulated in `other`, this way every field that was in the original request will be passed to a compiler. -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Settings { - /// The output selection filters. - pub output_selection: Option, - /// Flag for system compilation mode. - #[serde(default)] - pub is_system: bool, - /// Flag to force `evmla` IR. - #[serde(default)] - pub force_evmla: bool, - /// Other fields. - #[serde(flatten)] - pub other: serde_json::Value, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Optimizer { - /// Whether the optimizer is enabled. - pub enabled: bool, - /// The optimization mode string. - pub mode: Option, -} - -impl Default for Optimizer { - fn default() -> Self { - Self { - enabled: true, - mode: None, - } - } -} - -pub struct ZkSolc { - zksolc_path: PathBuf, - solc_path: PathBuf, - zksolc_version: String, -} - -impl ZkSolc { - pub fn new( - zksolc_path: impl Into, - solc_path: impl Into, - zksolc_version: String, - ) -> Self { - ZkSolc { - zksolc_path: zksolc_path.into(), - solc_path: solc_path.into(), - zksolc_version, - } - } - - pub async fn async_compile( - &self, - input: ZkSolcInput, - ) -> Result { - use tokio::io::AsyncWriteExt; - let mut command = tokio::process::Command::new(&self.zksolc_path); - command.stdout(Stdio::piped()).stderr(Stdio::piped()); - - match &input { - ZkSolcInput::StandardJson(input) => { - if !self.is_post_1_5_0() { - if input.settings.is_system { - command.arg("--system-mode"); - } - if input.settings.force_evmla { - command.arg("--force-evmla"); - } - } - - command.arg("--solc").arg(self.solc_path.to_str().unwrap()); - } - ZkSolcInput::YulSingleFile { is_system, .. } => { - if self.is_post_1_5_0() { - if *is_system { - command.arg("--enable-eravm-extensions"); - } else { - command.arg("--solc").arg(self.solc_path.to_str().unwrap()); - } - } else { - if *is_system { - command.arg("--system-mode"); - } - command.arg("--solc").arg(self.solc_path.to_str().unwrap()); - } - } - } - match input { - ZkSolcInput::StandardJson(input) => { - let mut child = command - .arg("--standard-json") - .stdin(Stdio::piped()) - .spawn() - .map_err(|_err| ContractVerifierError::InternalError)?; - let stdin = child.stdin.as_mut().unwrap(); - let content = serde_json::to_vec(&input).unwrap(); - stdin - .write_all(&content) - .await - .map_err(|_err| ContractVerifierError::InternalError)?; - stdin - .flush() - .await - .map_err(|_err| ContractVerifierError::InternalError)?; - - let output = child - .wait_with_output() - .await - .map_err(|_err| ContractVerifierError::InternalError)?; - if output.status.success() { - Ok(ZkSolcOutput::StandardJson( - serde_json::from_slice(&output.stdout) - .expect("Compiler output must be valid JSON"), - )) - } else { - Err(ContractVerifierError::CompilerError( - "zksolc".to_string(), - String::from_utf8_lossy(&output.stderr).to_string(), - )) - } - } - ZkSolcInput::YulSingleFile { source_code, .. } => { - let mut file = tempfile::Builder::new() - .prefix("input") - .suffix(".yul") - .rand_bytes(0) - .tempfile() - .map_err(|_err| ContractVerifierError::InternalError)?; - file.write_all(source_code.as_bytes()) - .map_err(|_err| ContractVerifierError::InternalError)?; - let child = command - .arg(file.path().to_str().unwrap()) - .arg("--optimization") - .arg("3") - .arg("--yul") - .arg("--bin") - .spawn() - .map_err(|_err| ContractVerifierError::InternalError)?; - let output = child - .wait_with_output() - .await - .map_err(|_err| ContractVerifierError::InternalError)?; - if output.status.success() { - Ok(ZkSolcOutput::YulSingleFile( - String::from_utf8(output.stdout).expect("Couldn't parse string"), - )) - } else { - Err(ContractVerifierError::CompilerError( - "zksolc".to_string(), - String::from_utf8_lossy(&output.stderr).to_string(), - )) - } - } - } - } - - pub fn is_post_1_5_0(&self) -> bool { - // Special case - if &self.zksolc_version == "vm-1.5.0-a167aa3" { - false - } else if let Some(version) = self.zksolc_version.strip_prefix("v") { - if let Ok(semver) = Version::parse(version) { - let target = Version::new(1, 5, 0); - semver >= target - } else { - true - } - } else { - true - } - } -} - -#[cfg(test)] -mod tests { - use crate::zksolc_utils::ZkSolc; - - #[test] - fn check_is_post_1_5_0() { - // Special case. - let mut zksolc = ZkSolc::new(".", ".", "vm-1.5.0-a167aa3".to_string()); - assert!(!zksolc.is_post_1_5_0(), "vm-1.5.0-a167aa3"); - - zksolc.zksolc_version = "v1.5.0".to_string(); - assert!(zksolc.is_post_1_5_0(), "v1.5.0"); - - zksolc.zksolc_version = "v1.5.1".to_string(); - assert!(zksolc.is_post_1_5_0(), "v1.5.1"); - - zksolc.zksolc_version = "v1.10.1".to_string(); - assert!(zksolc.is_post_1_5_0(), "v1.10.1"); - - zksolc.zksolc_version = "v2.0.0".to_string(); - assert!(zksolc.is_post_1_5_0(), "v2.0.0"); - - zksolc.zksolc_version = "v1.4.15".to_string(); - assert!(!zksolc.is_post_1_5_0(), "v1.4.15"); - - zksolc.zksolc_version = "v1.3.21".to_string(); - assert!(!zksolc.is_post_1_5_0(), "v1.3.21"); - - zksolc.zksolc_version = "v0.5.1".to_string(); - assert!(!zksolc.is_post_1_5_0(), "v0.5.1"); - } -} diff --git a/core/lib/contract_verifier/src/zkvyper_utils.rs b/core/lib/contract_verifier/src/zkvyper_utils.rs deleted file mode 100644 index c597f78d4588..000000000000 --- a/core/lib/contract_verifier/src/zkvyper_utils.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::{collections::HashMap, fs::File, io::Write, path::PathBuf, process::Stdio}; - -use crate::error::ContractVerifierError; - -#[derive(Debug)] -pub struct ZkVyperInput { - pub sources: HashMap, - pub optimizer_mode: Option, -} - -pub struct ZkVyper { - zkvyper_path: PathBuf, - vyper_path: PathBuf, -} - -impl ZkVyper { - pub fn new(zkvyper_path: impl Into, vyper_path: impl Into) -> Self { - ZkVyper { - zkvyper_path: zkvyper_path.into(), - vyper_path: vyper_path.into(), - } - } - - pub async fn async_compile( - &self, - input: ZkVyperInput, - ) -> Result { - let mut command = tokio::process::Command::new(&self.zkvyper_path); - if let Some(o) = input.optimizer_mode.as_ref() { - command.arg("-O").arg(o); - } - command - .arg("--vyper") - .arg(self.vyper_path.to_str().unwrap()) - .arg("-f") - .arg("combined_json") - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - - let temp_dir = tempfile::tempdir().map_err(|_err| ContractVerifierError::InternalError)?; - for (mut name, content) in input.sources { - if !name.ends_with(".vy") { - name += ".vy"; - } - let path = temp_dir.path().join(name); - if let Some(prefix) = path.parent() { - std::fs::create_dir_all(prefix) - .map_err(|_err| ContractVerifierError::InternalError)?; - } - let mut file = - File::create(&path).map_err(|_err| ContractVerifierError::InternalError)?; - file.write_all(content.as_bytes()) - .map_err(|_err| ContractVerifierError::InternalError)?; - command.arg(path.into_os_string()); - } - - let child = command - .spawn() - .map_err(|_err| ContractVerifierError::InternalError)?; - let output = child - .wait_with_output() - .await - .map_err(|_err| ContractVerifierError::InternalError)?; - if output.status.success() { - Ok(serde_json::from_slice(&output.stdout).expect("Compiler output must be valid JSON")) - } else { - Err(ContractVerifierError::CompilerError( - "zkvyper".to_string(), - String::from_utf8_lossy(&output.stderr).to_string(), - )) - } - } -} diff --git a/core/lib/dal/.sqlx/query-0429f2fa683bdff6fc1ff5069de69d57dbfda4be1f70232afffca82a895d43e0.json b/core/lib/dal/.sqlx/query-12c062c6a5078ebcbde378126a3773e86be9876cd198610e0792322e2a0797af.json similarity index 50% rename from core/lib/dal/.sqlx/query-0429f2fa683bdff6fc1ff5069de69d57dbfda4be1f70232afffca82a895d43e0.json rename to core/lib/dal/.sqlx/query-12c062c6a5078ebcbde378126a3773e86be9876cd198610e0792322e2a0797af.json index 5693bdf987e5..0027377ae596 100644 --- a/core/lib/dal/.sqlx/query-0429f2fa683bdff6fc1ff5069de69d57dbfda4be1f70232afffca82a895d43e0.json +++ b/core/lib/dal/.sqlx/query-12c062c6a5078ebcbde378126a3773e86be9876cd198610e0792322e2a0797af.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n WITH\n sl AS (\n SELECT\n (\n SELECT\n ARRAY[hashed_key, value] AS kv\n FROM\n storage_logs\n WHERE\n storage_logs.miniblock_number = $1\n AND storage_logs.hashed_key >= u.start_key\n AND storage_logs.hashed_key <= u.end_key\n ORDER BY\n storage_logs.hashed_key\n LIMIT\n 1\n )\n FROM\n UNNEST($2::bytea [], $3::bytea []) AS u (start_key, end_key)\n )\n \n SELECT\n sl.kv[1] AS \"hashed_key?\",\n sl.kv[2] AS \"value?\",\n initial_writes.index\n FROM\n sl\n LEFT OUTER JOIN initial_writes ON initial_writes.hashed_key = sl.kv[1]\n ", + "query": "\n WITH\n sl AS (\n SELECT\n (\n SELECT\n ARRAY[hashed_key, value] AS kv\n FROM\n storage_logs\n WHERE\n storage_logs.miniblock_number <= $1\n AND storage_logs.hashed_key >= u.start_key\n AND storage_logs.hashed_key <= u.end_key\n ORDER BY\n storage_logs.hashed_key\n LIMIT\n 1\n )\n FROM\n UNNEST($2::bytea [], $3::bytea []) AS u (start_key, end_key)\n )\n \n SELECT\n sl.kv[1] AS \"hashed_key?\",\n sl.kv[2] AS \"value?\",\n initial_writes.index\n FROM\n sl\n LEFT OUTER JOIN initial_writes ON initial_writes.hashed_key = sl.kv[1]\n ", "describe": { "columns": [ { @@ -32,5 +32,5 @@ true ] }, - "hash": "0429f2fa683bdff6fc1ff5069de69d57dbfda4be1f70232afffca82a895d43e0" + "hash": "12c062c6a5078ebcbde378126a3773e86be9876cd198610e0792322e2a0797af" } diff --git a/core/lib/dal/.sqlx/query-1689c212d411ebd99a22210519ea2d505a1aabf52ff4136d2ed1b39c70dd1632.json b/core/lib/dal/.sqlx/query-1689c212d411ebd99a22210519ea2d505a1aabf52ff4136d2ed1b39c70dd1632.json index 7b939d137db9..b84cd1bcba89 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-442212bb5f28f234cd624f2acc27944b2acedce201da4454aadb79f3545713ae.json b/core/lib/dal/.sqlx/query-2ae0541e9af1a9966585a25dfe772cb2ea9f2209fe2c12dda6c72c96bdb496d3.json similarity index 72% rename from core/lib/dal/.sqlx/query-442212bb5f28f234cd624f2acc27944b2acedce201da4454aadb79f3545713ae.json rename to core/lib/dal/.sqlx/query-2ae0541e9af1a9966585a25dfe772cb2ea9f2209fe2c12dda6c72c96bdb496d3.json index 621295d4ab81..b706a9df4373 100644 --- a/core/lib/dal/.sqlx/query-442212bb5f28f234cd624f2acc27944b2acedce201da4454aadb79f3545713ae.json +++ b/core/lib/dal/.sqlx/query-2ae0541e9af1a9966585a25dfe772cb2ea9f2209fe2c12dda6c72c96bdb496d3.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n storage_logs.hashed_key,\n storage_logs.value,\n initial_writes.index\n FROM\n storage_logs\n INNER JOIN initial_writes ON storage_logs.hashed_key = initial_writes.hashed_key\n WHERE\n storage_logs.miniblock_number = $1\n AND storage_logs.hashed_key >= $2::bytea\n AND storage_logs.hashed_key <= $3::bytea\n ORDER BY\n storage_logs.hashed_key\n ", + "query": "\n SELECT\n storage_logs.hashed_key,\n storage_logs.value,\n initial_writes.index\n FROM\n storage_logs\n INNER JOIN initial_writes ON storage_logs.hashed_key = initial_writes.hashed_key\n WHERE\n storage_logs.miniblock_number <= $1\n AND storage_logs.hashed_key >= $2::bytea\n AND storage_logs.hashed_key <= $3::bytea\n ORDER BY\n storage_logs.hashed_key\n ", "describe": { "columns": [ { @@ -32,5 +32,5 @@ false ] }, - "hash": "442212bb5f28f234cd624f2acc27944b2acedce201da4454aadb79f3545713ae" + "hash": "2ae0541e9af1a9966585a25dfe772cb2ea9f2209fe2c12dda6c72c96bdb496d3" } diff --git a/core/lib/dal/.sqlx/query-2fa2ba4a62f79d780d239409d426b602aa0cf9b0c5b1ef39b7d07d6309454fcd.json b/core/lib/dal/.sqlx/query-2fa2ba4a62f79d780d239409d426b602aa0cf9b0c5b1ef39b7d07d6309454fcd.json index 1d515edba819..0db6ba6f51b6 100644 --- a/core/lib/dal/.sqlx/query-2fa2ba4a62f79d780d239409d426b602aa0cf9b0c5b1ef39b7d07d6309454fcd.json +++ b/core/lib/dal/.sqlx/query-2fa2ba4a62f79d780d239409d426b602aa0cf9b0c5b1ef39b7d07d6309454fcd.json @@ -69,7 +69,7 @@ false, false, false, - false, + true, false, false, true, diff --git a/core/lib/dal/.sqlx/query-40a7a619ed490eb3848a45fdde787f3b5d4ba906ec9a38e10912e3c7832fc93e.json b/core/lib/dal/.sqlx/query-40a7a619ed490eb3848a45fdde787f3b5d4ba906ec9a38e10912e3c7832fc93e.json new file mode 100644 index 000000000000..1b68326b8962 --- /dev/null +++ b/core/lib/dal/.sqlx/query-40a7a619ed490eb3848a45fdde787f3b5d4ba906ec9a38e10912e3c7832fc93e.json @@ -0,0 +1,42 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n factory_deps.bytecode_hash,\n factory_deps.bytecode,\n transactions.data -> 'calldata' AS \"calldata?\",\n transactions.contract_address AS \"contract_address?\"\n FROM\n (\n SELECT\n miniblock_number,\n tx_hash,\n topic3\n FROM\n events\n WHERE\n address = $1\n AND topic1 = $2\n AND topic4 = $3\n LIMIT\n 1\n ) deploy_event\n JOIN factory_deps ON factory_deps.bytecode_hash = deploy_event.topic3\n LEFT JOIN transactions ON transactions.hash = deploy_event.tx_hash\n WHERE\n deploy_event.miniblock_number <= (\n SELECT\n MAX(number)\n FROM\n miniblocks\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "bytecode_hash", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "bytecode", + "type_info": "Bytea" + }, + { + "ordinal": 2, + "name": "calldata?", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "contract_address?", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Bytea", + "Bytea", + "Bytea" + ] + }, + "nullable": [ + false, + false, + null, + true + ] + }, + "hash": "40a7a619ed490eb3848a45fdde787f3b5d4ba906ec9a38e10912e3c7832fc93e" +} diff --git a/core/lib/dal/.sqlx/query-72a4f50355324cce85ebaef9fa32826095e9290f0c1157094bd0c44e06012e42.json b/core/lib/dal/.sqlx/query-72a4f50355324cce85ebaef9fa32826095e9290f0c1157094bd0c44e06012e42.json index 707b7ce9e75c..2f4203aaa326 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-8ab1634beba74aaef952562a3bcc84b0dd496700a61569929dcc7602ec678b09.json b/core/lib/dal/.sqlx/query-8ab1634beba74aaef952562a3bcc84b0dd496700a61569929dcc7602ec678b09.json deleted file mode 100644 index 5869c1d37a04..000000000000 --- a/core/lib/dal/.sqlx/query-8ab1634beba74aaef952562a3bcc84b0dd496700a61569929dcc7602ec678b09.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n factory_deps.bytecode,\n transactions.data AS \"data?\",\n transactions.contract_address AS \"contract_address?\"\n FROM\n (\n SELECT\n miniblock_number,\n tx_hash,\n topic3\n FROM\n events\n WHERE\n address = $1\n AND topic1 = $2\n AND topic4 = $3\n LIMIT\n 1\n ) deploy_event\n JOIN factory_deps ON factory_deps.bytecode_hash = deploy_event.topic3\n LEFT JOIN transactions ON transactions.hash = deploy_event.tx_hash\n WHERE\n deploy_event.miniblock_number <= (\n SELECT\n MAX(number)\n FROM\n miniblocks\n )\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "bytecode", - "type_info": "Bytea" - }, - { - "ordinal": 1, - "name": "data?", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "contract_address?", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [ - "Bytea", - "Bytea", - "Bytea" - ] - }, - "nullable": [ - false, - true, - true - ] - }, - "hash": "8ab1634beba74aaef952562a3bcc84b0dd496700a61569929dcc7602ec678b09" -} diff --git a/core/lib/dal/.sqlx/query-a115f795672787fe25bfaa8fd345094de508af93f4085be7cf3b54b1e8ecdadd.json b/core/lib/dal/.sqlx/query-a115f795672787fe25bfaa8fd345094de508af93f4085be7cf3b54b1e8ecdadd.json index ebe8ce232cfb..ac7989a5be77 100644 --- a/core/lib/dal/.sqlx/query-a115f795672787fe25bfaa8fd345094de508af93f4085be7cf3b54b1e8ecdadd.json +++ b/core/lib/dal/.sqlx/query-a115f795672787fe25bfaa8fd345094de508af93f4085be7cf3b54b1e8ecdadd.json @@ -67,7 +67,7 @@ false, false, false, - false, + true, false, false, true, diff --git a/core/lib/dal/.sqlx/query-a36135b5908992324c4308f549ea77a428820fdcea9969aff3b29ca16727357b.json b/core/lib/dal/.sqlx/query-a36135b5908992324c4308f549ea77a428820fdcea9969aff3b29ca16727357b.json index 1d27af2bbc1a..50d3ce5188df 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 000000000000..2edb0822ac69 --- /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 c234cbe42356..000000000000 --- 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 2419082dcc23..079ce55bd569 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 2cd001b274da..8c43f8865ac7 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 000000000000..87f6a8cb75a0 --- /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 000000000000..103a22cb8e3c --- /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/migrations/20241106093512_make_zk_compiler_version_nullable.down.sql b/core/lib/dal/migrations/20241106093512_make_zk_compiler_version_nullable.down.sql new file mode 100644 index 000000000000..2693a565fd02 --- /dev/null +++ b/core/lib/dal/migrations/20241106093512_make_zk_compiler_version_nullable.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE contract_verification_requests + ALTER COLUMN zk_compiler_version SET NOT NULL; diff --git a/core/lib/dal/migrations/20241106093512_make_zk_compiler_version_nullable.up.sql b/core/lib/dal/migrations/20241106093512_make_zk_compiler_version_nullable.up.sql new file mode 100644 index 000000000000..92a689956f55 --- /dev/null +++ b/core/lib/dal/migrations/20241106093512_make_zk_compiler_version_nullable.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE contract_verification_requests + ALTER COLUMN zk_compiler_version DROP NOT NULL; diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 4cb577986380..ba843bbf92f3 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/contract_verification_dal.rs b/core/lib/dal/src/contract_verification_dal.rs index 291e60a50d90..93a4ce2fd35a 100644 --- a/core/lib/dal/src/contract_verification_dal.rs +++ b/core/lib/dal/src/contract_verification_dal.rs @@ -1,28 +1,25 @@ #![doc = include_str!("../doc/ContractVerificationDal.md")] + use std::{ fmt::{Display, Formatter}, time::Duration, }; -use anyhow::Context as _; use sqlx::postgres::types::PgInterval; -use zksync_db_connection::connection::Connection; +use zksync_db_connection::{error::SqlxContext, instrument::InstrumentExt}; use zksync_types::{ contract_verification_api::{ - DeployContractCalldata, VerificationIncomingRequest, VerificationInfo, VerificationRequest, + VerificationIncomingRequest, VerificationInfo, VerificationRequest, VerificationRequestStatus, }, - Address, CONTRACT_DEPLOYER_ADDRESS, + web3, Address, CONTRACT_DEPLOYER_ADDRESS, H256, }; use zksync_utils::address_to_h256; use zksync_vm_interface::VmEvent; -use crate::{models::storage_verification_request::StorageVerificationRequest, Core}; - -#[derive(Debug)] -pub struct ContractVerificationDal<'a, 'c> { - pub(crate) storage: &'a mut Connection<'c, Core>, -} +use crate::{ + models::storage_verification_request::StorageVerificationRequest, Connection, Core, DalResult, +}; #[derive(Debug)] enum Compiler { @@ -43,8 +40,24 @@ impl Display for Compiler { } } +#[derive(Debug)] +pub struct DeployedContractData { + pub bytecode_hash: H256, + /// Bytecode as persisted in Postgres (i.e., with additional padding for EVM bytecodes). + pub bytecode: Vec, + /// Recipient of the deployment transaction. + pub contract_address: Option
, + /// Call data for the deployment transaction. + pub calldata: Option>, +} + +#[derive(Debug)] +pub struct ContractVerificationDal<'a, 'c> { + pub(crate) storage: &'a mut Connection<'c, Core>, +} + impl ContractVerificationDal<'_, '_> { - pub async fn get_count_of_queued_verification_requests(&mut self) -> sqlx::Result { + pub async fn get_count_of_queued_verification_requests(&mut self) -> DalResult { sqlx::query!( r#" SELECT @@ -55,15 +68,16 @@ impl ContractVerificationDal<'_, '_> { status = 'queued' "# ) - .fetch_one(self.storage.conn()) + .instrument("get_count_of_queued_verification_requests") + .fetch_one(self.storage) .await .map(|row| row.count as usize) } pub async fn add_contract_verification_request( &mut self, - query: VerificationIncomingRequest, - ) -> sqlx::Result { + query: &VerificationIncomingRequest, + ) -> DalResult { sqlx::query!( r#" INSERT INTO @@ -90,16 +104,18 @@ impl ContractVerificationDal<'_, '_> { query.contract_address.as_bytes(), // Serialization should always succeed. serde_json::to_string(&query.source_code_data).unwrap(), - query.contract_name, + &query.contract_name, query.compiler_versions.zk_compiler_version(), query.compiler_versions.compiler_version(), query.optimization_used, - query.optimizer_mode, - query.constructor_arguments.0, + query.optimizer_mode.as_deref(), + query.constructor_arguments.0.as_slice(), query.is_system, query.force_evmla, ) - .fetch_one(self.storage.conn()) + .instrument("add_contract_verification_request") + .with_arg("address", &query.contract_address) + .fetch_one(self.storage) .await .map(|row| row.id as usize) } @@ -111,7 +127,7 @@ impl ContractVerificationDal<'_, '_> { pub async fn get_next_queued_verification_request( &mut self, processing_timeout: Duration, - ) -> sqlx::Result> { + ) -> DalResult> { let processing_timeout = PgInterval { months: 0, days: 0, @@ -160,7 +176,9 @@ impl ContractVerificationDal<'_, '_> { "#, &processing_timeout ) - .fetch_optional(self.storage.conn()) + .instrument("get_next_queued_verification_request") + .with_arg("processing_timeout", &processing_timeout) + .fetch_optional(self.storage) .await? .map(Into::into); Ok(result) @@ -170,12 +188,10 @@ impl ContractVerificationDal<'_, '_> { pub async fn save_verification_info( &mut self, verification_info: VerificationInfo, - ) -> anyhow::Result<()> { - let mut transaction = self - .storage - .start_transaction() - .await - .context("start_transaction()")?; + ) -> DalResult<()> { + let mut transaction = self.storage.start_transaction().await?; + let id = verification_info.request.id; + let address = verification_info.request.req.contract_address; sqlx::query!( r#" @@ -188,10 +204,12 @@ impl ContractVerificationDal<'_, '_> { "#, verification_info.request.id as i64, ) - .execute(transaction.conn()) + .instrument("save_verification_info#set_status") + .with_arg("id", &id) + .with_arg("address", &address) + .execute(&mut transaction) .await?; - let address = verification_info.request.req.contract_address; // Serialization should always succeed. let verification_info_json = serde_json::to_value(verification_info) .expect("Failed to serialize verification info into serde_json"); @@ -209,20 +227,22 @@ impl ContractVerificationDal<'_, '_> { address.as_bytes(), &verification_info_json ) - .execute(transaction.conn()) + .instrument("save_verification_info#insert") + .with_arg("id", &id) + .with_arg("address", &address) + .execute(&mut transaction) .await?; - transaction.commit().await.context("commit()")?; - Ok(()) + transaction.commit().await } pub async fn save_verification_error( &mut self, id: usize, - error: String, - compilation_errors: serde_json::Value, - panic_message: Option, - ) -> sqlx::Result<()> { + error: &str, + compilation_errors: &serde_json::Value, + panic_message: Option<&str>, + ) -> DalResult<()> { sqlx::query!( r#" UPDATE contract_verification_requests @@ -236,11 +256,14 @@ impl ContractVerificationDal<'_, '_> { id = $1 "#, id as i64, - error.as_str(), - &compilation_errors, + error, + compilation_errors, panic_message ) - .execute(self.storage.conn()) + .instrument("save_verification_error") + .with_arg("id", &id) + .with_arg("error", &error) + .execute(self.storage) .await?; Ok(()) } @@ -248,8 +271,8 @@ impl ContractVerificationDal<'_, '_> { pub async fn get_verification_request_status( &mut self, id: usize, - ) -> anyhow::Result> { - let Some(row) = sqlx::query!( + ) -> DalResult> { + sqlx::query!( r#" SELECT status, @@ -262,41 +285,46 @@ impl ContractVerificationDal<'_, '_> { "#, id as i64, ) - .fetch_optional(self.storage.conn()) - .await? - else { - return Ok(None); - }; - - let mut compilation_errors = vec![]; - if let Some(errors) = row.compilation_errors { - for value in errors.as_array().context("expected an array")? { - compilation_errors.push(value.as_str().context("expected string")?.to_string()); + .try_map(|row| { + let mut compilation_errors = vec![]; + if let Some(errors) = row.compilation_errors { + let serde_json::Value::Array(errors) = errors else { + return Err(anyhow::anyhow!("errors are not an array")) + .decode_column("compilation_errors")?; + }; + for value in errors { + let serde_json::Value::String(err) = value else { + return Err(anyhow::anyhow!("error is not a string")) + .decode_column("compilation_errors")?; + }; + compilation_errors.push(err.to_owned()); + } } - } - Ok(Some(VerificationRequestStatus { - status: row.status, - error: row.error, - compilation_errors: if compilation_errors.is_empty() { - None - } else { - Some(compilation_errors) - }, - })) + + Ok(VerificationRequestStatus { + status: row.status, + error: row.error, + compilation_errors: (!compilation_errors.is_empty()).then_some(compilation_errors), + }) + }) + .instrument("get_verification_request_status") + .with_arg("id", &id) + .fetch_optional(self.storage) + .await } /// Returns bytecode and calldata from the contract and the transaction that created it. pub async fn get_contract_info_for_verification( &mut self, address: Address, - ) -> anyhow::Result, DeployContractCalldata)>> { + ) -> DalResult> { let address_h256 = address_to_h256(&address); - - let Some(row) = sqlx::query!( + sqlx::query!( r#" SELECT + factory_deps.bytecode_hash, factory_deps.bytecode, - transactions.data AS "data?", + transactions.data -> 'calldata' AS "calldata?", transactions.contract_address AS "contract_address?" FROM ( @@ -327,30 +355,29 @@ impl ContractVerificationDal<'_, '_> { VmEvent::DEPLOY_EVENT_SIGNATURE.as_bytes(), address_h256.as_bytes(), ) - .fetch_optional(self.storage.conn()) - .await? - else { - return Ok(None); - }; - let calldata = match row.contract_address { - Some(contract_address) if contract_address == CONTRACT_DEPLOYER_ADDRESS.0.to_vec() => { - // `row.contract_address` and `row.data` are either both `None` or both `Some(_)`. - // In this arm it's checked that `row.contract_address` is `Some(_)`, so it's safe to unwrap `row.data`. - let data: serde_json::Value = row.data.context("data missing")?; - let calldata_str: String = serde_json::from_value( - data.get("calldata").context("calldata missing")?.clone(), - ) - .context("failed parsing calldata")?; - let calldata = hex::decode(&calldata_str[2..]).context("invalid calldata")?; - DeployContractCalldata::Deploy(calldata) - } - _ => DeployContractCalldata::Ignore, - }; - Ok(Some((row.bytecode, calldata))) + .try_map(|row| { + Ok(DeployedContractData { + bytecode_hash: H256::from_slice(&row.bytecode_hash), + bytecode: row.bytecode, + contract_address: row.contract_address.as_deref().map(Address::from_slice), + calldata: row + .calldata + .map(|calldata| { + serde_json::from_value::(calldata) + .decode_column("calldata") + .map(|bytes| bytes.0) + }) + .transpose()?, + }) + }) + .instrument("get_contract_info_for_verification") + .with_arg("address", &address) + .fetch_optional(self.storage) + .await } /// Returns true if the contract has a stored contracts_verification_info. - pub async fn is_contract_verified(&mut self, address: Address) -> sqlx::Result { + pub async fn is_contract_verified(&mut self, address: Address) -> DalResult { let count = sqlx::query!( r#" SELECT @@ -362,13 +389,15 @@ impl ContractVerificationDal<'_, '_> { "#, address.as_bytes() ) - .fetch_one(self.storage.conn()) + .instrument("is_contract_verified") + .with_arg("address", &address) + .fetch_one(self.storage) .await? .count; Ok(count > 0) } - async fn get_compiler_versions(&mut self, compiler: Compiler) -> sqlx::Result> { + async fn get_compiler_versions(&mut self, compiler: Compiler) -> DalResult> { let compiler = format!("{compiler}"); let versions: Vec<_> = sqlx::query!( r#" @@ -383,7 +412,9 @@ impl ContractVerificationDal<'_, '_> { "#, &compiler ) - .fetch_all(self.storage.conn()) + .instrument("get_compiler_versions") + .with_arg("compiler", &compiler) + .fetch_all(self.storage) .await? .into_iter() .map(|row| row.version) @@ -391,32 +422,28 @@ impl ContractVerificationDal<'_, '_> { Ok(versions) } - pub async fn get_zksolc_versions(&mut self) -> sqlx::Result> { + pub async fn get_zksolc_versions(&mut self) -> DalResult> { self.get_compiler_versions(Compiler::ZkSolc).await } - pub async fn get_solc_versions(&mut self) -> sqlx::Result> { + pub async fn get_solc_versions(&mut self) -> DalResult> { self.get_compiler_versions(Compiler::Solc).await } - pub async fn get_zkvyper_versions(&mut self) -> sqlx::Result> { + pub async fn get_zkvyper_versions(&mut self) -> DalResult> { self.get_compiler_versions(Compiler::ZkVyper).await } - pub async fn get_vyper_versions(&mut self) -> sqlx::Result> { + pub async fn get_vyper_versions(&mut self) -> DalResult> { self.get_compiler_versions(Compiler::Vyper).await } async fn set_compiler_versions( &mut self, compiler: Compiler, - versions: Vec, - ) -> anyhow::Result<()> { - let mut transaction = self - .storage - .start_transaction() - .await - .context("start_transaction")?; + versions: &[String], + ) -> DalResult<()> { + let mut transaction = self.storage.start_transaction().await?; let compiler = format!("{compiler}"); sqlx::query!( @@ -427,7 +454,9 @@ impl ContractVerificationDal<'_, '_> { "#, &compiler ) - .execute(transaction.conn()) + .instrument("set_compiler_versions#delete") + .with_arg("compiler", &compiler) + .execute(&mut transaction) .await?; sqlx::query!( @@ -443,34 +472,36 @@ impl ContractVerificationDal<'_, '_> { UNNEST($1::TEXT []) AS u (version) ON CONFLICT (version, compiler) DO NOTHING "#, - &versions, + versions, &compiler, ) - .execute(transaction.conn()) + .instrument("set_compiler_versions#insert") + .with_arg("compiler", &compiler) + .with_arg("versions.len", &versions.len()) + .execute(&mut transaction) .await?; - transaction.commit().await.context("commit()")?; - Ok(()) + transaction.commit().await } - pub async fn set_zksolc_versions(&mut self, versions: Vec) -> anyhow::Result<()> { + pub async fn set_zksolc_versions(&mut self, versions: &[String]) -> DalResult<()> { self.set_compiler_versions(Compiler::ZkSolc, versions).await } - pub async fn set_solc_versions(&mut self, versions: Vec) -> anyhow::Result<()> { + pub async fn set_solc_versions(&mut self, versions: &[String]) -> DalResult<()> { self.set_compiler_versions(Compiler::Solc, versions).await } - pub async fn set_zkvyper_versions(&mut self, versions: Vec) -> anyhow::Result<()> { + pub async fn set_zkvyper_versions(&mut self, versions: &[String]) -> DalResult<()> { self.set_compiler_versions(Compiler::ZkVyper, versions) .await } - pub async fn set_vyper_versions(&mut self, versions: Vec) -> anyhow::Result<()> { + pub async fn set_vyper_versions(&mut self, versions: &[String]) -> DalResult<()> { self.set_compiler_versions(Compiler::Vyper, versions).await } - pub async fn get_all_successful_requests(&mut self) -> sqlx::Result> { + pub async fn get_all_successful_requests(&mut self) -> DalResult> { let result = sqlx::query_as!( StorageVerificationRequest, r#" @@ -494,7 +525,8 @@ impl ContractVerificationDal<'_, '_> { id "#, ) - .fetch_all(self.storage.conn()) + .instrument("get_all_successful_requests") + .fetch_all(self.storage) .await? .into_iter() .map(Into::into) @@ -505,8 +537,8 @@ impl ContractVerificationDal<'_, '_> { pub async fn get_contract_verification_info( &mut self, address: Address, - ) -> anyhow::Result> { - let Some(row) = sqlx::query!( + ) -> DalResult> { + Ok(sqlx::query!( r#" SELECT verification_info @@ -517,14 +549,164 @@ impl ContractVerificationDal<'_, '_> { "#, address.as_bytes(), ) - .fetch_optional(self.storage.conn()) + .try_map(|row| { + row.verification_info + .map(|info| serde_json::from_value(info).decode_column("verification_info")) + .transpose() + }) + .instrument("get_contract_verification_info") + .with_arg("address", &address) + .fetch_optional(self.storage) .await? - else { - return Ok(None); + .flatten()) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use zksync_types::{ + contract_verification_api::{CompilerVersions, SourceCodeData}, + tx::IncludedTxLocation, + Execute, L1BatchNumber, L2BlockNumber, ProtocolVersion, + }; + use zksync_utils::bytecode::hash_bytecode; + use zksync_vm_interface::{tracer::ValidationTraces, TransactionExecutionMetrics}; + + use super::*; + use crate::{ + tests::{create_l2_block_header, mock_l2_transaction}, + ConnectionPool, CoreDal, + }; + + #[tokio::test] + async fn getting_contract_info_for_verification() { + let pool = ConnectionPool::::test_pool().await; + let mut conn = pool.connection().await.unwrap(); + + conn.protocol_versions_dal() + .save_protocol_version_with_tx(&ProtocolVersion::default()) + .await + .unwrap(); + conn.blocks_dal() + .insert_l2_block(&create_l2_block_header(0)) + .await + .unwrap(); + + // Add a transaction, its bytecode and the bytecode deployment event. + let deployed_address = Address::repeat_byte(12); + let mut tx = mock_l2_transaction(); + let bytecode = vec![1; 32]; + let bytecode_hash = hash_bytecode(&bytecode); + tx.execute = Execute::for_deploy(H256::zero(), bytecode.clone(), &[]); + conn.transactions_dal() + .insert_transaction_l2( + &tx, + TransactionExecutionMetrics::default(), + ValidationTraces::default(), + ) + .await + .unwrap(); + conn.factory_deps_dal() + .insert_factory_deps( + L2BlockNumber(0), + &HashMap::from([(bytecode_hash, bytecode.clone())]), + ) + .await + .unwrap(); + let location = IncludedTxLocation { + tx_hash: tx.hash(), + tx_index_in_l2_block: 0, + tx_initiator_address: tx.initiator_account(), }; - let Some(info) = row.verification_info else { - return Ok(None); + let deploy_event = VmEvent { + location: (L1BatchNumber(0), 0), + address: CONTRACT_DEPLOYER_ADDRESS, + indexed_topics: vec![ + VmEvent::DEPLOY_EVENT_SIGNATURE, + address_to_h256(&tx.initiator_account()), + bytecode_hash, + address_to_h256(&deployed_address), + ], + value: vec![], }; - Ok(Some(serde_json::from_value(info).context("invalid info")?)) + conn.events_dal() + .save_events(L2BlockNumber(0), &[(location, vec![&deploy_event])]) + .await + .unwrap(); + + let contract = conn + .contract_verification_dal() + .get_contract_info_for_verification(deployed_address) + .await + .unwrap() + .expect("no info"); + assert_eq!(contract.bytecode_hash, bytecode_hash); + assert_eq!(contract.bytecode, bytecode); + assert_eq!(contract.contract_address, Some(CONTRACT_DEPLOYER_ADDRESS)); + assert_eq!(contract.calldata.unwrap(), tx.execute.calldata); + } + + async fn test_working_with_verification_requests(zksolc: Option<&str>) { + let request = VerificationIncomingRequest { + contract_address: Address::repeat_byte(11), + source_code_data: SourceCodeData::SolSingleFile("contract Test {}".to_owned()), + contract_name: "Test".to_string(), + compiler_versions: CompilerVersions::Solc { + compiler_zksolc_version: zksolc.map(str::to_owned), + compiler_solc_version: "0.8.27".to_owned(), + }, + optimization_used: true, + optimizer_mode: Some("z".to_owned()), + constructor_arguments: web3::Bytes(b"test".to_vec()), + is_system: false, + force_evmla: true, + }; + + let pool = ConnectionPool::::test_pool().await; + let mut conn = pool.connection().await.unwrap(); + let id = conn + .contract_verification_dal() + .add_contract_verification_request(&request) + .await + .unwrap(); + + let status = conn + .contract_verification_dal() + .get_verification_request_status(id) + .await + .unwrap() + .expect("request not persisted"); + assert_eq!(status.status, "queued"); + + let req = conn + .contract_verification_dal() + .get_next_queued_verification_request(Duration::from_secs(600)) + .await + .unwrap() + .expect("request not queued"); + assert_eq!(req.id, id); + assert_eq!(req.req.contract_address, request.contract_address); + assert_eq!(req.req.contract_name, request.contract_name); + assert_eq!(req.req.compiler_versions, request.compiler_versions); + assert_eq!(req.req.optimization_used, request.optimization_used); + assert_eq!(req.req.optimizer_mode, request.optimizer_mode); + assert_eq!(req.req.constructor_arguments, request.constructor_arguments); + assert_eq!(req.req.is_system, request.is_system); + assert_eq!(req.req.force_evmla, request.force_evmla); + + let maybe_req = conn + .contract_verification_dal() + .get_next_queued_verification_request(Duration::from_secs(600)) + .await + .unwrap(); + assert!(maybe_req.is_none()); + } + + #[tokio::test] + async fn working_with_verification_requests() { + test_working_with_verification_requests(None).await; + test_working_with_verification_requests(Some("1.5.7")).await; } } diff --git a/core/lib/dal/src/models/storage_transaction.rs b/core/lib/dal/src/models/storage_transaction.rs index dbd4fa947520..459a3ec0c0fb 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, diff --git a/core/lib/dal/src/models/storage_verification_request.rs b/core/lib/dal/src/models/storage_verification_request.rs index 61895fab76d3..ae4718e41290 100644 --- a/core/lib/dal/src/models/storage_verification_request.rs +++ b/core/lib/dal/src/models/storage_verification_request.rs @@ -12,7 +12,7 @@ pub struct StorageVerificationRequest { pub contract_address: Vec, pub source_code: String, pub contract_name: String, - pub zk_compiler_version: String, + pub zk_compiler_version: Option, pub compiler_version: String, pub optimization_used: bool, pub optimizer_mode: Option, diff --git a/core/lib/dal/src/pruning_dal/tests.rs b/core/lib/dal/src/pruning_dal/tests.rs index 4f94ff7f63d3..70dda48d8c82 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/storage_logs_dal.rs b/core/lib/dal/src/storage_logs_dal.rs index adad6eb7e1db..1675d76643c2 100644 --- a/core/lib/dal/src/storage_logs_dal.rs +++ b/core/lib/dal/src/storage_logs_dal.rs @@ -225,60 +225,13 @@ impl StorageLogsDal<'_, '_> { Ok(()) } - pub async fn is_contract_deployed_at_address(&mut self, address: Address) -> bool { - let hashed_key = get_code_key(&address).hashed_key(); - let row = sqlx::query!( - r#" - SELECT - COUNT(*) AS "count!" - FROM - ( - SELECT - * - FROM - storage_logs - WHERE - hashed_key = $1 - AND miniblock_number <= COALESCE( - ( - SELECT - MAX(number) - FROM - miniblocks - ), - ( - SELECT - miniblock_number - FROM - snapshot_recovery - ) - ) - ORDER BY - miniblock_number DESC, - operation_number DESC - LIMIT - 1 - ) sl - WHERE - sl.value != $2 - "#, - hashed_key.as_bytes(), - FAILED_CONTRACT_DEPLOYMENT_BYTECODE_HASH.as_bytes(), - ) - .fetch_one(self.storage.conn()) - .await - .unwrap(); - - row.count > 0 - } - /// Returns addresses and the corresponding deployment L2 block numbers among the specified contract /// `addresses`. `at_l2_block` allows filtering deployment by L2 blocks. pub async fn filter_deployed_contracts( &mut self, addresses: impl Iterator, at_l2_block: Option, - ) -> DalResult> { + ) -> DalResult> { let (bytecode_hashed_keys, address_by_hashed_key): (Vec<_>, HashMap<_, _>) = addresses .map(|address| { let hashed_key = get_code_key(&address).hashed_key().0; @@ -330,12 +283,13 @@ impl StorageLogsDal<'_, '_> { .await?; let deployment_data = rows.into_iter().filter_map(|row| { - if row.value == FAILED_CONTRACT_DEPLOYMENT_BYTECODE_HASH.as_bytes() { + let bytecode_hash = H256::from_slice(&row.value); + if bytecode_hash == FAILED_CONTRACT_DEPLOYMENT_BYTECODE_HASH { return None; } let l2_block_number = L2BlockNumber(row.miniblock_number as u32); let address = address_by_hashed_key[row.hashed_key.as_slice()]; - Some((address, l2_block_number)) + Some((address, (l2_block_number, bytecode_hash))) }); Ok(deployment_data.collect()) } @@ -727,7 +681,7 @@ impl StorageLogsDal<'_, '_> { FROM storage_logs WHERE - storage_logs.miniblock_number = $1 + storage_logs.miniblock_number <= $1 AND storage_logs.hashed_key >= u.start_key AND storage_logs.hashed_key <= u.end_key ORDER BY @@ -784,7 +738,7 @@ impl StorageLogsDal<'_, '_> { storage_logs INNER JOIN initial_writes ON storage_logs.hashed_key = initial_writes.hashed_key WHERE - storage_logs.miniblock_number = $1 + storage_logs.miniblock_number <= $1 AND storage_logs.hashed_key >= $2::bytea AND storage_logs.hashed_key <= $3::bytea ORDER BY @@ -1168,8 +1122,9 @@ mod tests { async fn filtering_deployed_contracts() { let contract_address = Address::repeat_byte(1); let other_contract_address = Address::repeat_byte(23); + let bytecode_hash = H256::repeat_byte(0xff); let successful_deployment = - StorageLog::new_write_log(get_code_key(&contract_address), H256::repeat_byte(0xff)); + StorageLog::new_write_log(get_code_key(&contract_address), bytecode_hash); let failed_deployment = StorageLog::new_write_log( get_code_key(&contract_address), FAILED_CONTRACT_DEPLOYMENT_BYTECODE_HASH, @@ -1233,7 +1188,7 @@ mod tests { .unwrap(); assert_eq!( deployed_map, - HashMap::from([(contract_address, L2BlockNumber(2))]) + HashMap::from([(contract_address, (L2BlockNumber(2), bytecode_hash))]) ); } @@ -1268,7 +1223,7 @@ mod tests { .unwrap(); assert_eq!( deployed_map, - HashMap::from([(contract_address, L2BlockNumber(2))]) + HashMap::from([(contract_address, (L2BlockNumber(2), bytecode_hash))]) ); for new_l2_block in [None, Some(L2BlockNumber(3))] { @@ -1283,8 +1238,8 @@ mod tests { assert_eq!( deployed_map, HashMap::from([ - (contract_address, L2BlockNumber(2)), - (other_contract_address, L2BlockNumber(3)), + (contract_address, (L2BlockNumber(2), bytecode_hash)), + (other_contract_address, (L2BlockNumber(3), bytecode_hash)), ]) ); } diff --git a/core/lib/dal/src/sync_dal.rs b/core/lib/dal/src/sync_dal.rs index 55e6543c0285..4372a83f1feb 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 baa2ee584856..11d4e55a55af 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/tokens_dal.rs b/core/lib/dal/src/tokens_dal.rs index 218e152fa82a..b5fd67fc63c8 100644 --- a/core/lib/dal/src/tokens_dal.rs +++ b/core/lib/dal/src/tokens_dal.rs @@ -98,7 +98,7 @@ impl TokensDal<'_, '_> { .filter_map(|address| { if address.is_zero() { None - } else if let Some(deployed_at) = token_deployment_data.get(&address) { + } else if let Some((deployed_at, _)) = token_deployment_data.get(&address) { (deployed_at > &block_number).then_some(address.0) } else { // Token belongs to a "pending" L2 block that's not yet fully inserted to the database. diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index 5314e9799b33..9c0889ebfc75 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 c2209bb9c938..44d7ed89c477 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/contract_verifier.rs b/core/lib/env_config/src/contract_verifier.rs index 3079a8daa9cf..484e06341586 100644 --- a/core/lib/env_config/src/contract_verifier.rs +++ b/core/lib/env_config/src/contract_verifier.rs @@ -18,11 +18,8 @@ mod tests { fn expected_config() -> ContractVerifierConfig { ContractVerifierConfig { compilation_timeout: 30, - polling_interval: Some(1000), prometheus_port: 3314, - threads_per_server: Some(128), port: 3070, - url: "127.0.0.1:3070".to_string(), } } @@ -31,12 +28,8 @@ mod tests { let mut lock = MUTEX.lock(); let config = r#" CONTRACT_VERIFIER_COMPILATION_TIMEOUT=30 - CONTRACT_VERIFIER_POLLING_INTERVAL=1000 CONTRACT_VERIFIER_PROMETHEUS_PORT=3314 CONTRACT_VERIFIER_PORT=3070 - CONTRACT_VERIFIER_URL=127.0.0.1:3070 - CONTRACT_VERIFIER_THREADS_PER_SERVER=128 - "#; lock.set_env(config); diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index 3792f356be4e..ae4e1d1d5b4f 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 8ceeb215faf4..7dc5ece5db59 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -1,17 +1,21 @@ use std::env; -use zksync_config::configs::{ - da_client::{ - avail::{ - AvailClientConfig, AvailSecrets, AVAIL_FULL_CLIENT_NAME, AVAIL_GAS_RELAY_CLIENT_NAME, +use zksync_config::{ + configs::{ + da_client::{ + avail::{ + AvailClientConfig, AvailSecrets, AVAIL_FULL_CLIENT_NAME, + AVAIL_GAS_RELAY_CLIENT_NAME, + }, + celestia::CelestiaSecrets, + eigen::{EigenSecrets, EIGEN_DISPERSER_CLIENT_NAME, EIGEN_MEMSTORE_CLIENT_NAME}, + DAClientConfig, AVAIL_CLIENT_CONFIG_NAME, CELESTIA_CLIENT_CONFIG_NAME, + EIGEN_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, }, - secrets::DataAvailabilitySecrets, - AvailConfig, + EigenConfig, }; use crate::{envy_load, FromEnv}; @@ -34,7 +38,17 @@ impl FromEnv for DAClientConfig { }, }), CELESTIA_CLIENT_CONFIG_NAME => Self::Celestia(envy_load("da_celestia_config", "DA_")?), - EIGEN_CLIENT_CONFIG_NAME => Self::Eigen(envy_load("da_eigen_config", "DA_")?), + EIGEN_CLIENT_CONFIG_NAME => match env::var("DA_EIGEN_CLIENT_TYPE")?.as_str() { + EIGEN_DISPERSER_CLIENT_NAME => Self::Eigen(EigenConfig::Disperser(envy_load( + "da_eigen_config_disperser", + "DA_", + )?)), + EIGEN_MEMSTORE_CLIENT_NAME => Self::Eigen(EigenConfig::MemStore(envy_load( + "da_eigen_config_memstore", + "DA_", + )?)), + _ => anyhow::bail!("Unknown Eigen DA client type"), + }, OBJECT_STORE_CLIENT_CONFIG_NAME => { Self::ObjectStore(envy_load("da_object_store", "DA_")?) } @@ -94,6 +108,7 @@ mod tests { configs::{ da_client::{ avail::{AvailClientConfig, AvailDefaultConfig}, + eigen::{DisperserConfig, MemStoreConfig}, DAClientConfig::{self, ObjectStore}, }, object_store::ObjectStoreMode::GCS, @@ -244,22 +259,66 @@ mod tests { } #[test] - fn from_env_eigen_client() { + fn from_env_eigen_client_memstore() { + let mut lock = MUTEX.lock(); + let config = r#" + DA_CLIENT="Eigen" + DA_EIGEN_CLIENT_TYPE="MemStore" + DA_MAX_BLOB_SIZE_BYTES=10 + DA_BLOB_EXPIRATION=20 + DA_GET_LATENCY=30 + DA_PUT_LATENCY=40 + "#; + lock.set_env(config); + + let actual = DAClientConfig::from_env().unwrap(); + assert_eq!( + actual, + DAClientConfig::Eigen(EigenConfig::MemStore(MemStoreConfig { + max_blob_size_bytes: 10, + blob_expiration: 20, + get_latency: 30, + put_latency: 40, + })) + ); + } + + #[test] + fn from_env_eigen_client_remote() { let mut lock = MUTEX.lock(); let config = r#" DA_CLIENT="Eigen" - DA_RPC_NODE_URL="localhost:12345" - DA_INCLUSION_POLLING_INTERVAL_MS="1000" + DA_EIGEN_CLIENT_TYPE="Disperser" + DA_DISPERSER_RPC="http://localhost:8080" + DA_ETH_CONFIRMATION_DEPTH=0 + DA_EIGENDA_ETH_RPC="http://localhost:8545" + DA_EIGENDA_SVC_MANAGER_ADDRESS="0x123" + DA_BLOB_SIZE_LIMIT=1000 + DA_STATUS_QUERY_TIMEOUT=2 + DA_STATUS_QUERY_INTERVAL=3 + DA_WAIT_FOR_FINALIZATION=true + DA_AUTHENTICATED=false + DA_VERIFY_CERT=false + DA_PATH_TO_POINTS="resources" "#; 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, - }) + DAClientConfig::Eigen(EigenConfig::Disperser(DisperserConfig { + disperser_rpc: "http://localhost:8080".to_string(), + eth_confirmation_depth: 0, + eigenda_eth_rpc: "http://localhost:8545".to_string(), + eigenda_svc_manager_address: "0x123".to_string(), + blob_size_limit: 1000, + status_query_timeout: 2, + status_query_interval: 3, + wait_for_finalization: true, + authenticated: false, + verify_cert: false, + path_to_points: "resources".to_string(), + })) ); } diff --git a/core/lib/env_config/src/lib.rs b/core/lib/env_config/src/lib.rs index b72c2c5d5b94..f7c45e98500e 100644 --- a/core/lib/env_config/src/lib.rs +++ b/core/lib/env_config/src/lib.rs @@ -1,4 +1,3 @@ -use anyhow::Context as _; use serde::de::DeserializeOwned; mod api; @@ -33,6 +32,7 @@ mod vm_runner; mod wallets; mod da_client; +mod timestamp_asserter; pub trait FromEnv: Sized { fn from_env() -> anyhow::Result; @@ -43,5 +43,5 @@ pub trait FromEnv: Sized { pub fn envy_load(name: &str, prefix: &str) -> anyhow::Result { envy::prefixed(prefix) .from_env() - .with_context(|| format!("Cannot load config <{name}>")) + .map_err(|e| anyhow::anyhow!("Failed to load {} from env: {}", name, e)) } 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 000000000000..df586f5925ee --- /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/mempool/src/mempool_store.rs b/core/lib/mempool/src/mempool_store.rs index f6f9b72f9b64..70176b456dd1 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 b84ab7d57651..d40158ae9558 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 99a63ffd08e2..7c2694dff5ef 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/multivm/Cargo.toml b/core/lib/multivm/Cargo.toml index eb770bf9b57e..27130bc2720d 100644 --- a/core/lib/multivm/Cargo.toml +++ b/core/lib/multivm/Cargo.toml @@ -43,6 +43,7 @@ ethabi.workspace = true [dev-dependencies] assert_matches.workspace = true pretty_assertions.workspace = true +rand.workspace = true test-casing.workspace = true zksync_test_account.workspace = true zksync_eth_signer.workspace = true diff --git a/core/lib/multivm/src/glue/types/vm/vm_block_result.rs b/core/lib/multivm/src/glue/types/vm/vm_block_result.rs index 50bb19938fe7..c4eb0b1741aa 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_block_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_block_result.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use circuit_sequencer_api_1_3_3::sort_storage_access::sort_storage_access_queries as sort_storage_access_queries_1_3_3; use itertools::Itertools; use zk_evm_1_3_1::aux_structures::LogQuery as LogQuery_1_3_1; @@ -47,7 +49,7 @@ impl GlueFrom for crate::interface::Fi circuit_statistic: Default::default(), }, refunds: Refunds::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), }, final_execution_state: CurrentExecutionState { events: value.full_result.events, @@ -104,7 +106,7 @@ impl GlueFrom for crate::interface::Fi circuit_statistic: Default::default(), }, refunds: Refunds::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), }, final_execution_state: CurrentExecutionState { events: value.full_result.events, @@ -160,7 +162,7 @@ impl GlueFrom for crate::interface: circuit_statistic: Default::default(), }, refunds: Refunds::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), }, final_execution_state: CurrentExecutionState { events: value.full_result.events, @@ -230,7 +232,7 @@ impl GlueFrom circuit_statistic: Default::default(), }, refunds: Refunds::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), } } } @@ -263,7 +265,7 @@ impl GlueFrom circuit_statistic: Default::default(), }, refunds: Refunds::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), } } } @@ -312,7 +314,7 @@ impl GlueFrom circuit_statistic: Default::default(), }, refunds: Refunds::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), } } } diff --git a/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs b/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs index 4c4cffcc6876..fa251116b85c 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_partial_execution_result.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::glue::{GlueFrom, GlueInto}; impl GlueFrom @@ -22,7 +24,7 @@ impl GlueFrom gas_refunded: 0, operator_suggested_refund: 0, }, - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), } } } @@ -49,7 +51,7 @@ impl GlueFrom gas_refunded: 0, operator_suggested_refund: 0, }, - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), } } } @@ -76,7 +78,7 @@ impl GlueFrom gas_refunded: 0, operator_suggested_refund: 0, }, - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), } } } diff --git a/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs b/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs index 8978d4348edd..fcbcde990f37 100644 --- a/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs +++ b/core/lib/multivm/src/glue/types/vm/vm_tx_execution_result.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::{ glue::{GlueFrom, GlueInto}, interface::{ @@ -66,14 +68,14 @@ impl GlueFrom VmExecutionResultAndLogs { result: ExecutionResult::Halt { reason: halt }, logs: Default::default(), statistics: Default::default(), refunds: Default::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), }, } } @@ -102,14 +104,14 @@ impl logs: Default::default(), statistics: Default::default(), refunds: Default::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), }, TxRevertReason::Halt(halt) => VmExecutionResultAndLogs { result: ExecutionResult::Halt { reason: halt }, logs: Default::default(), statistics: Default::default(), refunds: Default::default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), }, } } @@ -133,7 +135,7 @@ impl GlueFrom { unreachable!("Halt is the only revert reason for VM 5") diff --git a/core/lib/multivm/src/tracers/validator/mod.rs b/core/lib/multivm/src/tracers/validator/mod.rs index 057551a9efeb..a095be9f3748 100644 --- a/core/lib/multivm/src/tracers/validator/mod.rs +++ b/core/lib/multivm/src/tracers/validator/mod.rs @@ -1,7 +1,7 @@ use std::{ collections::{BTreeSet, HashSet}, marker::PhantomData, - sync::Arc, + sync::{Arc, Mutex}, }; use once_cell::sync::OnceCell; @@ -13,6 +13,10 @@ 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}, + L1BatchEnv, +}; use self::types::{NewTrustedValidationItems, ValidationTracerMode}; use crate::{ @@ -47,8 +51,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_env: L1BatchEnv, pub result: Arc>, + pub traces: Arc>, _marker: PhantomData H>, } @@ -57,30 +64,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_env: L1BatchEnv) -> 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_env, + } + } + + 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 +165,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 +217,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 c206bd6fb2ad..d3dc7fd87c42 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,52 @@ 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_env.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 e6fb05e24069..4466d96a96b7 100644 --- a/core/lib/multivm/src/versions/shadow/tests.rs +++ b/core/lib/multivm/src/versions/shadow/tests.rs @@ -231,7 +231,22 @@ mod evm_emulator { #[test] fn mock_emulator_with_deployment() { - test_mock_emulator_with_deployment::(); + test_mock_emulator_with_deployment::(false); + } + + #[test] + fn mock_emulator_with_reverted_deployment() { + test_mock_emulator_with_deployment::(true); + } + + #[test] + fn mock_emulator_with_recursive_deployment() { + test_mock_emulator_with_recursive_deployment::(); + } + + #[test] + fn mock_emulator_with_partial_reverts() { + test_mock_emulator_with_partial_reverts::(); } #[test] diff --git a/core/lib/multivm/src/versions/testonly/evm_emulator.rs b/core/lib/multivm/src/versions/testonly/evm_emulator.rs index 6de394842aaa..a77274ec581c 100644 --- a/core/lib/multivm/src/versions/testonly/evm_emulator.rs +++ b/core/lib/multivm/src/versions/testonly/evm_emulator.rs @@ -1,6 +1,8 @@ use std::collections::HashMap; +use assert_matches::assert_matches; use ethabi::Token; +use rand::{rngs::StdRng, Rng, SeedableRng}; use zksync_contracts::{load_contract, read_bytecode, SystemContractCode}; use zksync_system_constants::{ CONTRACT_DEPLOYER_ADDRESS, KNOWN_CODES_STORAGE_ADDRESS, L2_BASE_TOKEN_ADDRESS, @@ -18,7 +20,8 @@ use zksync_utils::{ use super::{default_system_env, TestedVm, VmTester, VmTesterBuilder}; use crate::interface::{ - storage::InMemoryStorage, TxExecutionMode, VmExecutionResultAndLogs, VmInterfaceExt, + storage::InMemoryStorage, ExecutionResult, TxExecutionMode, VmExecutionResultAndLogs, + VmInterfaceExt, }; const MOCK_DEPLOYER_PATH: &str = "etc/contracts-test-data/artifacts-zk/contracts/mock-evm/mock-evm.sol/MockContractDeployer.json"; @@ -146,11 +149,26 @@ pub(crate) fn test_tracing_evm_contract_deployment() { .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 + // The EraVM contract also deployed in a transaction should be filtered out assert_eq!( - new_known_factory_deps[&expected_bytecode_hash], - evm_bytecode + vm_result.dynamic_factory_deps, + HashMap::from([(expected_bytecode_hash, evm_bytecode)]) + ); + + // "Deploy" a bytecode in another transaction and check that the first tx doesn't interfere with the returned `dynamic_factory_deps`. + let args = [Token::Bytes((0..32).rev().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); + + assert_eq!( + vm_result.dynamic_factory_deps, + HashMap::from([(expected_bytecode_hash, evm_bytecode)]) ); } @@ -310,7 +328,7 @@ pub(crate) fn test_calling_to_mock_emulator_from_native_contract() assert!(!vm_result.result.is_failed(), "{:?}", vm_result.result); } -pub(crate) fn test_mock_emulator_with_deployment() { +pub(crate) fn test_mock_emulator_with_deployment(revert: bool) { let contract_address = Address::repeat_byte(0xaa); let mut vm = EvmTestBuilder::new(true, contract_address) .with_mock_deployer() @@ -329,6 +347,7 @@ pub(crate) fn test_mock_emulator_with_deployment() { .encode_input(&[ Token::FixedBytes(new_evm_bytecode_hash.0.into()), Token::Bytes(new_evm_bytecode.clone()), + Token::Bool(revert), ]) .unwrap(), value: 0.into(), @@ -336,16 +355,159 @@ pub(crate) fn test_mock_emulator_with_deployment() { }, None, ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(test_tx, true); + + assert_eq!(vm_result.result.is_failed(), revert, "{vm_result:?}"); + let expected_dynamic_deps = if revert { + HashMap::new() + } else { + HashMap::from([(new_evm_bytecode_hash, new_evm_bytecode)]) + }; + assert_eq!(vm_result.dynamic_factory_deps, expected_dynamic_deps); + + // Test that a following transaction can decommit / call EVM contracts deployed in the previous transaction. + let test_fn = mock_emulator_abi + .function("testCallToPreviousDeployment") + .unwrap(); + let test_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(contract_address), + calldata: test_fn.encode_input(&[]).unwrap(), + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + let (_, vm_result) = vm + .vm + .execute_transaction_with_bytecode_compression(test_tx, true); + + if revert { + assert_matches!( + &vm_result.result, + ExecutionResult::Revert { output } + if output.to_string().contains("contract code length") + ); + } else { + assert!(!vm_result.result.is_failed(), "{vm_result:?}"); + } + assert!(vm_result.dynamic_factory_deps.is_empty(), "{vm_result:?}"); +} + +fn encode_deployment(hash: H256, bytecode: Vec) -> Token { + assert_eq!(bytecode.len(), 32); + Token::Tuple(vec![ + Token::FixedBytes(hash.0.to_vec()), + Token::FixedBytes(bytecode), + ]) +} + +pub(crate) fn test_mock_emulator_with_recursive_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 bytecodes: HashMap<_, _> = (0_u8..10) + .map(|byte| { + let bytecode = vec![byte; 32]; + (hash_evm_bytecode(&bytecode), bytecode) + }) + .collect(); + let test_fn = mock_emulator_abi + .function("testRecursiveDeployment") + .unwrap(); + let deployments: Vec<_> = bytecodes + .iter() + .map(|(hash, code)| encode_deployment(*hash, code.clone())) + .collect(); + let test_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(contract_address), + calldata: test_fn.encode_input(&[Token::Array(deployments)]).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:?}"); + assert_eq!(vm_result.dynamic_factory_deps, bytecodes); +} + +pub(crate) fn test_mock_emulator_with_partial_reverts() { + for seed in [1, 10, 100, 1_000] { + println!("Testing with RNG seed {seed}"); + let mut rng = StdRng::seed_from_u64(seed); + test_mock_emulator_with_partial_reverts_and_rng::(&mut rng); + } +} + +fn test_mock_emulator_with_partial_reverts_and_rng(rng: &mut impl Rng) { + 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 all_bytecodes: HashMap<_, _> = (0_u8..10) + .map(|_| { + let bytecode = vec![rng.gen(); 32]; + (hash_evm_bytecode(&bytecode), bytecode) + }) + .collect(); + let should_revert: Vec<_> = (0..10).map(|_| rng.gen::()).collect(); + + let test_fn = mock_emulator_abi + .function("testDeploymentWithPartialRevert") + .unwrap(); + let deployments: Vec<_> = all_bytecodes + .iter() + .map(|(hash, code)| encode_deployment(*hash, code.clone())) + .collect(); + let revert_tokens: Vec<_> = should_revert.iter().copied().map(Token::Bool).collect(); + + let test_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(contract_address), + calldata: test_fn + .encode_input(&[Token::Array(deployments), Token::Array(revert_tokens)]) + .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(); + let dynamic_deps = &vm_result.dynamic_factory_deps; assert_eq!( - factory_deps, - HashMap::from([(new_evm_bytecode_hash, new_evm_bytecode)]) + dynamic_deps.len(), + should_revert + .iter() + .map(|flag| !flag as usize) + .sum::(), + "{dynamic_deps:?}" ); + for ((bytecode_hash, bytecode), &should_revert) in all_bytecodes.iter().zip(&should_revert) { + assert_eq!( + dynamic_deps.get(bytecode_hash), + (!should_revert).then_some(bytecode), + "hash={bytecode_hash:?}, deps={dynamic_deps:?}" + ); + } } pub(crate) fn test_mock_emulator_with_delegate_call() { diff --git a/core/lib/multivm/src/versions/vm_1_4_1/implementation/execution.rs b/core/lib/multivm/src/versions/vm_1_4_1/implementation/execution.rs index cc199fef9416..35ff73071ca6 100644 --- a/core/lib/multivm/src/versions/vm_1_4_1/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_1_4_1/implementation/execution.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{collections::HashMap, mem}; use zk_evm_1_4_1::aux_structures::Timestamp; @@ -99,7 +99,7 @@ impl Vm { logs, statistics, refunds, - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), // dynamic bytecode deployment is not supported }; (stop_reason, result) diff --git a/core/lib/multivm/src/versions/vm_1_4_2/implementation/execution.rs b/core/lib/multivm/src/versions/vm_1_4_2/implementation/execution.rs index f6e49cd8b149..341584168be4 100644 --- a/core/lib/multivm/src/versions/vm_1_4_2/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_1_4_2/implementation/execution.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use zk_evm_1_4_1::aux_structures::Timestamp; use crate::{ @@ -96,7 +98,7 @@ impl Vm { logs, statistics, refunds, - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), // dynamic bytecode deployment is not supported }; (stop_reason, result) diff --git a/core/lib/multivm/src/versions/vm_boojum_integration/implementation/execution.rs b/core/lib/multivm/src/versions/vm_boojum_integration/implementation/execution.rs index b8b939f86731..e942f0fc4245 100644 --- a/core/lib/multivm/src/versions/vm_boojum_integration/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_boojum_integration/implementation/execution.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{collections::HashMap, mem}; use zk_evm_1_4_0::aux_structures::Timestamp; @@ -93,7 +93,7 @@ impl Vm { logs, statistics, refunds, - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), // dynamic bytecode deployment is not supported }; (stop_reason, result) 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 1999db47fb98..9c1c0b7dfb7f 100644 --- a/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/vm_fast/circuits_tracer.rs @@ -1,5 +1,7 @@ use circuit_sequencer_api_1_5_0::{geometry_config::get_geometry_config, toolset::GeometryConfig}; -use zksync_vm2::interface::{CycleStats, GlobalStateInterface, Opcode, OpcodeType, Tracer}; +use zksync_vm2::interface::{ + CycleStats, GlobalStateInterface, Opcode, OpcodeType, ShouldStop, Tracer, +}; use zksync_vm_interface::CircuitStatistic; use crate::vm_latest::tracers::circuits_capacity::*; @@ -24,7 +26,10 @@ pub(super) struct CircuitsTracer { } impl Tracer for CircuitsTracer { - fn after_instruction(&mut self, _: &mut S) { + fn after_instruction( + &mut self, + _: &mut S, + ) -> ShouldStop { self.main_vm_cycles += 1; match OP::VALUE { @@ -110,6 +115,8 @@ impl Tracer for CircuitsTracer { self.ram_permutation_cycles += UMA_READ_RAM_CYCLES; } } + + 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 index d869796cd2c1..62aba8df5b9b 100644 --- a/core/lib/multivm/src/versions/vm_fast/evm_deploy_tracer.rs +++ b/core/lib/multivm/src/versions/vm_fast/evm_deploy_tracer.rs @@ -6,7 +6,7 @@ use zksync_system_constants::{CONTRACT_DEPLOYER_ADDRESS, KNOWN_CODES_STORAGE_ADD use zksync_types::U256; use zksync_utils::{bytecode::hash_evm_bytecode, h256_to_u256}; use zksync_vm2::interface::{ - CallframeInterface, CallingMode, GlobalStateInterface, Opcode, OpcodeType, Tracer, + CallframeInterface, CallingMode, GlobalStateInterface, Opcode, OpcodeType, ShouldStop, Tracer, }; use super::utils::read_fat_pointer; @@ -16,8 +16,8 @@ use super::utils::read_fat_pointer; pub(super) struct DynamicBytecodes(Rc>>>); impl DynamicBytecodes { - pub(super) fn take(&self, hash: U256) -> Option> { - self.0.borrow_mut().remove(&hash) + pub(super) fn map(&self, hash: U256, f: impl FnOnce(&[u8]) -> R) -> Option { + self.0.borrow().get(&hash).map(|code| f(code)) } fn insert(&self, hash: U256, bytecode: Vec) { @@ -76,9 +76,13 @@ impl EvmDeployTracer { impl Tracer for EvmDeployTracer { #[inline(always)] - fn after_instruction(&mut self, state: &mut S) { + 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/tests/evm_emulator.rs b/core/lib/multivm/src/versions/vm_fast/tests/evm_emulator.rs index cb7d54dba29f..7b5ea3e4447b 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/evm_emulator.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/evm_emulator.rs @@ -4,7 +4,8 @@ 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_partial_reverts, test_mock_emulator_with_payment, + test_mock_emulator_with_recursion, test_mock_emulator_with_recursive_deployment, test_mock_emulator_with_static_call, test_tracing_evm_contract_deployment, }, vm_fast::Vm, @@ -39,7 +40,22 @@ fn calling_to_mock_emulator_from_native_contract() { #[test] fn mock_emulator_with_deployment() { - test_mock_emulator_with_deployment::>(); + test_mock_emulator_with_deployment::>(false); +} + +#[test] +fn mock_emulator_with_reverted_deployment() { + test_mock_emulator_with_deployment::>(false); +} + +#[test] +fn mock_emulator_with_recursive_deployment() { + test_mock_emulator_with_recursive_deployment::>(); +} + +#[test] +fn mock_emulator_with_partial_reverts() { + test_mock_emulator_with_partial_reverts::>(); } #[test] diff --git a/core/lib/multivm/src/versions/vm_fast/vm.rs b/core/lib/multivm/src/versions/vm_fast/vm.rs index b7113bf8f198..d18f7b91f323 100644 --- a/core/lib/multivm/src/versions/vm_fast/vm.rs +++ b/core/lib/multivm/src/versions/vm_fast/vm.rs @@ -55,7 +55,6 @@ 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, }, }; @@ -212,6 +211,16 @@ impl Vm { }; break (ExecutionResult::Halt { reason }, true); } + ExecutionEnd::StoppedByTracer => { + break ( + ExecutionResult::Halt { + reason: Halt::TracerCustom( + "Unexpectedly stopped by tracer".to_string(), + ), + }, + false, + ); + } }; match Hook::from_u32(hook) { @@ -643,8 +652,10 @@ impl Vm { // 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); + let factory_deps_marked_as_known = VmEvent::extract_bytecodes_marked_as_known(&logs.events); + let dynamic_factory_deps = self + .world + .decommit_dynamic_bytecodes(factory_deps_marked_as_known); VmExecutionResultAndLogs { result: result.execution_result, @@ -661,7 +672,7 @@ impl Vm { total_log_queries: 0, }, refunds: result.refunds, - new_known_factory_deps: Some(new_known_factory_deps), + dynamic_factory_deps, } } } @@ -838,16 +849,15 @@ impl World { ) } - fn decommit_bytecodes(&self, hashes: &[H256]) -> HashMap> { - let bytecodes = hashes.iter().map(|&hash| { - let int_hash = h256_to_u256(hash); + fn decommit_dynamic_bytecodes( + &self, + candidate_hashes: impl Iterator, + ) -> HashMap> { + let bytecodes = candidate_hashes.filter_map(|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) + .dynamic_bytecodes + .map(h256_to_u256(hash), <[u8]>::to_vec)?; + Some((hash, bytecode)) }); bytecodes.collect() } @@ -923,17 +933,28 @@ impl zksync_vm2::World for World { self.program_cache .entry(hash) .or_insert_with(|| { - let bytecode = self.bytecode_cache.entry(hash).or_insert_with(|| { - // 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))) + let cached = self + .bytecode_cache + .get(&hash) + .map(|code| Program::new(code, false)) + .or_else(|| { + self.dynamic_bytecodes + .map(hash, |code| Program::new(code, false)) + }); + + if let Some(cached) = cached { + cached + } else { + let code = self + .storage + .load_factory_dep(u256_to_h256(hash)) .unwrap_or_else(|| { panic!("VM tried to decommit nonexistent bytecode: {hash:?}"); - }) - }); - Program::new(bytecode, false) + }); + let program = Program::new(&code, false); + self.bytecode_cache.insert(hash, code); + program + } }) .clone() } diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs index d9331720ce28..f8acfaec4259 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/execution.rs @@ -1,6 +1,7 @@ use std::mem; use zk_evm_1_5_0::aux_structures::Timestamp; +use zksync_vm_interface::VmEvent; use crate::{ interface::{ @@ -14,7 +15,6 @@ use crate::{ circuits_capacity::circuit_statistic_from_cycles, dispatcher::TracerDispatcher, DefaultExecutionTracer, PubdataTracer, RefundsTracer, }, - utils::extract_bytecodes_marked_as_known, vm::Vm, }, HistoryMode, @@ -101,8 +101,8 @@ impl Vm { circuit_statistic_from_cycles(tx_tracer.circuits_tracer.statistics), ); let result = tx_tracer.result_tracer.into_result(); - let factory_deps_marked_as_known = extract_bytecodes_marked_as_known(&logs.events); - let new_known_factory_deps = self.decommit_bytecodes(&factory_deps_marked_as_known); + let factory_deps_marked_as_known = VmEvent::extract_bytecodes_marked_as_known(&logs.events); + let dynamic_factory_deps = self.decommit_dynamic_bytecodes(factory_deps_marked_as_known); *dispatcher = tx_tracer.dispatcher; let result = VmExecutionResultAndLogs { @@ -110,7 +110,7 @@ impl Vm { logs, statistics, refunds, - new_known_factory_deps: Some(new_known_factory_deps), + dynamic_factory_deps, }; (stop_reason, result) diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs index d91fbfdb24df..507e3d8c7598 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/oracles/decommitter.rs @@ -1,4 +1,7 @@ -use std::{collections::HashMap, fmt::Debug}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, +}; use zk_evm_1_5_0::{ abstractions::{DecommittmentProcessor, Memory, MemoryType}, @@ -27,6 +30,9 @@ pub struct DecommitterOracle { /// The cache of bytecodes that the bootloader "knows", but that are not necessarily in the database. /// And it is also used as a database cache. pub known_bytecodes: HistoryRecorder>, H>, + /// Subset of `known_bytecodes` that are dynamically deployed during VM execution. Currently, + /// only EVM bytecodes can be deployed like that. + pub dynamic_bytecode_hashes: HashSet, /// Stores pages of memory where certain code hashes have already been decommitted. /// It is expected that they all are present in the DB. // `decommitted_code_hashes` history is necessary @@ -40,6 +46,7 @@ impl DecommitterOracle { Self { storage, known_bytecodes: HistoryRecorder::default(), + dynamic_bytecode_hashes: HashSet::default(), decommitted_code_hashes: HistoryRecorder::default(), decommitment_requests: HistoryRecorder::default(), } @@ -76,6 +83,17 @@ impl DecommitterOracle { } } + pub fn insert_dynamic_bytecode( + &mut self, + bytecode_hash: U256, + bytecode: Vec, + timestamp: Timestamp, + ) { + self.dynamic_bytecode_hashes.insert(bytecode_hash); + self.known_bytecodes + .insert(bytecode_hash, bytecode, timestamp); + } + pub fn get_decommitted_bytecodes_after_timestamp(&self, timestamp: Timestamp) -> usize { // Note, that here we rely on the fact that for each used bytecode // there is one and only one corresponding event in the history of it. 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 b9b96c670983..5b6e24eefbf0 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 @@ -4,7 +4,8 @@ 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_partial_reverts, test_mock_emulator_with_payment, + test_mock_emulator_with_recursion, test_mock_emulator_with_recursive_deployment, test_mock_emulator_with_static_call, test_tracing_evm_contract_deployment, }, vm_latest::{HistoryEnabled, Vm}, @@ -39,7 +40,22 @@ fn calling_to_mock_emulator_from_native_contract() { #[test] fn mock_emulator_with_deployment() { - test_mock_emulator_with_deployment::>(); + test_mock_emulator_with_deployment::>(false); +} + +#[test] +fn mock_emulator_with_reverted_deployment() { + test_mock_emulator_with_deployment::>(true); +} + +#[test] +fn mock_emulator_with_recursive_deployment() { + test_mock_emulator_with_recursive_deployment::>(); +} + +#[test] +fn mock_emulator_with_partial_reverts() { + test_mock_emulator_with_partial_reverts::>(); } #[test] diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs index becc4f225276..61c8ef0b5abf 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs @@ -89,14 +89,13 @@ impl VmTracer for EvmDeployTracer { state: &mut ZkSyncVmState, _bootloader_state: &mut BootloaderState, ) -> TracerExecutionStatus { + let timestamp = Timestamp(state.local_state.timestamp); for published_bytecode in mem::take(&mut self.pending_bytecodes) { - let hash = hash_evm_bytecode(&published_bytecode); + let hash = h256_to_u256(hash_evm_bytecode(&published_bytecode)); let as_words = bytes_to_be_words(published_bytecode); - - state.decommittment_processor.populate( - vec![(h256_to_u256(hash), as_words)], - Timestamp(state.local_state.timestamp), - ); + state + .decommittment_processor + .insert_dynamic_bytecode(hash, as_words, timestamp); } TracerExecutionStatus::Continue } diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs index d25f66361f1b..90bb0c610e2c 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs @@ -83,27 +83,21 @@ pub(crate) fn new_vm_state( let mut memory = SimpleMemory::default(); let event_sink = InMemoryEventSink::default(); let precompiles_processor = PrecompilesProcessorWithHistory::::default(); + let mut decommittment_processor: DecommitterOracle = DecommitterOracle::new(storage); - - decommittment_processor.populate( - vec![( - h256_to_u256(system_env.base_system_smart_contracts.default_aa.hash), - system_env - .base_system_smart_contracts - .default_aa - .code - .clone(), - )], - Timestamp(0), - ); - + let mut initial_bytecodes = vec![( + h256_to_u256(system_env.base_system_smart_contracts.default_aa.hash), + system_env + .base_system_smart_contracts + .default_aa + .code + .clone(), + )]; if let Some(evm_emulator) = &system_env.base_system_smart_contracts.evm_emulator { - decommittment_processor.populate( - vec![(h256_to_u256(evm_emulator.hash), evm_emulator.code.clone())], - Timestamp(0), - ); + initial_bytecodes.push((h256_to_u256(evm_emulator.hash), evm_emulator.code.clone())); } + decommittment_processor.populate(initial_bytecodes, Timestamp(0)); memory.populate( vec![( diff --git a/core/lib/multivm/src/versions/vm_latest/utils/mod.rs b/core/lib/multivm/src/versions/vm_latest/utils/mod.rs index aeb66755f514..97483633bc54 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/mod.rs @@ -1,9 +1,6 @@ //! Utility functions for the VM. -use once_cell::sync::Lazy; use zk_evm_1_5_0::aux_structures::MemoryPage; -use zksync_types::{H256, KNOWN_CODES_STORAGE_ADDRESS}; -use zksync_vm_interface::VmEvent; pub mod fee; pub mod l2_blocks; @@ -14,24 +11,3 @@ pub mod transaction_encoding; pub const fn heap_page_from_base(base: MemoryPage) -> MemoryPage { MemoryPage(base.0 + 2) } - -/// Extracts all bytecodes marked as known on the system contracts. -pub fn extract_bytecodes_marked_as_known(all_generated_events: &[VmEvent]) -> Vec { - static PUBLISHED_BYTECODE_SIGNATURE: Lazy = Lazy::new(|| { - ethabi::long_signature( - "MarkedAsKnown", - &[ethabi::ParamType::FixedBytes(32), ethabi::ParamType::Bool], - ) - }); - - all_generated_events - .iter() - .filter(|event| { - // Filter events from the deployer contract that match the expected signature. - event.address == KNOWN_CODES_STORAGE_ADDRESS - && event.indexed_topics.len() == 3 - && event.indexed_topics[0] == *PUBLISHED_BYTECODE_SIGNATURE - }) - .map(|event| event.indexed_topics[1]) - .collect() -} diff --git a/core/lib/multivm/src/versions/vm_latest/vm.rs b/core/lib/multivm/src/versions/vm_latest/vm.rs index ef6cee454a87..ff90eb14ee42 100644 --- a/core/lib/multivm/src/versions/vm_latest/vm.rs +++ b/core/lib/multivm/src/versions/vm_latest/vm.rs @@ -85,16 +85,24 @@ impl Vm { self.state.local_state.callstack.current.ergs_remaining } - pub(crate) fn decommit_bytecodes(&self, hashes: &[H256]) -> HashMap> { - let bytecodes = hashes.iter().map(|&hash| { - let bytecode_words = self - .state - .decommittment_processor + pub(crate) fn decommit_dynamic_bytecodes( + &self, + candidate_hashes: impl Iterator, + ) -> HashMap> { + let decommitter = &self.state.decommittment_processor; + let bytecodes = candidate_hashes.filter_map(|hash| { + let int_hash = h256_to_u256(hash); + if !decommitter.dynamic_bytecode_hashes.contains(&int_hash) { + return None; + } + let bytecode = decommitter .known_bytecodes .inner() - .get(&h256_to_u256(hash)) - .unwrap_or_else(|| panic!("Bytecode with hash {hash:?} not found")); - (hash, be_words_to_bytes(bytecode_words)) + .get(&int_hash) + .unwrap_or_else(|| { + panic!("Bytecode with hash {hash:?} not found"); + }); + Some((hash, be_words_to_bytes(bytecode))) }); bytecodes.collect() } diff --git a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs index 9462a89be2ab..e8d19dfbba97 100644 --- a/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_refunds_enhancement/implementation/execution.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{collections::HashMap, mem}; use zk_evm_1_3_3::aux_structures::Timestamp; @@ -90,7 +90,7 @@ impl Vm { logs, statistics, refunds, - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), // dynamic bytecode deployment is not supported }; (stop_reason, result) diff --git a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs index b1ad4d257b77..d3d511ed5398 100644 --- a/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs +++ b/core/lib/multivm/src/versions/vm_virtual_blocks/implementation/execution.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{collections::HashMap, mem}; use zk_evm_1_3_3::aux_structures::Timestamp; @@ -88,7 +88,7 @@ impl Vm { .refund_tracer .map(|r| r.get_refunds()) .unwrap_or_default(), - new_known_factory_deps: None, + dynamic_factory_deps: HashMap::new(), // dynamic bytecode deployment is not supported }; tx_tracer.dispatcher.save_results(&mut result); diff --git a/core/lib/protobuf_config/Cargo.toml b/core/lib/protobuf_config/Cargo.toml index 87a0a63567ba..92d9bd53978c 100644 --- a/core/lib/protobuf_config/Cargo.toml +++ b/core/lib/protobuf_config/Cargo.toml @@ -26,7 +26,6 @@ rand.workspace = true hex.workspace = true secrecy.workspace = true tracing.workspace = true -time.workspace = true [build-dependencies] zksync_protobuf_build.workspace = true diff --git a/core/lib/protobuf_config/src/contract_verifier.rs b/core/lib/protobuf_config/src/contract_verifier.rs index e0b0517ea0f6..3fb7cfe0bdf7 100644 --- a/core/lib/protobuf_config/src/contract_verifier.rs +++ b/core/lib/protobuf_config/src/contract_verifier.rs @@ -10,29 +10,19 @@ impl ProtoRepr for proto::ContractVerifier { Ok(Self::Type { compilation_timeout: *required(&self.compilation_timeout) .context("compilation_timeout")?, - polling_interval: self.polling_interval, prometheus_port: required(&self.prometheus_port) .and_then(|x| Ok((*x).try_into()?)) .context("prometheus_port")?, - url: required(&self.url).cloned().context("url")?, port: required(&self.port) .and_then(|x| (*x).try_into().context("overflow")) .context("port")?, - threads_per_server: self - .threads_per_server - .map(|a| a.try_into()) - .transpose() - .context("threads_per_server")?, }) } fn build(this: &Self::Type) -> Self { Self { port: Some(this.port as u32), - url: Some(this.url.clone()), compilation_timeout: Some(this.compilation_timeout), - polling_interval: this.polling_interval, - threads_per_server: this.threads_per_server.map(|a| a as u32), prometheus_port: Some(this.prometheus_port.into()), } } diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index 84f03c5afe3a..660246928edb 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 341a6a9e4f43..1ae882198cd3 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -4,7 +4,7 @@ use zksync_config::configs::{ da_client::{ avail::{AvailClientConfig, AvailConfig, AvailDefaultConfig, AvailGasRelayConfig}, celestia::CelestiaConfig, - eigen::EigenConfig, + eigen::{DisperserConfig, EigenConfig, MemStoreConfig}, DAClientConfig::{Avail, Celestia, Eigen, ObjectStore}, }, }; @@ -52,13 +52,53 @@ impl ProtoRepr for proto::DataAvailabilityClient { 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::Eigen(conf) => { + let config = required(&conf.config).context("config")?; + let eigen_config = match config { + proto::eigen_config::Config::MemStore(conf) => { + EigenConfig::MemStore(MemStoreConfig { + max_blob_size_bytes: *required(&conf.max_blob_size_bytes) + .context("max_blob_size_bytes")?, + blob_expiration: *required(&conf.blob_expiration) + .context("blob_expiration")?, + get_latency: *required(&conf.get_latency).context("get_latency")?, + put_latency: *required(&conf.put_latency).context("put_latency")?, + }) + } + proto::eigen_config::Config::Disperser(conf) => { + EigenConfig::Disperser(DisperserConfig { + disperser_rpc: required(&conf.disperser_rpc) + .context("disperser_rpc")? + .clone(), + eth_confirmation_depth: *required(&conf.eth_confirmation_depth) + .context("eth_confirmation_depth")?, + eigenda_eth_rpc: required(&conf.eigenda_eth_rpc) + .context("eigenda_eth_rpc")? + .clone(), + eigenda_svc_manager_address: required( + &conf.eigenda_svc_manager_address, + ) + .context("eigenda_svc_manager_address")? + .clone(), + blob_size_limit: *required(&conf.blob_size_limit) + .context("blob_size_limit")?, + status_query_timeout: *required(&conf.status_query_timeout) + .context("status_query_timeout")?, + status_query_interval: *required(&conf.status_query_interval) + .context("status_query_interval")?, + wait_for_finalization: *required(&conf.wait_for_finalization) + .context("wait_for_finalization")?, + authenticated: *required(&conf.authenticated) + .context("authenticated")?, + verify_cert: *required(&conf.verify_cert).context("verify_cert")?, + path_to_points: required(&conf.path_to_points) + .context("path_to_points")? + .clone(), + }) + } + }; + Eigen(eigen_config) + } proto::data_availability_client::Config::ObjectStore(conf) => { ObjectStore(object_store_proto::ObjectStore::read(conf)?) } @@ -95,10 +135,41 @@ impl ProtoRepr for proto::DataAvailabilityClient { 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), - }), + Eigen(config) => match config { + EigenConfig::MemStore(config) => { + proto::data_availability_client::Config::Eigen(proto::EigenConfig { + config: Some(proto::eigen_config::Config::MemStore( + proto::MemStoreConfig { + max_blob_size_bytes: Some(config.max_blob_size_bytes), + blob_expiration: Some(config.blob_expiration), + get_latency: Some(config.get_latency), + put_latency: Some(config.put_latency), + }, + )), + }) + } + EigenConfig::Disperser(config) => { + proto::data_availability_client::Config::Eigen(proto::EigenConfig { + config: Some(proto::eigen_config::Config::Disperser( + proto::DisperserConfig { + disperser_rpc: Some(config.disperser_rpc.clone()), + eth_confirmation_depth: Some(config.eth_confirmation_depth), + eigenda_eth_rpc: Some(config.eigenda_eth_rpc.clone()), + eigenda_svc_manager_address: Some( + config.eigenda_svc_manager_address.clone(), + ), + blob_size_limit: Some(config.blob_size_limit), + status_query_timeout: Some(config.status_query_timeout), + status_query_interval: Some(config.status_query_interval), + wait_for_finalization: Some(config.wait_for_finalization), + authenticated: Some(config.authenticated), + verify_cert: Some(config.verify_cert), + path_to_points: Some(config.path_to_points.clone()), + }, + )), + }) + } + }, ObjectStore(config) => proto::data_availability_client::Config::ObjectStore( object_store_proto::ObjectStore::build(config), ), diff --git a/core/lib/protobuf_config/src/general.rs b/core/lib/protobuf_config/src/general.rs index b73539a0897f..83b4c84f20bf 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 68f7f699de20..90c0ba071c0b 100644 --- a/core/lib/protobuf_config/src/lib.rs +++ b/core/lib/protobuf_config/src/lib.rs @@ -28,7 +28,6 @@ mod observability; mod proof_data_handler; pub mod proto; mod prover; -mod prover_autoscaler; mod prover_job_monitor; mod pruning; mod secrets; @@ -36,6 +35,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/contract_verifier.proto b/core/lib/protobuf_config/src/proto/config/contract_verifier.proto index 31b1d3ed2ec4..8493274c6911 100644 --- a/core/lib/protobuf_config/src/proto/config/contract_verifier.proto +++ b/core/lib/protobuf_config/src/proto/config/contract_verifier.proto @@ -4,9 +4,10 @@ package zksync.config.contract_verifier; message ContractVerifier{ optional uint32 port = 1; // required; u16 - optional string url = 2; // required optional uint64 compilation_timeout = 3; - optional uint64 polling_interval = 4; - optional uint32 threads_per_server = 5; optional uint32 prometheus_port = 6; + + reserved 2; reserved "url"; + reserved 4; reserved "polling_interval"; + reserved 5; reserved "threads_per_server"; } diff --git a/core/lib/protobuf_config/src/proto/config/contracts.proto b/core/lib/protobuf_config/src/proto/config/contracts.proto index 6ab03e6aa11b..4ae0ee1614f8 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 0a302120d775..a143d043ebe5 100644 --- a/core/lib/protobuf_config/src/proto/config/da_client.proto +++ b/core/lib/protobuf_config/src/proto/config/da_client.proto @@ -36,9 +36,32 @@ message CelestiaConfig { optional uint64 timeout_ms = 4; } +message MemStoreConfig { + optional uint64 max_blob_size_bytes = 3; + optional uint64 blob_expiration = 4; + optional uint64 get_latency = 5; + optional uint64 put_latency = 6; +} + +message DisperserConfig { + optional string disperser_rpc = 3; + optional int32 eth_confirmation_depth = 4; + optional string eigenda_eth_rpc = 5; + optional string eigenda_svc_manager_address = 6; + optional uint32 blob_size_limit = 7; + optional uint64 status_query_timeout = 8; + optional uint64 status_query_interval = 9; + optional bool wait_for_finalization = 10; + optional bool authenticated = 11; + optional bool verify_cert = 12; + optional string path_to_points = 13; +} + message EigenConfig { - optional string rpc_node_url = 1; - optional uint64 inclusion_polling_interval_ms = 2; + oneof config { + MemStoreConfig mem_store = 1; + DisperserConfig disperser = 2; + } } message DataAvailabilityClient { diff --git a/core/lib/protobuf_config/src/proto/config/general.proto b/core/lib/protobuf_config/src/proto/config/general.proto index ee70b61b18b3..216272f3f9ad 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 deleted file mode 100644 index 742181653861..000000000000 --- a/core/lib/protobuf_config/src/proto/config/prover_autoscaler.proto +++ /dev/null @@ -1,74 +0,0 @@ -syntax = "proto3"; - -package zksync.config.prover_autoscaler; - -import "zksync/std.proto"; -import "zksync/config/observability.proto"; - -message ProverAutoscalerConfig { - optional std.Duration graceful_shutdown_timeout = 1; // optional - optional ProverAutoscalerAgentConfig agent_config = 2; // optional - optional ProverAutoscalerScalerConfig scaler_config = 3; // optional - optional observability.Observability observability = 4; // optional -} - -message ProverAutoscalerAgentConfig { - optional uint32 prometheus_port = 1; // required - optional uint32 http_port = 2; // required - repeated string namespaces = 3; // optional - optional string cluster_name = 4; // optional - optional bool dry_run = 5; // optional -} - -message ProtocolVersion { - optional string namespace = 1; // required - optional string protocol_version = 2; // required -} - -message ClusterPriority { - optional string cluster = 1; // required - optional uint32 priority = 2; // required -} - -message ProverSpeed { - optional string gpu = 1; // required - optional uint32 speed = 2; // required -} - -message MaxProver { - optional string cluster_and_gpu = 1; // required, format: / - optional uint32 max = 2; // required -} - -message MinProver { - optional string namespace = 1; // required - 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; // 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/timestamp_asserter.proto b/core/lib/protobuf_config/src/proto/config/timestamp_asserter.proto new file mode 100644 index 000000000000..c8d0b9d1fec7 --- /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 deleted file mode 100644 index 6b67d9f620ff..000000000000 --- a/core/lib/protobuf_config/src/prover_autoscaler.rs +++ /dev/null @@ -1,301 +0,0 @@ -use std::collections::HashMap; - -use anyhow::Context; -use time::Duration; -use zksync_config::configs::{self, prover_autoscaler::Gpu}; -use zksync_protobuf::{read_optional, repr::ProtoRepr, required, ProtoFmt}; - -use crate::{proto::prover_autoscaler as proto, read_optional_repr}; - -impl ProtoRepr for proto::ProverAutoscalerConfig { - type Type = configs::prover_autoscaler::ProverAutoscalerConfig; - fn read(&self) -> anyhow::Result { - Ok(Self::Type { - graceful_shutdown_timeout: read_optional(&self.graceful_shutdown_timeout) - .context("graceful_shutdown_timeout")? - .unwrap_or(Self::Type::default_graceful_shutdown_timeout()), - agent_config: read_optional_repr(&self.agent_config), - scaler_config: read_optional_repr(&self.scaler_config), - observability: read_optional_repr(&self.observability), - }) - } - - fn build(this: &Self::Type) -> Self { - Self { - graceful_shutdown_timeout: Some(ProtoFmt::build(&this.graceful_shutdown_timeout)), - agent_config: this.agent_config.as_ref().map(ProtoRepr::build), - scaler_config: this.scaler_config.as_ref().map(ProtoRepr::build), - observability: this.observability.as_ref().map(ProtoRepr::build), - } - } -} - -impl ProtoRepr for proto::ProverAutoscalerAgentConfig { - type Type = configs::prover_autoscaler::ProverAutoscalerAgentConfig; - fn read(&self) -> anyhow::Result { - Ok(Self::Type { - prometheus_port: required(&self.prometheus_port) - .and_then(|x| Ok((*x).try_into()?)) - .context("prometheus_port")?, - http_port: required(&self.http_port) - .and_then(|x| Ok((*x).try_into()?)) - .context("http_port")?, - namespaces: self.namespaces.to_vec(), - cluster_name: Some("".to_string()), - dry_run: self.dry_run.unwrap_or(Self::Type::default_dry_run()), - }) - } - - fn build(this: &Self::Type) -> Self { - Self { - prometheus_port: Some(this.prometheus_port.into()), - http_port: Some(this.http_port.into()), - namespaces: this.namespaces.clone(), - cluster_name: this.cluster_name.clone(), - dry_run: Some(this.dry_run), - } - } -} - -impl ProtoRepr for proto::ProverAutoscalerScalerConfig { - type Type = configs::prover_autoscaler::ProverAutoscalerScalerConfig; - fn read(&self) -> anyhow::Result { - Ok(Self::Type { - prometheus_port: required(&self.prometheus_port) - .and_then(|x| Ok((*x).try_into()?)) - .context("prometheus_port")?, - scaler_run_interval: read_optional(&self.scaler_run_interval) - .context("scaler_run_interval")? - .unwrap_or(Self::Type::default_scaler_run_interval()), - prover_job_monitor_url: required(&self.prover_job_monitor_url) - .context("prover_job_monitor_url")? - .clone(), - agents: self.agents.to_vec(), - protocol_versions: self - .protocol_versions - .iter() - .enumerate() - .map(|(i, e)| e.read().context(i)) - .collect::>() - .context("protocol_versions")?, - cluster_priorities: self - .cluster_priorities - .iter() - .enumerate() - .map(|(i, e)| e.read().context(i)) - .collect::>() - .context("cluster_priorities")?, - prover_speed: self - .prover_speed - .iter() - .enumerate() - .map(|(i, e)| e.read().context(i)) - .collect::>() - .context("prover_speed")?, - long_pending_duration: match self.long_pending_duration_s { - Some(s) => Duration::seconds(s.into()), - None => Self::Type::default_long_pending_duration(), - }, - max_provers: self.max_provers.iter().fold(HashMap::new(), |mut acc, e| { - let (cluster_and_gpu, max) = e.read().expect("max_provers"); - if let Some((cluster, gpu)) = cluster_and_gpu.split_once('/') { - acc.entry(cluster.to_string()) - .or_default() - .insert(gpu.parse().expect("max_provers/gpu"), max); - } - acc - }), - min_provers: self - .min_provers - .iter() - .enumerate() - .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(), - }) - } - - fn build(this: &Self::Type) -> Self { - Self { - prometheus_port: Some(this.prometheus_port.into()), - scaler_run_interval: Some(ProtoFmt::build(&this.scaler_run_interval)), - prover_job_monitor_url: Some(this.prover_job_monitor_url.clone()), - agents: this.agents.clone(), - protocol_versions: this - .protocol_versions - .iter() - .map(|(k, v)| proto::ProtocolVersion::build(&(k.clone(), v.clone()))) - .collect(), - cluster_priorities: this - .cluster_priorities - .iter() - .map(|(k, v)| proto::ClusterPriority::build(&(k.clone(), *v))) - .collect(), - prover_speed: this - .prover_speed - .iter() - .map(|(k, v)| proto::ProverSpeed::build(&(*k, *v))) - .collect(), - long_pending_duration_s: Some(this.long_pending_duration.whole_seconds() as u32), - max_provers: this - .max_provers - .iter() - .flat_map(|(cluster, inner_map)| { - inner_map.iter().map(move |(gpu, max)| { - proto::MaxProver::build(&(format!("{}/{}", cluster, gpu), *max)) - }) - }) - .collect(), - min_provers: this - .min_provers - .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), - } - } -} - -impl ProtoRepr for proto::ProtocolVersion { - type Type = (String, String); - fn read(&self) -> anyhow::Result { - Ok(( - required(&self.namespace).context("namespace")?.clone(), - required(&self.protocol_version) - .context("protocol_version")? - .clone(), - )) - } - fn build(this: &Self::Type) -> Self { - Self { - namespace: Some(this.0.clone()), - protocol_version: Some(this.1.clone()), - } - } -} - -impl ProtoRepr for proto::ClusterPriority { - type Type = (String, u32); - fn read(&self) -> anyhow::Result { - Ok(( - required(&self.cluster).context("cluster")?.clone(), - *required(&self.priority).context("priority")?, - )) - } - fn build(this: &Self::Type) -> Self { - Self { - cluster: Some(this.0.clone()), - priority: Some(this.1), - } - } -} - -impl ProtoRepr for proto::ProverSpeed { - type Type = (Gpu, u32); - fn read(&self) -> anyhow::Result { - Ok(( - required(&self.gpu).context("gpu")?.parse()?, - *required(&self.speed).context("speed")?, - )) - } - fn build(this: &Self::Type) -> Self { - Self { - gpu: Some(this.0.to_string()), - speed: Some(this.1), - } - } -} - -impl ProtoRepr for proto::MaxProver { - type Type = (String, u32); - fn read(&self) -> anyhow::Result { - Ok(( - required(&self.cluster_and_gpu) - .context("cluster_and_gpu")? - .parse()?, - *required(&self.max).context("max")?, - )) - } - fn build(this: &Self::Type) -> Self { - Self { - cluster_and_gpu: Some(this.0.to_string()), - max: Some(this.1), - } - } -} - -impl ProtoRepr for proto::MinProver { - type Type = (String, u32); - fn read(&self) -> anyhow::Result { - Ok(( - required(&self.namespace).context("namespace")?.clone(), - *required(&self.min).context("min")?, - )) - } - fn build(this: &Self::Type) -> Self { - Self { - namespace: Some(this.0.to_string()), - min: Some(this.1), - } - } -} - -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/timestamp_asserter.rs b/core/lib/protobuf_config/src/timestamp_asserter.rs new file mode 100644 index 000000000000..5984caff8c6f --- /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/prover_interface/Cargo.toml b/core/lib/prover_interface/Cargo.toml index 889b80b4fbee..50671fb3acb4 100644 --- a/core/lib/prover_interface/Cargo.toml +++ b/core/lib/prover_interface/Cargo.toml @@ -11,7 +11,7 @@ keywords.workspace = true categories.workspace = true [dependencies] -zksync_multivm.workspace = true +zksync_vm_interface.workspace = true zksync_object_store.workspace = true zksync_types.workspace = true diff --git a/core/lib/prover_interface/src/inputs.rs b/core/lib/prover_interface/src/inputs.rs index cfc1d4a0d552..48a839dc9217 100644 --- a/core/lib/prover_interface/src/inputs.rs +++ b/core/lib/prover_interface/src/inputs.rs @@ -2,12 +2,12 @@ use std::{collections::HashMap, convert::TryInto, fmt::Debug}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, Bytes}; -use zksync_multivm::interface::{L1BatchEnv, SystemEnv}; use zksync_object_store::{_reexports::BoxedError, serialize_using_bincode, Bucket, StoredObject}; use zksync_types::{ basic_fri_types::Eip4844Blobs, block::L2BlockExecutionData, commitment::PubdataParams, witness_block_state::WitnessStorageState, L1BatchNumber, ProtocolVersionId, H256, U256, }; +use zksync_vm_interface::{L1BatchEnv, SystemEnv}; const HASH_LEN: usize = H256::len_bytes(); diff --git a/core/lib/types/src/contract_verification_api.rs b/core/lib/types/src/contract_verification_api.rs index 8ee1d3ec6491..21e511549beb 100644 --- a/core/lib/types/src/contract_verification_api.rs +++ b/core/lib/types/src/contract_verification_api.rs @@ -152,17 +152,19 @@ pub enum CompilerType { Vyper, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum CompilerVersions { #[serde(rename_all = "camelCase")] Solc { - compiler_zksolc_version: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + compiler_zksolc_version: Option, compiler_solc_version: String, }, #[serde(rename_all = "camelCase")] Vyper { - compiler_zkvyper_version: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + compiler_zkvyper_version: Option, compiler_vyper_version: String, }, } @@ -175,29 +177,29 @@ impl CompilerVersions { } } - pub fn zk_compiler_version(&self) -> String { + pub fn zk_compiler_version(&self) -> Option<&str> { match self { - CompilerVersions::Solc { + Self::Solc { compiler_zksolc_version, .. - } => compiler_zksolc_version.clone(), - CompilerVersions::Vyper { + } => compiler_zksolc_version.as_deref(), + Self::Vyper { compiler_zkvyper_version, .. - } => compiler_zkvyper_version.clone(), + } => compiler_zkvyper_version.as_deref(), } } - pub fn compiler_version(&self) -> String { + pub fn compiler_version(&self) -> &str { match self { - CompilerVersions::Solc { + Self::Solc { compiler_solc_version, .. - } => compiler_solc_version.clone(), - CompilerVersions::Vyper { + } => compiler_solc_version, + Self::Vyper { compiler_vyper_version, .. - } => compiler_vyper_version.clone(), + } => compiler_vyper_version, } } } @@ -213,10 +215,21 @@ pub struct VerificationRequest { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CompilationArtifacts { + /// In case of EVM contracts, this is the creation bytecode (`bytecode` in `solc` output). pub bytecode: Vec, + /// Deployed bytecode (`deployedBytecode` in `solc` output). Only set for EVM contracts; for EraVM contracts, the deployed bytecode + /// is always `bytecode` (i.e., there's no distinction between creation and deployed bytecodes). + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deployed_bytecode: Option>, pub abi: serde_json::Value, } +impl CompilationArtifacts { + pub fn deployed_bytecode(&self) -> &[u8] { + self.deployed_bytecode.as_deref().unwrap_or(&self.bytecode) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct VerificationInfo { @@ -235,12 +248,6 @@ pub struct VerificationRequestStatus { pub compilation_errors: Option>, } -#[derive(Debug)] -pub enum DeployContractCalldata { - Deploy(Vec), - Ignore, -} - #[cfg(test)] mod tests { use assert_matches::assert_matches; diff --git a/core/lib/types/src/lib.rs b/core/lib/types/src/lib.rs index 69e6e42fd51c..320264f28f0a 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/utils/Cargo.toml b/core/lib/utils/Cargo.toml index b87b2ad98964..9b65ccdd29cb 100644 --- a/core/lib/utils/Cargo.toml +++ b/core/lib/utils/Cargo.toml @@ -16,6 +16,7 @@ zk_evm.workspace = true zksync_vlog.workspace = true bigdecimal.workspace = true +const-decoder.workspace = true num = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["time"] } diff --git a/core/lib/utils/src/bytecode.rs b/core/lib/utils/src/bytecode.rs index 01cce5bc34d0..4fda5e9d48a0 100644 --- a/core/lib/utils/src/bytecode.rs +++ b/core/lib/utils/src/bytecode.rs @@ -1,7 +1,8 @@ // FIXME: move to basic_types? +use anyhow::Context as _; use zk_evm::k256::sha2::{Digest, Sha256}; -use zksync_basic_types::H256; +use zksync_basic_types::{H256, U256}; use crate::bytes_to_chunks; @@ -98,9 +99,62 @@ pub fn hash_evm_bytecode(bytecode: &[u8]) -> H256 { H256(output) } +pub fn prepare_evm_bytecode(raw: &[u8]) -> anyhow::Result<&[u8]> { + // EVM bytecodes are prefixed with a big-endian `U256` bytecode length. + let bytecode_len_bytes = raw.get(..32).context("length < 32")?; + let bytecode_len = U256::from_big_endian(bytecode_len_bytes); + let bytecode_len: usize = bytecode_len + .try_into() + .map_err(|_| anyhow::anyhow!("length ({bytecode_len}) overflow"))?; + let bytecode = raw.get(32..(32 + bytecode_len)).with_context(|| { + format!( + "prefixed length ({bytecode_len}) exceeds real length ({})", + raw.len() - 32 + ) + })?; + // Since slicing above succeeded, this one is safe. + let padding = &raw[(32 + bytecode_len)..]; + anyhow::ensure!( + padding.iter().all(|&b| b == 0), + "bytecode padding contains non-zero bytes" + ); + Ok(bytecode) +} + +pub mod testonly { + use const_decoder::Decoder; + + pub const RAW_EVM_BYTECODE: &[u8] = &const_decoder::decode!( + Decoder::Hex, + b"00000000000000000000000000000000000000000000000000000000000001266080604052348015\ + 600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063fb5343f314604c57\ + 5b5f80fd5b604a60048036038101906046919060a6565b6066565b005b6052606f565b604051605d\ + 919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd5b5f819050919050\ + 565b6088816078565b81146091575f80fd5b50565b5f8135905060a0816081565b92915050565b5f\ + 6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092915050565b60d381\ + 6078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056fea2646970667358\ + 221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9ce4d0964736f6c63\ + 4300081a00330000000000000000000000000000000000000000000000000000" + ); + pub const PROCESSED_EVM_BYTECODE: &[u8] = &const_decoder::decode!( + Decoder::Hex, + b"6080604052348015600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063\ + fb5343f314604c575b5f80fd5b604a60048036038101906046919060a6565b6066565b005b605260\ + 6f565b604051605d919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd\ + 5b5f819050919050565b6088816078565b81146091575f80fd5b50565b5f8135905060a081608156\ + 5b92915050565b5f6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092\ + 915050565b60d3816078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056\ + fea2646970667358221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9\ + ce4d0964736f6c634300081a0033" + ); +} + #[cfg(test)] mod tests { - use super::*; + use super::{ + testonly::{PROCESSED_EVM_BYTECODE, RAW_EVM_BYTECODE}, + *, + }; #[test] fn bytecode_markers_are_valid() { @@ -115,4 +169,10 @@ mod tests { Some(BytecodeMarker::Evm) ); } + + #[test] + fn preparing_evm_bytecode() { + let prepared = prepare_evm_bytecode(RAW_EVM_BYTECODE).unwrap(); + assert_eq!(prepared, PROCESSED_EVM_BYTECODE); + } } diff --git a/core/lib/vm_executor/src/batch/executor.rs b/core/lib/vm_executor/src/batch/executor.rs index 6dc9354fd7db..12b0718a4a56 100644 --- a/core/lib/vm_executor/src/batch/executor.rs +++ b/core/lib/vm_executor/src/batch/executor.rs @@ -99,11 +99,13 @@ where let elapsed = latency.observe(); if !res.tx_result.result.is_failed() { - let gas_per_nanosecond = - res.tx_result.statistics.computational_gas_used as f64 / elapsed.as_nanos() as f64; + let gas_used = res.tx_result.statistics.computational_gas_used; EXECUTOR_METRICS .computational_gas_per_nanosecond - .observe(gas_per_nanosecond); + .observe(gas_used as f64 / elapsed.as_nanos() as f64); + EXECUTOR_METRICS + .computational_gas_used + .observe(gas_used.into()); } else { // The amount of computational gas paid for failed transactions is hard to get // but comparing to the gas limit makes sense, since we can burn all gas @@ -111,6 +113,7 @@ where EXECUTOR_METRICS .failed_tx_gas_limit_per_nanosecond .observe(tx_gas_limit as f64 / elapsed.as_nanos() as f64); + EXECUTOR_METRICS.failed_tx_gas_limit.observe(tx_gas_limit); } Ok(res) } diff --git a/core/lib/vm_executor/src/batch/metrics.rs b/core/lib/vm_executor/src/batch/metrics.rs index 6851193e9be9..37f7997c31fd 100644 --- a/core/lib/vm_executor/src/batch/metrics.rs +++ b/core/lib/vm_executor/src/batch/metrics.rs @@ -21,6 +21,10 @@ const GAS_PER_NANOSECOND_BUCKETS: Buckets = Buckets::values(&[ 0.01, 0.03, 0.1, 0.3, 0.5, 0.75, 1., 1.5, 3., 5., 10., 20., 50., ]); +const GAS_USED_BUCKETS: Buckets = Buckets::values(&[ + 10000., 25000., 45000., 70000., 100000., 150000., 225000., 350000., 500000., +]); + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EncodeLabelValue, EncodeLabelSet)] #[metrics(label = "stage", rename_all = "snake_case")] pub(super) enum TxExecutionStage { @@ -37,8 +41,14 @@ pub(super) struct ExecutorMetrics { pub batch_executor_command_response_time: Family>, #[metrics(buckets = GAS_PER_NANOSECOND_BUCKETS)] pub computational_gas_per_nanosecond: Histogram, + /// Computational gas used, per transaction. + #[metrics(buckets = GAS_USED_BUCKETS)] + pub computational_gas_used: Histogram, #[metrics(buckets = GAS_PER_NANOSECOND_BUCKETS)] pub failed_tx_gas_limit_per_nanosecond: Histogram, + /// Gas limit, per failed transaction. + #[metrics(buckets = GAS_USED_BUCKETS)] + pub failed_tx_gas_limit: Histogram, /// Cumulative latency of interacting with the storage when executing a transaction /// in the batch executor. #[metrics(buckets = Buckets::LATENCIES)] diff --git a/core/lib/vm_executor/src/oneshot/mock.rs b/core/lib/vm_executor/src/oneshot/mock.rs index a7363c633c6c..89eaf3c75e29 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,19 +61,20 @@ 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, { Box::new( move |tx: &Transaction, env: &OneshotEnv| -> VmExecutionResultAndLogs { - VmExecutionResultAndLogs { - result: responses(tx, env), - logs: Default::default(), - statistics: Default::default(), - refunds: Default::default(), - new_known_factory_deps: None, - } + VmExecutionResultAndLogs::mock(responses(tx, env)) }, ) } @@ -82,11 +87,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 +110,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 +128,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 154c838f824f..7d45dcca8cd3 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, @@ -171,13 +171,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: FastVmMode::Old, panic_on_divergence: self.panic_on_divergence, @@ -188,11 +189,13 @@ where }; tokio::task::spawn_blocking(move || { - let (validation_tracer, mut validation_result) = - ValidationTracer::::new( - validation_params, - sandbox.env.system.version.into(), - ); + let validation_tracer = ValidationTracer::::new( + validation_params, + sandbox.env.system.version.into(), + l1_batch_env, + ); + let mut validation_result = validation_tracer.get_result(); + let validation_traces = validation_tracer.get_traces(); let tracers = vec![validation_tracer.into_tracer_pointer()]; let exec_result = sandbox.execute_in_vm(|vm, transaction| { @@ -209,7 +212,7 @@ where match (exec_result.result, validation_result) { (_, Err(violated_rule)) => Err(ValidationError::ViolatedRule(violated_rule)), (ExecutionResult::Halt { reason }, _) => Err(ValidationError::FailedTx(reason)), - _ => Ok(()), + _ => Ok(validation_traces.lock().unwrap().clone()), } }) .await diff --git a/core/lib/vm_interface/src/executor.rs b/core/lib/vm_interface/src/executor.rs index 60522ba338a2..30534b1420cf 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 88328e42b812..d24f55ab5044 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/outputs/execution_result.rs b/core/lib/vm_interface/src/types/outputs/execution_result.rs index 018ea075db51..9bb784fbf71c 100644 --- a/core/lib/vm_interface/src/types/outputs/execution_result.rs +++ b/core/lib/vm_interface/src/types/outputs/execution_result.rs @@ -21,10 +21,6 @@ const L1_MESSAGE_EVENT_SIGNATURE: H256 = H256([ 58, 54, 228, 114, 145, 244, 32, 31, 175, 19, 127, 171, 8, 29, 146, 41, 91, 206, 45, 83, 190, 44, 108, 166, 139, 168, 44, 127, 170, 156, 226, 65, ]); -const PUBLISHED_BYTECODE_SIGNATURE: H256 = H256([ - 201, 71, 34, 255, 19, 234, 207, 83, 84, 124, 71, 65, 218, 181, 34, 131, 83, 160, 89, 56, 255, - 205, 213, 212, 162, 213, 51, 174, 14, 97, 130, 135, -]); pub fn bytecode_len_in_bytes(bytecodehash: H256) -> usize { usize::from(u16::from_be_bytes([bytecodehash[2], bytecodehash[3]])) * 32 @@ -50,6 +46,11 @@ impl VmEvent { 72, 13, 60, 159, 114, 123, 94, 92, 18, 3, 212, 198, 31, 177, 133, 211, 127, 8, 230, 178, 220, 94, 155, 191, 152, 89, 27, 26, 122, 221, 245, 124, ]); + /// Long signature of the known bytecodes storage bytecode publication event (`MarkedAsKnown`). + pub const PUBLISHED_BYTECODE_SIGNATURE: H256 = H256([ + 201, 71, 34, 255, 19, 234, 207, 83, 84, 124, 71, 65, 218, 181, 34, 131, 83, 160, 89, 56, + 255, 205, 213, 212, 162, 213, 51, 174, 14, 97, 130, 135, + ]); /// Extracts all the "long" L2->L1 messages that were submitted by the L1Messenger contract. pub fn extract_long_l2_to_l1_messages(events: &[Self]) -> Vec> { @@ -79,12 +80,25 @@ impl VmEvent { // Filter events from the deployer contract that match the expected signature. event.address == KNOWN_CODES_STORAGE_ADDRESS && event.indexed_topics.len() == 3 - && event.indexed_topics[0] == PUBLISHED_BYTECODE_SIGNATURE + && event.indexed_topics[0] == Self::PUBLISHED_BYTECODE_SIGNATURE && event.indexed_topics[2] != H256::zero() }) .map(|event| event.indexed_topics[1]) .collect() } + + /// Extracts all bytecodes marked as known on the system contracts. + pub fn extract_bytecodes_marked_as_known(events: &[Self]) -> impl Iterator + '_ { + events + .iter() + .filter(|event| { + // Filter events from the deployer contract that match the expected signature. + event.address == KNOWN_CODES_STORAGE_ADDRESS + && event.indexed_topics.len() == 3 + && event.indexed_topics[0] == Self::PUBLISHED_BYTECODE_SIGNATURE + }) + .map(|event| event.indexed_topics[1]) + } } /// Refunds produced for the user. @@ -120,10 +134,10 @@ pub struct VmExecutionResultAndLogs { pub logs: VmExecutionLogs, pub statistics: VmExecutionStatistics, pub refunds: Refunds, - /// Bytecodes decommitted during VM execution. `None` if not computed by the VM. - // FIXME: currently, this is only filled up by `vm_latest`; probably makes sense to narrow down - // to *dynamic* factory deps, so that `HashMap::new()` is a valid value for VMs not supporting EVM emulation. - pub new_known_factory_deps: Option>>, + /// Dynamic bytecodes decommitted during VM execution (i.e., not present in the storage at the start of VM execution + /// or in `factory_deps` fields of executed transactions). Currently, the only kind of such codes are EVM bytecodes. + /// Correspondingly, they may only be present if supported by the VM version, and if the VM is initialized with the EVM emulator base system contract. + pub dynamic_factory_deps: HashMap>, } #[derive(Debug, Clone, PartialEq)] @@ -144,6 +158,22 @@ impl ExecutionResult { } impl VmExecutionResultAndLogs { + /// Creates a mock full result based on the provided base result. + pub fn mock(result: ExecutionResult) -> Self { + Self { + result, + logs: VmExecutionLogs::default(), + statistics: VmExecutionStatistics::default(), + refunds: Refunds::default(), + dynamic_factory_deps: HashMap::new(), + } + } + + /// Creates a mock successful result with no payload. + pub fn mock_success() -> Self { + Self::mock(ExecutionResult::Success { output: vec![] }) + } + pub fn get_execution_metrics(&self, tx: Option<&Transaction>) -> VmExecutionMetrics { let contracts_deployed = tx .map(|tx| tx.execute.factory_deps.len() as u16) @@ -414,6 +444,6 @@ mod tests { "MarkedAsKnown", &[ethabi::ParamType::FixedBytes(32), ethabi::ParamType::Bool], ); - assert_eq!(PUBLISHED_BYTECODE_SIGNATURE, expected_signature); + assert_eq!(VmEvent::PUBLISHED_BYTECODE_SIGNATURE, expected_signature); } } diff --git a/core/lib/vm_interface/src/types/outputs/finished_l1batch.rs b/core/lib/vm_interface/src/types/outputs/finished_l1batch.rs index 8f7c1d4fb0d6..7e90d425ab15 100644 --- a/core/lib/vm_interface/src/types/outputs/finished_l1batch.rs +++ b/core/lib/vm_interface/src/types/outputs/finished_l1batch.rs @@ -1,7 +1,6 @@ use zksync_types::writes::StateDiffRecord; use super::{BootloaderMemory, CurrentExecutionState, VmExecutionResultAndLogs}; -use crate::{ExecutionResult, Refunds, VmExecutionLogs, VmExecutionStatistics}; /// State of the VM after the batch execution. #[derive(Debug, Clone)] @@ -21,13 +20,7 @@ pub struct FinishedL1Batch { impl FinishedL1Batch { pub fn mock() -> Self { FinishedL1Batch { - block_tip_execution_result: VmExecutionResultAndLogs { - result: ExecutionResult::Success { output: vec![] }, - logs: VmExecutionLogs::default(), - statistics: VmExecutionStatistics::default(), - refunds: Refunds::default(), - new_known_factory_deps: None, - }, + block_tip_execution_result: VmExecutionResultAndLogs::mock_success(), final_execution_state: CurrentExecutionState { events: vec![], deduplicated_storage_logs: vec![], diff --git a/core/lib/vm_interface/src/types/tracer.rs b/core/lib/vm_interface/src/types/tracer.rs index ba07772c7f23..1c3f65f443ef 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)] +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 060c04298547..0883971f4de8 100644 --- a/core/lib/vm_interface/src/utils/shadow.rs +++ b/core/lib/vm_interface/src/utils/shadow.rs @@ -190,15 +190,13 @@ impl CheckDivergence for VmExecutionResultAndLogs { &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); - } - + // Order deps to have a more reasonable diff on a mismatch + let these_deps = self.dynamic_factory_deps.iter().collect::>(); + let other_deps = other + .dynamic_factory_deps + .iter() + .collect::>(); + errors.check_match("dynamic_factory_deps", &these_deps, &other_deps); errors } } diff --git a/core/lib/web3_decl/src/namespaces/zks.rs b/core/lib/web3_decl/src/namespaces/zks.rs index 47aae2a0835e..07a7cc4ff1c2 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 eb2170bcc848..5faef68507fa 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/Cargo.toml b/core/node/api_server/Cargo.toml index 067b9b3e3722..d0723a9d23e7 100644 --- a/core/node/api_server/Cargo.toml +++ b/core/node/api_server/Cargo.toml @@ -61,5 +61,4 @@ zksync_node_genesis.workspace = true zksync_node_test_utils.workspace = true assert_matches.workspace = true -const-decoder.workspace = true test-casing.workspace = true diff --git a/core/node/api_server/src/execution_sandbox/error.rs b/core/node/api_server/src/execution_sandbox/error.rs index 5d63d50a3c85..4523412ae194 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 7958b5ed3c12..d58bf6ca38f3 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 e342f2d73de9..0aff15b973e0 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 758547abbd6e..3d58f807a89a 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/testonly.rs b/core/node/api_server/src/testonly.rs index 3add9c2f165c..de6501716125 100644 --- a/core/node/api_server/src/testonly.rs +++ b/core/node/api_server/src/testonly.rs @@ -2,7 +2,6 @@ use std::{collections::HashMap, iter}; -use const_decoder::Decoder; use zk_evm_1_5_0::zkevm_opcode_defs::decoding::{EncodingModeProduction, VmEncodingMode}; use zksync_contracts::{ eth_contract, get_loadnext_contract, load_contract, read_bytecode, @@ -27,30 +26,6 @@ use zksync_types::{ }; use zksync_utils::{address_to_u256, u256_to_h256}; -pub(crate) const RAW_EVM_BYTECODE: &[u8] = &const_decoder::decode!( - Decoder::Hex, - b"00000000000000000000000000000000000000000000000000000000000001266080604052348015\ - 600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063fb5343f314604c57\ - 5b5f80fd5b604a60048036038101906046919060a6565b6066565b005b6052606f565b604051605d\ - 919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd5b5f819050919050\ - 565b6088816078565b81146091575f80fd5b50565b5f8135905060a0816081565b92915050565b5f\ - 6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092915050565b60d381\ - 6078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056fea2646970667358\ - 221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9ce4d0964736f6c63\ - 4300081a00330000000000000000000000000000000000000000000000000000" -); -pub(crate) const PROCESSED_EVM_BYTECODE: &[u8] = &const_decoder::decode!( - Decoder::Hex, - b"6080604052348015600e575f80fd5b50600436106030575f3560e01c8063816898ff146034578063\ - fb5343f314604c575b5f80fd5b604a60048036038101906046919060a6565b6066565b005b605260\ - 6f565b604051605d919060d9565b60405180910390f35b805f8190555050565b5f5481565b5f80fd\ - 5b5f819050919050565b6088816078565b81146091575f80fd5b50565b5f8135905060a081608156\ - 5b92915050565b5f6020828403121560b85760b76074565b5b5f60c3848285016094565b91505092\ - 915050565b60d3816078565b82525050565b5f60208201905060ea5f83018460cc565b9291505056\ - fea2646970667358221220caca1247066da378f2ec77c310f2ae51576272367b4fa11cc4350af4e9\ - ce4d0964736f6c634300081a0033" -); - const EXPENSIVE_CONTRACT_PATH: &str = "etc/contracts-test-data/artifacts-zk/contracts/expensive/expensive.sol/Expensive.json"; const PRECOMPILES_CONTRACT_PATH: &str = 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 736edf0b2475..06333f0c1369 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 75cc1ad602f8..011d9e4e2b2f 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 536a9767c1f2..bba462404cf0 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 e2a51ae8e9a8..cbc55a73c7ce 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 ea3f77fbcd82..cbe405b2aa63 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 fdd63254cf07..c861f04a832e 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 3d764816fe0d..1a6a7a733ccd 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/utils.rs b/core/node/api_server/src/utils.rs index c7a1134682bf..6769e773dc77 100644 --- a/core/node/api_server/src/utils.rs +++ b/core/node/api_server/src/utils.rs @@ -6,33 +6,9 @@ use std::{ time::{Duration, Instant}, }; -use anyhow::Context; use zksync_dal::{Connection, Core, DalError}; -use zksync_multivm::circuit_sequencer_api_latest::boojum::ethereum_types::U256; use zksync_web3_decl::error::Web3Error; -pub(crate) fn prepare_evm_bytecode(raw: &[u8]) -> anyhow::Result> { - // EVM bytecodes are prefixed with a big-endian `U256` bytecode length. - let bytecode_len_bytes = raw.get(..32).context("length < 32")?; - let bytecode_len = U256::from_big_endian(bytecode_len_bytes); - let bytecode_len: usize = bytecode_len - .try_into() - .map_err(|_| anyhow::anyhow!("length ({bytecode_len}) overflow"))?; - let bytecode = raw.get(32..(32 + bytecode_len)).with_context(|| { - format!( - "prefixed length ({bytecode_len}) exceeds real length ({})", - raw.len() - 32 - ) - })?; - // Since slicing above succeeded, this one is safe. - let padding = &raw[(32 + bytecode_len)..]; - anyhow::ensure!( - padding.iter().all(|&b| b == 0), - "bytecode padding contains non-zero bytes" - ); - Ok(bytecode.to_vec()) -} - /// Opens a readonly transaction over the specified connection. pub(crate) async fn open_readonly_transaction<'r>( conn: &'r mut Connection<'_, Core>, @@ -90,15 +66,3 @@ macro_rules! report_filter { ReportFilter::new($interval, &LAST_TIMESTAMP) }}; } - -#[cfg(test)] -mod tests { - use super::*; - use crate::testonly::{PROCESSED_EVM_BYTECODE, RAW_EVM_BYTECODE}; - - #[test] - fn preparing_evm_bytecode() { - let prepared = prepare_evm_bytecode(RAW_EVM_BYTECODE).unwrap(); - assert_eq!(prepared, PROCESSED_EVM_BYTECODE); - } -} 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 31c8f15bb1ea..21f3f5ae49e1 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/eth.rs b/core/node/api_server/src/web3/namespaces/eth.rs index ee37cb989f1b..e594af20d183 100644 --- a/core/node/api_server/src/web3/namespaces/eth.rs +++ b/core/node/api_server/src/web3/namespaces/eth.rs @@ -12,7 +12,10 @@ use zksync_types::{ web3::{self, Bytes, SyncInfo, SyncState}, AccountTreeId, L2BlockNumber, StorageKey, H256, L2_BASE_TOKEN_ADDRESS, U256, }; -use zksync_utils::{bytecode::BytecodeMarker, u256_to_h256}; +use zksync_utils::{ + bytecode::{prepare_evm_bytecode, BytecodeMarker}, + u256_to_h256, +}; use zksync_web3_decl::{ error::Web3Error, types::{Address, Block, Filter, FilterChanges, Log, U64}, @@ -21,7 +24,7 @@ use zksync_web3_decl::{ use crate::{ execution_sandbox::BlockArgs, tx_sender::BinarySearchKind, - utils::{open_readonly_transaction, prepare_evm_bytecode}, + utils::open_readonly_transaction, web3::{backend_jsonrpsee::MethodTracer, metrics::API_METRICS, state::RpcState, TypedFilter}, }; @@ -403,12 +406,14 @@ impl EthNamespace { // Check if the bytecode is an EVM bytecode, and if so, pre-process it correspondingly. let marker = BytecodeMarker::new(contract_code.bytecode_hash); let prepared_bytecode = if marker == Some(BytecodeMarker::Evm) { - prepare_evm_bytecode(&contract_code.bytecode).with_context(|| { - format!( - "malformed EVM bytecode at address {address:?}, hash = {:?}", - contract_code.bytecode_hash - ) - })? + prepare_evm_bytecode(&contract_code.bytecode) + .with_context(|| { + format!( + "malformed EVM bytecode at address {address:?}, hash = {:?}", + contract_code.bytecode_hash + ) + })? + .to_vec() } else { contract_code.bytecode }; diff --git a/core/node/api_server/src/web3/namespaces/zks.rs b/core/node/api_server/src/web3/namespaces/zks.rs index bcfd7daf3461..1a4114bd2c6a 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 a2aee8c7420a..d43771811ee0 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 2d642b9a04b8..540ea085711b 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 d8080f1dba59..b35bb9f5fad7 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::{ @@ -45,7 +45,10 @@ use zksync_types::{ U256, U64, }; use zksync_utils::{ - bytecode::{hash_bytecode, hash_evm_bytecode}, + bytecode::{ + hash_bytecode, hash_evm_bytecode, + testonly::{PROCESSED_EVM_BYTECODE, RAW_EVM_BYTECODE}, + }, u256_to_h256, }; use zksync_vm_executor::oneshot::MockOneshotExecutor; @@ -64,11 +67,7 @@ use zksync_web3_decl::{ }; use super::*; -use crate::{ - testonly::{PROCESSED_EVM_BYTECODE, RAW_EVM_BYTECODE}, - tx_sender::SandboxExecutorOptions, - web3::testonly::TestServerBuilder, -}; +use crate::{tx_sender::SandboxExecutorOptions, web3::testonly::TestServerBuilder}; mod debug; mod filters; @@ -364,7 +363,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 +774,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 +858,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/api_server/src/web3/tests/vm.rs b/core/node/api_server/src/web3/tests/vm.rs index 7dd0164198a1..4e0426de7bfa 100644 --- a/core/node/api_server/src/web3/tests/vm.rs +++ b/core/node/api_server/src/web3/tests/vm.rs @@ -638,11 +638,8 @@ impl HttpTest for SendTransactionWithDetailedOutputTest { assert_eq!(env.l1_batch.first_l2_block.number, 1); VmExecutionResultAndLogs { - result: ExecutionResult::Success { output: vec![] }, logs: vm_execution_logs.clone(), - statistics: Default::default(), - refunds: Default::default(), - new_known_factory_deps: None, + ..VmExecutionResultAndLogs::mock_success() } }); tx_executor diff --git a/core/node/consensus/src/storage/testonly.rs b/core/node/consensus/src/storage/testonly.rs index 2aed011d23cf..0f29e2468267 100644 --- a/core/node/consensus/src/storage/testonly.rs +++ b/core/node/consensus/src/storage/testonly.rs @@ -73,7 +73,8 @@ impl ConnectionPool { L1BatchNumber(23), L2BlockNumber(87), vec![], - mock_genesis_params(protocol_version), + &BaseSystemContracts::load_from_disk(), + protocol_version, )) .await } diff --git a/core/node/contract_verification_server/Cargo.toml b/core/node/contract_verification_server/Cargo.toml index eeb2c7828467..038347debc64 100644 --- a/core/node/contract_verification_server/Cargo.toml +++ b/core/node/contract_verification_server/Cargo.toml @@ -11,9 +11,9 @@ keywords.workspace = true categories.workspace = true [dependencies] -zksync_config.workspace = true zksync_dal.workspace = true zksync_types.workspace = true +zksync_utils.workspace = true vise.workspace = true anyhow.workspace = true @@ -21,5 +21,11 @@ axum.workspace = true tokio = { workspace = true, features = ["time"] } tower-http = { workspace = true, features = ["cors"] } tracing.workspace = true -serde.workspace = true + +[dev-dependencies] +zksync_node_test_utils.workspace = true + +http-body-util.workspace = true serde_json.workspace = true +test-casing.workspace = true +tower.workspace = true diff --git a/core/node/contract_verification_server/src/api_decl.rs b/core/node/contract_verification_server/src/api_decl.rs index 256062936d32..d451cd79add9 100644 --- a/core/node/contract_verification_server/src/api_decl.rs +++ b/core/node/contract_verification_server/src/api_decl.rs @@ -3,10 +3,13 @@ use std::sync::Arc; use tower_http::cors::CorsLayer; use zksync_dal::{ConnectionPool, Core}; +use crate::cache::SupportedCompilersCache; + #[derive(Debug, Clone)] -pub struct RestApi { - pub(super) master_connection_pool: ConnectionPool, - pub(super) replica_connection_pool: ConnectionPool, +pub(crate) struct RestApi { + pub(crate) master_connection_pool: ConnectionPool, + pub(crate) replica_connection_pool: ConnectionPool, + pub(crate) supported_compilers: Arc, } impl RestApi { @@ -14,7 +17,9 @@ impl RestApi { master_connection_pool: ConnectionPool, replica_connection_pool: ConnectionPool, ) -> Self { + let supported_compilers = SupportedCompilersCache::new(replica_connection_pool.clone()); Self { + supported_compilers: Arc::new(supported_compilers), master_connection_pool, replica_connection_pool, } diff --git a/core/node/contract_verification_server/src/api_impl.rs b/core/node/contract_verification_server/src/api_impl.rs index b8111e98a1cc..94be65673bad 100644 --- a/core/node/contract_verification_server/src/api_impl.rs +++ b/core/node/contract_verification_server/src/api_impl.rs @@ -1,195 +1,234 @@ -use std::sync::Arc; +use std::{collections::HashSet, iter, sync::Arc}; +use anyhow::Context as _; use axum::{ extract::{Path, State}, - response::Response, + http::StatusCode, + response::{IntoResponse, Response}, Json, }; -use serde::Serialize; -use zksync_dal::CoreDal; -use zksync_types::{contract_verification_api::VerificationIncomingRequest, Address}; +use zksync_dal::{CoreDal, DalError}; +use zksync_types::{ + contract_verification_api::{ + CompilerVersions, VerificationIncomingRequest, VerificationInfo, VerificationRequestStatus, + }, + Address, +}; +use zksync_utils::bytecode::BytecodeMarker; use super::{api_decl::RestApi, metrics::METRICS}; -fn ok_json(data: impl Serialize) -> Response { - Response::builder() - .status(axum::http::StatusCode::OK) - .body(serde_json::to_string(&data).expect("Failed to serialize")) - .unwrap() +#[derive(Debug)] +pub(crate) enum ApiError { + IncorrectCompilerVersions, + UnsupportedCompilerVersions, + MissingZkCompilerVersion, + BogusZkCompilerVersion, + NoDeployedContract, + RequestNotFound, + VerificationInfoNotFound, + Internal(anyhow::Error), +} + +impl From for ApiError { + fn from(err: anyhow::Error) -> Self { + Self::Internal(err) + } +} + +impl From for ApiError { + fn from(err: DalError) -> Self { + Self::Internal(err.generalize()) + } } -fn bad_request(message: &str) -> Response { - Response::builder() - .status(axum::http::StatusCode::BAD_REQUEST) - .body(message.to_string()) - .unwrap() +impl ApiError { + pub fn message(&self) -> &'static str { + match self { + Self::IncorrectCompilerVersions => "incorrect compiler versions", + Self::UnsupportedCompilerVersions => "unsupported compiler versions", + Self::MissingZkCompilerVersion => "missing zk compiler version for EraVM bytecode", + Self::BogusZkCompilerVersion => "zk compiler version specified for EVM bytecode", + Self::NoDeployedContract => "There is no deployed contract on this address", + Self::RequestNotFound => "request not found", + Self::VerificationInfoNotFound => "verification info not found for address", + Self::Internal(_) => "internal server error", + } + } } -fn not_found() -> Response { - Response::builder() - .status(axum::http::StatusCode::NOT_FOUND) - .body(String::new()) - .unwrap() +impl IntoResponse for ApiError { + fn into_response(self) -> Response { + let status_code = match &self { + Self::IncorrectCompilerVersions + | Self::UnsupportedCompilerVersions + | Self::MissingZkCompilerVersion + | Self::BogusZkCompilerVersion + | Self::NoDeployedContract => StatusCode::BAD_REQUEST, + + Self::RequestNotFound | Self::VerificationInfoNotFound => StatusCode::NOT_FOUND, + + Self::Internal(err) => { + // Do not expose the error details to the client, but log it. + tracing::warn!("Internal error: {err:#}"); + StatusCode::INTERNAL_SERVER_ERROR + } + }; + (status_code, self.message()).into_response() + } } +type ApiResult = Result, ApiError>; + impl RestApi { #[tracing::instrument(skip(query))] fn validate_contract_verification_query( query: &VerificationIncomingRequest, - ) -> Result<(), Response> { + ) -> Result<(), ApiError> { if query.source_code_data.compiler_type() != query.compiler_versions.compiler_type() { - return Err(bad_request("incorrect compiler versions")); + return Err(ApiError::IncorrectCompilerVersions); } - Ok(()) } + fn validate_compilers( + versions: &CompilerVersions, + bytecode_kind: BytecodeMarker, + ) -> Result<(), ApiError> { + match bytecode_kind { + BytecodeMarker::EraVm if versions.zk_compiler_version().is_none() => { + Err(ApiError::MissingZkCompilerVersion) + } + BytecodeMarker::Evm if versions.zk_compiler_version().is_some() => { + Err(ApiError::BogusZkCompilerVersion) + } + _ => Ok(()), + } + } + /// Add a contract verification job to the queue if the requested contract wasn't previously verified. + // FIXME: this doesn't seem to check that the contract isn't verified; should it? #[tracing::instrument(skip(self_, request))] pub async fn verification( State(self_): State>, Json(request): Json, - ) -> Response { + ) -> ApiResult { let method_latency = METRICS.call[&"contract_verification"].start(); - if let Err(res) = Self::validate_contract_verification_query(&request) { - return res; + Self::validate_contract_verification_query(&request)?; + + let is_compilation_supported = self_ + .supported_compilers + .get(|supported| supported.contain(&request.compiler_versions)) + .await?; + if !is_compilation_supported { + return Err(ApiError::UnsupportedCompilerVersions); } + let mut storage = self_ .master_connection_pool .connection_tagged("api") - .await - .unwrap(); - - if !storage + .await?; + let deployment_info = storage .storage_logs_dal() - .is_contract_deployed_at_address(request.contract_address) - .await - { - return bad_request("There is no deployed contract on this address"); - } + .filter_deployed_contracts(iter::once(request.contract_address), None) + .await?; + let &(_, bytecode_hash) = deployment_info + .get(&request.contract_address) + .ok_or(ApiError::NoDeployedContract)?; + let bytecode_marker = BytecodeMarker::new(bytecode_hash).with_context(|| { + format!( + "unknown bytecode marker for bytecode hash {bytecode_hash:?} at address {:?}", + request.contract_address + ) + })?; + Self::validate_compilers(&request.compiler_versions, bytecode_marker)?; let request_id = storage .contract_verification_dal() - .add_contract_verification_request(request) - .await - .unwrap(); - + .add_contract_verification_request(&request) + .await?; method_latency.observe(); - ok_json(request_id) + Ok(Json(request_id)) } #[tracing::instrument(skip(self_))] pub async fn verification_request_status( State(self_): State>, id: Path, - ) -> Response { + ) -> ApiResult { let method_latency = METRICS.call[&"contract_verification_request_status"].start(); let status = self_ .replica_connection_pool .connection_tagged("api") - .await - .unwrap() + .await? .contract_verification_dal() .get_verification_request_status(*id) - .await - .unwrap(); + .await? + .ok_or(ApiError::RequestNotFound)?; method_latency.observe(); - match status { - Some(status) => ok_json(status), - None => not_found(), - } + Ok(Json(status)) } #[tracing::instrument(skip(self_))] - pub async fn zksolc_versions(State(self_): State>) -> Response { + pub async fn zksolc_versions(State(self_): State>) -> ApiResult> { let method_latency = METRICS.call[&"contract_verification_zksolc_versions"].start(); let versions = self_ - .replica_connection_pool - .connection_tagged("api") - .await - .unwrap() - .contract_verification_dal() - .get_zksolc_versions() - .await - .unwrap(); - + .supported_compilers + .get(|supported| supported.zksolc.clone()) + .await?; method_latency.observe(); - ok_json(versions) + Ok(Json(versions)) } #[tracing::instrument(skip(self_))] - pub async fn solc_versions(State(self_): State>) -> Response { + pub async fn solc_versions(State(self_): State>) -> ApiResult> { let method_latency = METRICS.call[&"contract_verification_solc_versions"].start(); let versions = self_ - .replica_connection_pool - .connection_tagged("api") - .await - .unwrap() - .contract_verification_dal() - .get_solc_versions() - .await - .unwrap(); - + .supported_compilers + .get(|supported| supported.solc.clone()) + .await?; method_latency.observe(); - ok_json(versions) + Ok(Json(versions)) } #[tracing::instrument(skip(self_))] - pub async fn zkvyper_versions(State(self_): State>) -> Response { + pub async fn zkvyper_versions(State(self_): State>) -> ApiResult> { let method_latency = METRICS.call[&"contract_verification_zkvyper_versions"].start(); let versions = self_ - .replica_connection_pool - .connection_tagged("api") - .await - .unwrap() - .contract_verification_dal() - .get_zkvyper_versions() - .await - .unwrap(); - + .supported_compilers + .get(|supported| supported.zkvyper.clone()) + .await?; method_latency.observe(); - ok_json(versions) + Ok(Json(versions)) } #[tracing::instrument(skip(self_))] - pub async fn vyper_versions(State(self_): State>) -> Response { + pub async fn vyper_versions(State(self_): State>) -> ApiResult> { let method_latency = METRICS.call[&"contract_verification_vyper_versions"].start(); let versions = self_ - .replica_connection_pool - .connection_tagged("api") - .await - .unwrap() - .contract_verification_dal() - .get_vyper_versions() - .await - .unwrap(); - + .supported_compilers + .get(|supported| supported.vyper.clone()) + .await?; method_latency.observe(); - ok_json(versions) + Ok(Json(versions)) } #[tracing::instrument(skip(self_))] pub async fn verification_info( State(self_): State>, address: Path
, - ) -> Response { + ) -> ApiResult { let method_latency = METRICS.call[&"contract_verification_info"].start(); - let info = self_ .replica_connection_pool .connection_tagged("api") - .await - .unwrap() + .await? .contract_verification_dal() .get_contract_verification_info(*address) - .await - .unwrap(); - + .await? + .ok_or(ApiError::VerificationInfoNotFound)?; method_latency.observe(); - match info { - Some(info) => ok_json(info), - None => not_found(), - } + Ok(Json(info)) } } diff --git a/core/node/contract_verification_server/src/cache.rs b/core/node/contract_verification_server/src/cache.rs new file mode 100644 index 000000000000..c8e367515287 --- /dev/null +++ b/core/node/contract_verification_server/src/cache.rs @@ -0,0 +1,122 @@ +use std::{ + collections::HashSet, + time::{Duration, Instant}, +}; + +use tokio::sync::RwLock; +use zksync_dal::{Connection, ConnectionPool, Core, CoreDal, DalError}; +use zksync_types::contract_verification_api::CompilerVersions; + +/// Compiler versions supported by the contract verifier. +#[derive(Debug, Clone)] +pub(crate) struct SupportedCompilerVersions { + pub solc: HashSet, + pub zksolc: HashSet, + pub vyper: HashSet, + pub zkvyper: HashSet, +} + +impl SupportedCompilerVersions { + /// Checks whether the supported compilers include ones specified in a request. + pub fn contain(&self, versions: &CompilerVersions) -> bool { + match versions { + CompilerVersions::Solc { + compiler_solc_version, + compiler_zksolc_version, + } => { + self.solc.contains(compiler_solc_version) + && compiler_zksolc_version + .as_ref() + .map_or(true, |ver| self.zksolc.contains(ver)) + } + CompilerVersions::Vyper { + compiler_vyper_version, + compiler_zkvyper_version, + } => { + self.vyper.contains(compiler_vyper_version) + && compiler_zkvyper_version + .as_ref() + .map_or(true, |ver| self.zkvyper.contains(ver)) + } + } + } +} + +impl SupportedCompilerVersions { + async fn new(connection: &mut Connection<'_, Core>) -> Result { + let solc = connection + .contract_verification_dal() + .get_solc_versions() + .await?; + let zksolc = connection + .contract_verification_dal() + .get_zksolc_versions() + .await?; + let vyper = connection + .contract_verification_dal() + .get_vyper_versions() + .await?; + let zkvyper = connection + .contract_verification_dal() + .get_zkvyper_versions() + .await?; + Ok(Self { + solc: solc.into_iter().collect(), + zksolc: zksolc.into_iter().collect(), + vyper: vyper.into_iter().collect(), + zkvyper: zkvyper.into_iter().collect(), + }) + } +} + +/// Cache for compiler versions supported by the contract verifier. +#[derive(Debug)] +pub(crate) struct SupportedCompilersCache { + connection_pool: ConnectionPool, + inner: RwLock>, +} + +impl SupportedCompilersCache { + const CACHE_UPDATE_INTERVAL: Duration = Duration::from_secs(10); + + pub fn new(connection_pool: ConnectionPool) -> Self { + Self { + connection_pool, + inner: RwLock::new(None), + } + } + + fn get_cached( + cache: Option<&(SupportedCompilerVersions, Instant)>, + action: impl FnOnce(&SupportedCompilerVersions) -> R, + ) -> Option { + cache.and_then(|(versions, updated_at)| { + (updated_at.elapsed() <= Self::CACHE_UPDATE_INTERVAL).then(|| action(versions)) + }) + } + + pub async fn get( + &self, + action: impl Fn(&SupportedCompilerVersions) -> R, + ) -> Result { + let output = Self::get_cached(self.inner.read().await.as_ref(), &action); + if let Some(output) = output { + return Ok(output); + } + + // We don't want to hold an exclusive lock while querying Postgres. + let supported = { + let mut connection = self.connection_pool.connection_tagged("api").await?; + let mut db_transaction = connection + .transaction_builder()? + .set_readonly() + .build() + .await?; + SupportedCompilerVersions::new(&mut db_transaction).await? + }; + let output = action(&supported); + // Another task may have written to the cache already, but we should be fine with updating it again. + *self.inner.write().await = Some((supported, Instant::now())); + Ok(output) + } +} diff --git a/core/node/contract_verification_server/src/lib.rs b/core/node/contract_verification_server/src/lib.rs index eea45f8564bf..912cec55f0b8 100644 --- a/core/node/contract_verification_server/src/lib.rs +++ b/core/node/contract_verification_server/src/lib.rs @@ -1,21 +1,24 @@ +use std::net::SocketAddr; + use anyhow::Context as _; use tokio::sync::watch; -use zksync_config::ContractVerifierConfig; use zksync_dal::ConnectionPool; use self::api_decl::RestApi; mod api_decl; mod api_impl; +mod cache; mod metrics; +#[cfg(test)] +mod tests; pub async fn start_server( master_connection_pool: ConnectionPool, replica_connection_pool: ConnectionPool, - config: ContractVerifierConfig, + bind_address: SocketAddr, mut stop_receiver: watch::Receiver, ) -> anyhow::Result<()> { - let bind_address = config.bind_addr(); let api = RestApi::new(master_connection_pool, replica_connection_pool).into_router(); let listener = tokio::net::TcpListener::bind(bind_address) diff --git a/core/node/contract_verification_server/src/tests.rs b/core/node/contract_verification_server/src/tests.rs new file mode 100644 index 000000000000..b7b0d3e8efb4 --- /dev/null +++ b/core/node/contract_verification_server/src/tests.rs @@ -0,0 +1,356 @@ +//! Tests for contract verification API server. + +use std::{str, time::Duration}; + +use axum::{ + body::Body, + http::{header, Method, Request, Response, StatusCode}, +}; +use http_body_util::BodyExt as _; +use test_casing::test_casing; +use tower::ServiceExt; +use zksync_dal::{Connection, Core, CoreDal}; +use zksync_node_test_utils::create_l2_block; +use zksync_types::{ + contract_verification_api::CompilerVersions, get_code_key, Address, L2BlockNumber, + ProtocolVersion, StorageLog, +}; +use zksync_utils::bytecode::{hash_bytecode, hash_evm_bytecode, BytecodeMarker}; + +use super::*; +use crate::api_impl::ApiError; + +const SOLC_VERSION: &str = "0.8.27"; +const ZKSOLC_VERSION: &str = "1.5.6"; + +async fn prepare_storage(storage: &mut Connection<'_, Core>) { + storage + .protocol_versions_dal() + .save_protocol_version_with_tx(&ProtocolVersion::default()) + .await + .unwrap(); + storage + .blocks_dal() + .insert_l2_block(&create_l2_block(0)) + .await + .unwrap(); + + storage + .contract_verification_dal() + .set_solc_versions(&[SOLC_VERSION.to_owned()]) + .await + .unwrap(); + storage + .contract_verification_dal() + .set_zksolc_versions(&[ZKSOLC_VERSION.to_owned()]) + .await + .unwrap(); +} + +async fn mock_deploy_contract( + storage: &mut Connection<'_, Core>, + address: Address, + kind: BytecodeMarker, +) { + let bytecode_hash = match kind { + BytecodeMarker::EraVm => hash_bytecode(&[0; 32]), + BytecodeMarker::Evm => hash_evm_bytecode(&[0; 96]), + }; + let deploy_log = StorageLog::new_write_log(get_code_key(&address), bytecode_hash); + storage + .storage_logs_dal() + .append_storage_logs(L2BlockNumber(0), &[deploy_log]) + .await + .unwrap() +} + +fn post_request(body: &serde_json::Value) -> Request { + Request::builder() + .method(Method::POST) + .uri("/contract_verification") + .header(header::CONTENT_TYPE, "application/json") + .body(Body::from(serde_json::to_vec(body).unwrap())) + .unwrap() +} + +async fn json_response(response: Response) -> serde_json::Value { + assert_eq!(response.status(), StatusCode::OK); + assert_eq!( + response.headers().get(header::CONTENT_TYPE).unwrap(), + "application/json" + ); + let response = response.into_body(); + let response = response.collect().await.unwrap().to_bytes(); + serde_json::from_slice(&response).unwrap() +} + +#[tokio::test] +async fn getting_compiler_versions() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let router = RestApi::new(pool.clone(), pool).into_router(); + let req = Request::builder() + .method(Method::GET) + .uri("/contract_verification/zksolc_versions") + .body(Body::empty()) + .unwrap(); + let response = router.clone().oneshot(req).await.unwrap(); + let versions = json_response(response).await; + assert_eq!(versions, serde_json::json!([ZKSOLC_VERSION])); + + let req = Request::builder() + .method(Method::GET) + .uri("/contract_verification/solc_versions") + .body(Body::empty()) + .unwrap(); + let response = router.oneshot(req).await.unwrap(); + let versions = json_response(response).await; + assert_eq!(versions, serde_json::json!([SOLC_VERSION])); +} + +#[test_casing(2, [BytecodeMarker::EraVm, BytecodeMarker::Evm])] +#[tokio::test] +async fn submitting_request(bytecode_kind: BytecodeMarker) { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let address = Address::repeat_byte(0x23); + let verification_request = serde_json::json!({ + "contractAddress": address, + "sourceCode": "contract Test {}", + "contractName": "Test", + "compilerZksolcVersion": match bytecode_kind { + BytecodeMarker::EraVm => Some(ZKSOLC_VERSION), + BytecodeMarker::Evm => None, + }, + "compilerSolcVersion": SOLC_VERSION, + "optimizationUsed": true, + }); + + let router = RestApi::new(pool.clone(), pool).into_router(); + let response = router + .clone() + .oneshot(post_request(&verification_request)) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); // the address is not deployed to + let error_message = response.collect().await.unwrap().to_bytes(); + let error_message = str::from_utf8(&error_message).unwrap(); + assert_eq!(error_message, ApiError::NoDeployedContract.message()); + + mock_deploy_contract(&mut storage, address, bytecode_kind).await; + + let response = router + .clone() + .oneshot(post_request(&verification_request)) + .await + .unwrap(); + let id = json_response(response).await; + assert_eq!(id, serde_json::json!(1)); + + let request = storage + .contract_verification_dal() + .get_next_queued_verification_request(Duration::from_secs(600)) + .await + .unwrap() + .expect("request not persisted"); + assert_eq!(request.id, 1); + assert_eq!(request.req.contract_address, address); + assert_eq!( + request.req.compiler_versions, + CompilerVersions::Solc { + compiler_zksolc_version: match bytecode_kind { + BytecodeMarker::EraVm => Some(ZKSOLC_VERSION.to_owned()), + BytecodeMarker::Evm => None, + }, + compiler_solc_version: SOLC_VERSION.to_owned(), + } + ); + assert_eq!(request.req.contract_name, "Test"); + assert!(request.req.optimization_used); + + let req = Request::builder() + .method(Method::GET) + .uri("/contract_verification/1") + .body(Body::empty()) + .unwrap(); + let response = router.oneshot(req).await.unwrap(); + let request_status = json_response(response).await; + assert_eq!(request_status["status"], "in_progress"); +} + +#[test_casing(2, [BytecodeMarker::EraVm, BytecodeMarker::Evm])] +#[tokio::test] +async fn submitting_request_with_invalid_compiler_type(bytecode_kind: BytecodeMarker) { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let address = Address::repeat_byte(0x23); + mock_deploy_contract(&mut storage, address, bytecode_kind).await; + + let verification_request = serde_json::json!({ + "contractAddress": address, + "sourceCode": "contract Test {}", + "contractName": "Test", + // Intentionally incorrect versions "shape" + "compilerZksolcVersion": match bytecode_kind { + BytecodeMarker::Evm => Some(ZKSOLC_VERSION), + BytecodeMarker::EraVm => None, + }, + "compilerSolcVersion": SOLC_VERSION, + "optimizationUsed": true, + }); + let router = RestApi::new(pool.clone(), pool).into_router(); + let response = router + .oneshot(post_request(&verification_request)) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let error_message = response.collect().await.unwrap().to_bytes(); + let error_message = str::from_utf8(&error_message).unwrap(); + let expected_message = match bytecode_kind { + BytecodeMarker::Evm => ApiError::BogusZkCompilerVersion.message(), + BytecodeMarker::EraVm => ApiError::MissingZkCompilerVersion.message(), + }; + assert_eq!(error_message, expected_message); +} + +#[test_casing(2, [BytecodeMarker::EraVm, BytecodeMarker::Evm])] +#[tokio::test] +async fn submitting_request_with_unsupported_solc(bytecode_kind: BytecodeMarker) { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let address = Address::repeat_byte(0x23); + mock_deploy_contract(&mut storage, address, bytecode_kind).await; + + let verification_request = serde_json::json!({ + "contractAddress": address, + "sourceCode": "contract Test {}", + "contractName": "Test", + "compilerZksolcVersion": match bytecode_kind { + BytecodeMarker::Evm => None, + BytecodeMarker::EraVm => Some(ZKSOLC_VERSION), + }, + "compilerSolcVersion": "1.0.0", + "optimizationUsed": true, + }); + let router = RestApi::new(pool.clone(), pool).into_router(); + let response = router + .oneshot(post_request(&verification_request)) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let error_message = response.collect().await.unwrap().to_bytes(); + let error_message = str::from_utf8(&error_message).unwrap(); + assert_eq!( + error_message, + ApiError::UnsupportedCompilerVersions.message() + ); +} + +#[tokio::test] +async fn submitting_request_with_unsupported_zksolc() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + + let address = Address::repeat_byte(0x23); + mock_deploy_contract(&mut storage, address, BytecodeMarker::EraVm).await; + + let verification_request = serde_json::json!({ + "contractAddress": address, + "sourceCode": "contract Test {}", + "contractName": "Test", + "compilerZksolcVersion": "1000.0.0", + "compilerSolcVersion": SOLC_VERSION, + "optimizationUsed": true, + }); + let router = RestApi::new(pool.clone(), pool).into_router(); + let response = router + .oneshot(post_request(&verification_request)) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let error_message = response.collect().await.unwrap().to_bytes(); + let error_message = str::from_utf8(&error_message).unwrap(); + assert_eq!( + error_message, + ApiError::UnsupportedCompilerVersions.message() + ); +} + +#[tokio::test] +async fn querying_missing_request() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + let router = RestApi::new(pool.clone(), pool).into_router(); + + let req = Request::builder() + .method(Method::GET) + .uri("/contract_verification/1") + .body(Body::empty()) + .unwrap(); + let response = router.oneshot(req).await.unwrap(); + + assert_eq!(response.status(), StatusCode::NOT_FOUND); + let error_message = response.collect().await.unwrap().to_bytes(); + let error_message = str::from_utf8(&error_message).unwrap(); + assert_eq!(error_message, ApiError::RequestNotFound.message()); +} + +#[tokio::test] +async fn querying_missing_verification_info() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + let router = RestApi::new(pool.clone(), pool).into_router(); + + let req = Request::builder() + .method(Method::GET) + .uri("/contract_verification/info/0x2323232323232323232323232323232323232323") + .body(Body::empty()) + .unwrap(); + let response = router.oneshot(req).await.unwrap(); + + assert_eq!(response.status(), StatusCode::NOT_FOUND); + let error_message = response.collect().await.unwrap().to_bytes(); + let error_message = str::from_utf8(&error_message).unwrap(); + assert_eq!(error_message, ApiError::VerificationInfoNotFound.message()); +} + +#[tokio::test] +async fn mismatched_compiler_type() { + let pool = ConnectionPool::test_pool().await; + let mut storage = pool.connection().await.unwrap(); + prepare_storage(&mut storage).await; + let address = Address::repeat_byte(0x23); + mock_deploy_contract(&mut storage, address, BytecodeMarker::EraVm).await; + + let verification_request = serde_json::json!({ + "contractAddress": address, + "sourceCode": "contract Test {}", + "contractName": "Test", + "compilerVyperVersion": "1.0.1", + "optimizationUsed": true, + }); + + let router = RestApi::new(pool.clone(), pool).into_router(); + let response = router + .oneshot(post_request(&verification_request)) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); + let error_message = response.collect().await.unwrap().to_bytes(); + let error_message = str::from_utf8(&error_message).unwrap(); + assert_eq!(error_message, ApiError::IncorrectCompilerVersions.message()); +} diff --git a/core/node/da_clients/Cargo.toml b/core/node/da_clients/Cargo.toml index e0c85b3030ab..7283277d148a 100644 --- a/core/node/da_clients/Cargo.toml +++ b/core/node/da_clients/Cargo.toml @@ -55,3 +55,14 @@ pbjson-types.workspace = true # Eigen dependencies tokio-stream.workspace = true +rlp.workspace = true +kzgpad-rs = { git = "https://github.com/Layr-Labs/kzgpad-rs.git", tag = "v0.1.0" } +rand.workspace = true +sha3.workspace = true +tiny-keccak.workspace = true +alloy = { version = "0.3", features = ["full"] } +ethabi = "16.0.0" +rust-kzg-bn254 = {git = "https://github.com/lambdaclass/rust-kzg-bn254", branch = "bump-ark"} +ark-bn254 = "0.5.0-alpha.0" +num-bigint = "0.4.6" +serial_test = "3.1.1" diff --git a/core/node/da_clients/src/eigen/blob_info.rs b/core/node/da_clients/src/eigen/blob_info.rs new file mode 100644 index 000000000000..658e3be284a8 --- /dev/null +++ b/core/node/da_clients/src/eigen/blob_info.rs @@ -0,0 +1,504 @@ +use std::fmt; + +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; + +use super::{ + common::G1Commitment as DisperserG1Commitment, + disperser::{ + BatchHeader as DisperserBatchHeader, BatchMetadata as DisperserBatchMetadata, + BlobHeader as DisperserBlobHeader, BlobInfo as DisperserBlobInfo, + BlobQuorumParam as DisperserBlobQuorumParam, + BlobVerificationProof as DisperserBlobVerificationProof, + }, +}; + +#[derive(Debug)] +pub enum ConversionError { + NotPresentError, +} + +impl fmt::Display for ConversionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ConversionError::NotPresentError => write!(f, "Failed to convert BlobInfo"), + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct G1Commitment { + pub x: Vec, + pub y: Vec, +} + +impl G1Commitment { + pub fn to_bytes(&self) -> Vec { + let mut bytes = vec![]; + bytes.extend(&self.x.len().to_be_bytes()); + bytes.extend(&self.x); + bytes.extend(&self.y.len().to_be_bytes()); + bytes.extend(&self.y); + + bytes + } +} + +impl Decodable for G1Commitment { + fn decode(rlp: &Rlp) -> Result { + let x: Vec = rlp.val_at(0)?; // Decode first element as Vec + let y: Vec = rlp.val_at(1)?; // Decode second element as Vec + + Ok(G1Commitment { x, y }) + } +} + +impl Encodable for G1Commitment { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.x); + s.append(&self.y); + } +} + +impl From for G1Commitment { + fn from(value: DisperserG1Commitment) -> Self { + Self { + x: value.x, + y: value.y, + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct BlobQuorumParam { + pub quorum_number: u32, + pub adversary_threshold_percentage: u32, + pub confirmation_threshold_percentage: u32, + pub chunk_length: u32, +} + +impl BlobQuorumParam { + pub fn to_bytes(&self) -> Vec { + let mut bytes = vec![]; + bytes.extend(&self.quorum_number.to_be_bytes()); + bytes.extend(&self.adversary_threshold_percentage.to_be_bytes()); + bytes.extend(&self.confirmation_threshold_percentage.to_be_bytes()); + bytes.extend(&self.chunk_length.to_be_bytes()); + + bytes + } +} + +impl Decodable for BlobQuorumParam { + fn decode(rlp: &Rlp) -> Result { + Ok(BlobQuorumParam { + quorum_number: rlp.val_at(0)?, + adversary_threshold_percentage: rlp.val_at(1)?, + confirmation_threshold_percentage: rlp.val_at(2)?, + chunk_length: rlp.val_at(3)?, + }) + } +} + +impl Encodable for BlobQuorumParam { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.quorum_number); + s.append(&self.adversary_threshold_percentage); + s.append(&self.confirmation_threshold_percentage); + s.append(&self.chunk_length); + } +} + +impl From for BlobQuorumParam { + fn from(value: DisperserBlobQuorumParam) -> Self { + Self { + quorum_number: value.quorum_number, + adversary_threshold_percentage: value.adversary_threshold_percentage, + confirmation_threshold_percentage: value.confirmation_threshold_percentage, + chunk_length: value.chunk_length, + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct BlobHeader { + pub commitment: G1Commitment, + pub data_length: u32, + pub blob_quorum_params: Vec, +} + +impl BlobHeader { + pub fn to_bytes(&self) -> Vec { + let mut bytes = vec![]; + bytes.extend(self.commitment.to_bytes()); + bytes.extend(&self.data_length.to_be_bytes()); + bytes.extend(&self.blob_quorum_params.len().to_be_bytes()); + + for quorum in &self.blob_quorum_params { + bytes.extend(quorum.to_bytes()); + } + + bytes + } +} + +impl Decodable for BlobHeader { + fn decode(rlp: &Rlp) -> Result { + let commitment: G1Commitment = rlp.val_at(0)?; + let data_length: u32 = rlp.val_at(1)?; + let blob_quorum_params: Vec = rlp.list_at(2)?; + + Ok(BlobHeader { + commitment, + data_length, + blob_quorum_params, + }) + } +} + +impl Encodable for BlobHeader { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3); + s.append(&self.commitment); + s.append(&self.data_length); + s.append_list(&self.blob_quorum_params); + } +} + +impl TryFrom for BlobHeader { + type Error = ConversionError; + fn try_from(value: DisperserBlobHeader) -> Result { + if value.commitment.is_none() { + return Err(ConversionError::NotPresentError); + } + let blob_quorum_params: Vec = value + .blob_quorum_params + .iter() + .map(|param| BlobQuorumParam::from(param.clone())) + .collect(); + Ok(Self { + commitment: G1Commitment::from(value.commitment.unwrap()), + data_length: value.data_length, + blob_quorum_params, + }) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct BatchHeader { + pub batch_root: Vec, + pub quorum_numbers: Vec, + pub quorum_signed_percentages: Vec, + pub reference_block_number: u32, +} + +impl BatchHeader { + pub fn to_bytes(&self) -> Vec { + let mut bytes = vec![]; + bytes.extend(&self.batch_root.len().to_be_bytes()); + bytes.extend(&self.batch_root); + bytes.extend(&self.quorum_numbers.len().to_be_bytes()); + bytes.extend(&self.quorum_numbers); + bytes.extend(&self.quorum_signed_percentages.len().to_be_bytes()); + bytes.extend(&self.quorum_signed_percentages); + bytes.extend(&self.reference_block_number.to_be_bytes()); + + bytes + } +} + +impl Decodable for BatchHeader { + fn decode(rlp: &Rlp) -> Result { + Ok(BatchHeader { + batch_root: rlp.val_at(0)?, + quorum_numbers: rlp.val_at(1)?, + quorum_signed_percentages: rlp.val_at(2)?, + reference_block_number: rlp.val_at(3)?, + }) + } +} + +impl Encodable for BatchHeader { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4); + s.append(&self.batch_root); + s.append(&self.quorum_numbers); + s.append(&self.quorum_signed_percentages); + s.append(&self.reference_block_number); + } +} + +impl From for BatchHeader { + fn from(value: DisperserBatchHeader) -> Self { + Self { + batch_root: value.batch_root, + quorum_numbers: value.quorum_numbers, + quorum_signed_percentages: value.quorum_signed_percentages, + reference_block_number: value.reference_block_number, + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct BatchMetadata { + pub batch_header: BatchHeader, + pub signatory_record_hash: Vec, + pub fee: Vec, + pub confirmation_block_number: u32, + pub batch_header_hash: Vec, +} + +impl BatchMetadata { + pub fn to_bytes(&self) -> Vec { + let mut bytes = vec![]; + bytes.extend(self.batch_header.to_bytes()); + bytes.extend(&self.signatory_record_hash); + bytes.extend(&self.confirmation_block_number.to_be_bytes()); + + bytes + } +} + +impl Decodable for BatchMetadata { + fn decode(rlp: &Rlp) -> Result { + let batch_header: BatchHeader = rlp.val_at(0)?; + + Ok(BatchMetadata { + batch_header, + signatory_record_hash: rlp.val_at(1)?, + fee: rlp.val_at(2)?, + confirmation_block_number: rlp.val_at(3)?, + batch_header_hash: rlp.val_at(4)?, + }) + } +} + +impl Encodable for BatchMetadata { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(5); + s.append(&self.batch_header); + s.append(&self.signatory_record_hash); + s.append(&self.fee); + s.append(&self.confirmation_block_number); + s.append(&self.batch_header_hash); + } +} + +impl TryFrom for BatchMetadata { + type Error = ConversionError; + fn try_from(value: DisperserBatchMetadata) -> Result { + if value.batch_header.is_none() { + return Err(ConversionError::NotPresentError); + } + Ok(Self { + batch_header: BatchHeader::from(value.batch_header.unwrap()), + signatory_record_hash: value.signatory_record_hash, + fee: value.fee, + confirmation_block_number: value.confirmation_block_number, + batch_header_hash: value.batch_header_hash, + }) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct BlobVerificationProof { + pub batch_id: u32, + pub blob_index: u32, + pub batch_medatada: BatchMetadata, + pub inclusion_proof: Vec, + pub quorum_indexes: Vec, +} + +impl BlobVerificationProof { + pub fn to_bytes(&self) -> Vec { + let mut bytes = vec![]; + bytes.extend(&self.batch_id.to_be_bytes()); + bytes.extend(&self.blob_index.to_be_bytes()); + bytes.extend(self.batch_medatada.to_bytes()); + bytes.extend(&self.inclusion_proof.len().to_be_bytes()); + bytes.extend(&self.inclusion_proof); + bytes.extend(&self.quorum_indexes.len().to_be_bytes()); + bytes.extend(&self.quorum_indexes); + + bytes + } +} + +impl Decodable for BlobVerificationProof { + fn decode(rlp: &Rlp) -> Result { + Ok(BlobVerificationProof { + batch_id: rlp.val_at(0)?, + blob_index: rlp.val_at(1)?, + batch_medatada: rlp.val_at(2)?, + inclusion_proof: rlp.val_at(3)?, + quorum_indexes: rlp.val_at(4)?, + }) + } +} + +impl Encodable for BlobVerificationProof { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(5); + s.append(&self.batch_id); + s.append(&self.blob_index); + s.append(&self.batch_medatada); + s.append(&self.inclusion_proof); + s.append(&self.quorum_indexes); + } +} + +impl TryFrom for BlobVerificationProof { + type Error = ConversionError; + fn try_from(value: DisperserBlobVerificationProof) -> Result { + if value.batch_metadata.is_none() { + return Err(ConversionError::NotPresentError); + } + Ok(Self { + batch_id: value.batch_id, + blob_index: value.blob_index, + batch_medatada: BatchMetadata::try_from(value.batch_metadata.unwrap())?, + inclusion_proof: value.inclusion_proof, + quorum_indexes: value.quorum_indexes, + }) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct BlobInfo { + pub blob_header: BlobHeader, + pub blob_verification_proof: BlobVerificationProof, +} + +impl BlobInfo { + pub fn to_bytes(&self) -> Vec { + let mut bytes = vec![]; + let blob_header_bytes = self.blob_header.to_bytes(); + bytes.extend(blob_header_bytes.len().to_be_bytes()); + bytes.extend(blob_header_bytes); + let blob_verification_proof_bytes = self.blob_verification_proof.to_bytes(); + bytes.extend(blob_verification_proof_bytes); + bytes + } +} + +impl Decodable for BlobInfo { + fn decode(rlp: &Rlp) -> Result { + let blob_header: BlobHeader = rlp.val_at(0)?; + let blob_verification_proof: BlobVerificationProof = rlp.val_at(1)?; + + Ok(BlobInfo { + blob_header, + blob_verification_proof, + }) + } +} + +impl Encodable for BlobInfo { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.blob_header); + s.append(&self.blob_verification_proof); + } +} + +impl TryFrom for BlobInfo { + type Error = ConversionError; + fn try_from(value: DisperserBlobInfo) -> Result { + if value.blob_header.is_none() || value.blob_verification_proof.is_none() { + return Err(ConversionError::NotPresentError); + } + Ok(Self { + blob_header: BlobHeader::try_from(value.blob_header.unwrap())?, + blob_verification_proof: BlobVerificationProof::try_from( + value.blob_verification_proof.unwrap(), + )?, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_blob_info_encoding_and_decoding() { + let blob_info = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + }, + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, + 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, + 211, 43, + ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, + }, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, + 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + ], + }, + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, + 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, + 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, + 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, + 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, + 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, + 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, + 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, + 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, + 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, + 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, + 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, + 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, + 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + + let encoded_blob_info = rlp::encode(&blob_info); + let decoded_blob_info: BlobInfo = rlp::decode(&encoded_blob_info).unwrap(); + + assert_eq!(blob_info, decoded_blob_info); + } +} diff --git a/core/node/da_clients/src/eigen/client.rs b/core/node/da_clients/src/eigen/client.rs index d977620526aa..44ca7355ad47 100644 --- a/core/node/da_clients/src/eigen/client.rs +++ b/core/node/da_clients/src/eigen/client.rs @@ -1,5 +1,6 @@ use std::{str::FromStr, sync::Arc}; +use anyhow::anyhow; use async_trait::async_trait; use secp256k1::SecretKey; use subxt_signer::ExposeSecret; @@ -9,12 +10,16 @@ use zksync_da_client::{ DataAvailabilityClient, }; -use super::sdk::RawEigenClient; +use super::{blob_info::BlobInfo, memstore::MemStore, sdk::RawEigenClient, Disperser}; use crate::utils::to_non_retriable_da_error; +/// EigenClient is a client for the Eigen DA service. +/// It can be configured to use one of two dispersal methods: +/// - Remote: Dispatch blobs to a remote Eigen service. +/// - Memstore: Stores blobs in memory, used for testing purposes. #[derive(Debug, Clone)] pub struct EigenClient { - client: Arc, + client: Disperser, } impl EigenClient { @@ -22,16 +27,14 @@ impl EigenClient { 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?, - ), - }) + let disperser: Disperser = match config.clone() { + EigenConfig::Disperser(config) => { + let client = RawEigenClient::new(private_key, config).await?; + Disperser::Remote(Arc::new(client)) + } + EigenConfig::MemStore(config) => Disperser::Memory(MemStore::new(config)), + }; + Ok(Self { client: disperser }) } } @@ -42,17 +45,43 @@ impl DataAvailabilityClient for EigenClient { _: u32, // batch number data: Vec, ) -> Result { - let blob_id = self - .client - .dispatch_blob(data) - .await - .map_err(to_non_retriable_da_error)?; + if let Some(blob_size_limit) = self.blob_size_limit() { + if data.len() > blob_size_limit { + return Err(DAError { + error: anyhow!("Blob size limit exceeded"), + is_retriable: false, + }); + } + } + + let blob_id = match &self.client { + Disperser::Remote(remote_disperser) => remote_disperser + .dispatch_blob(data) + .await + .map_err(to_non_retriable_da_error)?, + Disperser::Memory(memstore) => memstore + .clone() + .put_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![] })) + async fn get_inclusion_data(&self, blob_id: &str) -> Result, DAError> { + let rlp_encoded_bytes = hex::decode(blob_id).map_err(|_| DAError { + error: anyhow!("Failed to decode blob_id"), + is_retriable: false, + })?; + let blob_info: BlobInfo = rlp::decode(&rlp_encoded_bytes).map_err(|_| DAError { + error: anyhow!("Failed to decode blob_info"), + is_retriable: false, + })?; + let inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + Ok(Some(InclusionData { + data: inclusion_data, + })) } fn clone_boxed(&self) -> Box { @@ -60,6 +89,283 @@ impl DataAvailabilityClient for EigenClient { } fn blob_size_limit(&self) -> Option { - Some(1920 * 1024) // 2mb - 128kb as a buffer + match self.client.clone() { + Disperser::Memory(mem_store) => Some(mem_store.config.max_blob_size_bytes as usize), + Disperser::Remote(raw_eigen_client) => { + Some(raw_eigen_client.config.blob_size_limit as usize) + } + } + } +} + +#[cfg(test)] +impl EigenClient { + pub async fn get_blob_data(&self, blob_id: &str) -> anyhow::Result>, DAError> { + match &self.client { + Disperser::Remote(remote_client) => remote_client.get_blob_data(blob_id).await, + Disperser::Memory(memstore) => memstore.clone().get_blob_data(blob_id).await, + } + } +} +#[cfg(test)] +mod tests { + use serial_test::serial; + use zksync_config::configs::da_client::eigen::{DisperserConfig, MemStoreConfig}; + use zksync_types::secrets::PrivateKey; + + use super::*; + use crate::eigen::blob_info::BlobInfo; + + #[tokio::test] + #[serial] + async fn test_non_auth_dispersal() { + let config = EigenConfig::Disperser(DisperserConfig { + disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), + eth_confirmation_depth: -1, + eigenda_eth_rpc: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + blob_size_limit: 2 * 1024 * 1024, // 2MB + status_query_timeout: 1800000, // 30 minutes + status_query_interval: 5, // 5 ms + wait_for_finalization: false, + authenticated: false, + verify_cert: true, + path_to_points: "../../../resources".to_string(), + }); + let secrets = EigenSecrets { + private_key: PrivateKey::from_str( + "d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", + ) + .unwrap(), + }; + let client = EigenClient::new(config, secrets).await.unwrap(); + let data = vec![1; 20]; + let result = client.dispatch_blob(0, data.clone()).await.unwrap(); + let blob_info: BlobInfo = + rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); + let expected_inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + let actual_inclusion_data = client + .get_inclusion_data(&result.blob_id) + .await + .unwrap() + .unwrap() + .data; + assert_eq!(expected_inclusion_data, actual_inclusion_data); + let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); + assert_eq!(retrieved_data.unwrap(), data); + } + + #[tokio::test] + #[serial] + async fn test_auth_dispersal() { + let config = EigenConfig::Disperser(DisperserConfig { + disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), + eth_confirmation_depth: -1, + eigenda_eth_rpc: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + blob_size_limit: 2 * 1024 * 1024, // 2MB + status_query_timeout: 1800000, // 30 minutes + status_query_interval: 5, // 5 ms + wait_for_finalization: false, + authenticated: true, + verify_cert: true, + path_to_points: "../../../resources".to_string(), + }); + let secrets = EigenSecrets { + private_key: PrivateKey::from_str( + "d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", + ) + .unwrap(), + }; + let client = EigenClient::new(config, secrets).await.unwrap(); + let data = vec![1; 20]; + let result = client.dispatch_blob(0, data.clone()).await.unwrap(); + let blob_info: BlobInfo = + rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); + let expected_inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + let actual_inclusion_data = client + .get_inclusion_data(&result.blob_id) + .await + .unwrap() + .unwrap() + .data; + assert_eq!(expected_inclusion_data, actual_inclusion_data); + let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); + assert_eq!(retrieved_data.unwrap(), data); + } + + #[tokio::test] + async fn test_eigenda_memory_disperser() { + let config = EigenConfig::MemStore(MemStoreConfig { + max_blob_size_bytes: 2 * 1024 * 1024, // 2MB, + blob_expiration: 60 * 2, + get_latency: 0, + put_latency: 0, + }); + let secrets = EigenSecrets { + private_key: PrivateKey::from_str( + "d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", + ) + .unwrap(), + }; + let client = EigenClient::new(config, secrets).await.unwrap(); + let data = vec![1u8; 100]; + let result = client.dispatch_blob(0, data.clone()).await.unwrap(); + + let blob_info: BlobInfo = + rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); + let expected_inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + let actual_inclusion_data = client + .get_inclusion_data(&result.blob_id) + .await + .unwrap() + .unwrap() + .data; + assert_eq!(expected_inclusion_data, actual_inclusion_data); + + let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); + assert_eq!(retrieved_data.unwrap(), data); + } + #[tokio::test] + #[serial] + async fn test_wait_for_finalization() { + let config = EigenConfig::Disperser(DisperserConfig { + disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), + blob_size_limit: 2 * 1024 * 1024, // 2MB + status_query_timeout: 1800000, // 30 minutes + status_query_interval: 5000, // 5000 ms + wait_for_finalization: true, + authenticated: true, + verify_cert: true, + path_to_points: "../../../resources".to_string(), + eth_confirmation_depth: 0, + eigenda_eth_rpc: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + }); + let secrets = EigenSecrets { + private_key: PrivateKey::from_str( + "d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", + ) + .unwrap(), + }; + let client = EigenClient::new(config, secrets).await.unwrap(); + let data = vec![1; 20]; + let result = client.dispatch_blob(0, data.clone()).await.unwrap(); + let blob_info: BlobInfo = + rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); + let expected_inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + let actual_inclusion_data = client + .get_inclusion_data(&result.blob_id) + .await + .unwrap() + .unwrap() + .data; + assert_eq!(expected_inclusion_data, actual_inclusion_data); + let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); + assert_eq!(retrieved_data.unwrap(), data); + } + + #[tokio::test] + async fn test_eigenda_dispatch_blob_too_large() { + let config = EigenConfig::MemStore(MemStoreConfig { + max_blob_size_bytes: 99, + blob_expiration: 60 * 2, + get_latency: 0, + put_latency: 0, + }); + let secrets = EigenSecrets { + private_key: PrivateKey::from_str( + "d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", + ) + .unwrap(), + }; + let client = EigenClient::new(config, secrets).await.unwrap(); + let data = vec![1u8; 100]; + let actual_error = client + .dispatch_blob(0, data.clone()) + .await + .err() + .unwrap() + .error; + let expected_error = anyhow!("Blob size limit exceeded"); + assert_eq!(format!("{}", actual_error), format!("{}", expected_error)); + } + + #[tokio::test] + #[serial] + async fn test_eth_confirmation_depth() { + let config = EigenConfig::Disperser(DisperserConfig { + disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), + eth_confirmation_depth: 5, + eigenda_eth_rpc: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + blob_size_limit: 2 * 1024 * 1024, // 2MB + status_query_timeout: 1800000, // 30 minutes + status_query_interval: 5, // 5 ms + wait_for_finalization: false, + authenticated: false, + verify_cert: true, + path_to_points: "../../../resources".to_string(), + }); + let secrets = EigenSecrets { + private_key: PrivateKey::from_str( + "d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", + ) + .unwrap(), + }; + let client = EigenClient::new(config, secrets).await.unwrap(); + let data = vec![1; 20]; + let result = client.dispatch_blob(0, data.clone()).await.unwrap(); + let blob_info: BlobInfo = + rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); + let expected_inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + let actual_inclusion_data = client + .get_inclusion_data(&result.blob_id) + .await + .unwrap() + .unwrap() + .data; + assert_eq!(expected_inclusion_data, actual_inclusion_data); + let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); + assert_eq!(retrieved_data.unwrap(), data); + } + + #[tokio::test] + #[serial] + async fn test_auth_dispersal_eth_confirmation_depth() { + let config = EigenConfig::Disperser(DisperserConfig { + disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), + eth_confirmation_depth: 5, + eigenda_eth_rpc: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + eigenda_svc_manager_address: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + blob_size_limit: 2 * 1024 * 1024, // 2MB + status_query_timeout: 1800000, // 30 minutes + status_query_interval: 5, // 5 ms + wait_for_finalization: false, + authenticated: true, + verify_cert: true, + path_to_points: "../../../resources".to_string(), + }); + let secrets = EigenSecrets { + private_key: PrivateKey::from_str( + "d08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6", + ) + .unwrap(), + }; + let client = EigenClient::new(config, secrets).await.unwrap(); + let data = vec![1; 20]; + let result = client.dispatch_blob(0, data.clone()).await.unwrap(); + let blob_info: BlobInfo = + rlp::decode(&hex::decode(result.blob_id.clone()).unwrap()).unwrap(); + let expected_inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + let actual_inclusion_data = client + .get_inclusion_data(&result.blob_id) + .await + .unwrap() + .unwrap() + .data; + assert_eq!(expected_inclusion_data, actual_inclusion_data); + let retrieved_data = client.get_blob_data(&result.blob_id).await.unwrap(); + assert_eq!(retrieved_data.unwrap(), data); } } diff --git a/core/node/da_clients/src/eigen/eigenda-integration.md b/core/node/da_clients/src/eigen/eigenda-integration.md new file mode 100644 index 000000000000..ff3957af7251 --- /dev/null +++ b/core/node/da_clients/src/eigen/eigenda-integration.md @@ -0,0 +1,191 @@ +# Zksync-era <> EigenDA Integration + +EigenDA is as a high-throughput data availability layer for rollups. It is an EigenLayer AVS (Actively Validated +Service), so it leverages Ethereum's economic security instead of bootstrapping a new network with its own validators. +For more information you can check the [docs](https://docs.eigenda.xyz/). + +## Scope + +The scope of this first milestone is to spin up a local EigenDA dev environment, spin up a local zksync-era dev +environment and integrate them. Instead of sending 4844 blobs, the zksync-era sends blobs to EigenDA. On L1, mock the +verification logic, such that blocks continue building. Increase the blob size from 4844 size to 2MiB blob. Deploy the +integration to Holesky testnet and provide scripts to setup a network using EigenDA as DA provider. + +## Common changes + +Changes needed both for local and mainnet/testnet setup. + +1. Add `da_client` to `etc/env/file_based/general.yaml`: + +If you want to use memstore: + +```yaml +da_client: + eigen: + mem_store: + max_blob_size_bytes: 2097152 + blob_expiration: 100000 + get_latency: 100 + put_latency: 100 +``` + +If you want to use disperser: + +```yaml +da_client: + eigen: + disperser: + disperser_rpc: + eth_confirmation_depth: -1 + eigenda_eth_rpc: + eigenda_svc_manager_address: '0xD4A7E1Bd8015057293f0D0A557088c286942e84b' + blob_size_limit: 2097152 + status_query_timeout: 1800000 # ms + status_query_interval: 5 # ms + wait_for_finalization: false + authenticated: false + verify_cert: true + path_to_points: ./resources +``` + +Also set the private key in `etc/env/file_based/secrets.yaml`: + +```yaml +da: + eigen: + private_key: '' +``` + +2. (optional) for using pubdata with 2MiB (as per specification), modify `etc/env/file_based/general.yaml`: + +```yaml +max_pubdata_per_batch: 2097152 +``` + +## Local Setup + +1. Install `zkstack` + +```bash +cargo install --path zkstack_cli/crates/zkstack --force --locked +``` + +2. Start containers + +```bash +zkstack containers --observability true +``` + +3. Create `eigen_da` chain + +```bash +zkstack chain create \ + --chain-name eigen_da \ + --chain-id sequential \ + --prover-mode no-proofs \ + --wallet-creation localhost \ + --l1-batch-commit-data-generator-mode validium \ + --base-token-address 0x0000000000000000000000000000000000000001 \ + --base-token-price-nominator 1 \ + --base-token-price-denominator 1 \ + --set-as-default false +``` + +4. Initialize created ecosystem + +```bash +zkstack ecosystem init \ + --deploy-paymaster true \ + --deploy-erc20 true \ + --deploy-ecosystem true \ + --l1-rpc-url http://127.0.0.1:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_eigen_da \ + --chain eigen_da \ + --verbose +``` + +You may enable observability here if you want to. + +5. Start the server + +```bash +zkstack server --chain eigen_da +``` + +### Testing + +Modify the following flag in `core/lib/config/src/configs/da_dispatcher.rs` (then restart the server) + +```rs +pub const DEFAULT_USE_DUMMY_INCLUSION_DATA: bool = true; +``` + +And with the server running on one terminal, you can run the server integration tests on a separate terminal with the +following command: + +```bash +zkstack dev test integration --chain eigen_da +``` + +## Mainnet/Testnet setup + +### Modify localhost chain id number + +Modify line 32 in `zk_toolbox/crates/types/src/l1_network.rs`: + +```rs +L1Network::Localhost => 17000, +``` + +Then recompile the zkstack: + +```bash +cargo install --path zkstack_cli/crates/zkstack --force --locked +``` + +### Used wallets + +Modify `etc/env/file_based/wallets.yaml` and `configs/wallets.yaml` with the following wallets: + +```yaml +# Use your own holesky wallets, be sure they have enough funds +``` + +> ⚠️ Some steps distribute ~5000ETH to some wallets, modify `AMOUNT_FOR_DISTRIBUTION_TO_WALLETS` to a lower value if +> needed. + +### Create and initialize the ecosystem + +(be sure to have postgres container running on the background) + +```bash +zkstack chain create \ + --chain-name holesky_eigen_da \ + --chain-id 114411 \ + --prover-mode no-proofs \ + --wallet-creation localhost \ + --l1-batch-commit-data-generator-mode validium \ + --base-token-address 0x0000000000000000000000000000000000000001 \ + --base-token-price-nominator 1 \ + --base-token-price-denominator 1 \ + --set-as-default false + +zkstack ecosystem init \ + --deploy-paymaster true \ + --deploy-erc20 true \ + --deploy-ecosystem true \ + --l1-rpc-url $HOLESKY_RPC_URL \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_holesky_eigen_da \ + --prover-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --prover-db-name=zksync_prover_holesky_eigen_da \ + --chain holesky_eigen_da \ + --verbose +``` + +### Start the server + +```bash +zkstack server --chain holesky_eigen_da +``` diff --git a/core/node/da_clients/src/eigen/generated/eigendaservicemanager.rs b/core/node/da_clients/src/eigen/generated/eigendaservicemanager.rs new file mode 100644 index 000000000000..577f2613c0df --- /dev/null +++ b/core/node/da_clients/src/eigen/generated/eigendaservicemanager.rs @@ -0,0 +1,10481 @@ +// Disable clippy checks on autogenerated code +#![allow( + dead_code, + clippy::identity_op, + clippy::useless_conversion, + clippy::clone_on_copy, + clippy::needless_lifetimes, + clippy::type_complexity +)] +/** + +Generated by the following Solidity interface... +```solidity +interface EigenDAServiceManager { + struct BatchHeader { + bytes32 blobHeadersRoot; + bytes quorumNumbers; + bytes signedStakeForQuorums; + uint32 referenceBlockNumber; + } + struct G1Point { + uint256 X; + uint256 Y; + } + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + struct NonSignerStakesAndSignature { + uint32[] nonSignerQuorumBitmapIndices; + G1Point[] nonSignerPubkeys; + G1Point[] quorumApks; + G2Point apkG2; + G1Point sigma; + uint32[] quorumApkIndices; + uint32[] totalStakeIndices; + uint32[][] nonSignerStakeIndices; + } + struct QuorumStakeTotals { + uint96[] signedStakeForQuorum; + uint96[] totalStakeForQuorum; + } + struct RewardsSubmission { + StrategyAndMultiplier[] strategiesAndMultipliers; + address token; + uint256 amount; + uint32 startTimestamp; + uint32 duration; + } + struct SignatureWithSaltAndExpiry { + bytes signature; + bytes32 salt; + uint256 expiry; + } + struct StrategyAndMultiplier { + address strategy; + uint96 multiplier; + } + + event BatchConfirmed(bytes32 indexed batchHeaderHash, uint32 batchId); + event BatchConfirmerStatusChanged(address batchConfirmer, bool status); + event Initialized(uint8 version); + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event Paused(address indexed account, uint256 newPausedStatus); + event PauserRegistrySet(address pauserRegistry, address newPauserRegistry); + event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); + event StaleStakesForbiddenUpdate(bool value); + event Unpaused(address indexed account, uint256 newPausedStatus); + + constructor(address __avsDirectory, address __rewardsCoordinator, address __registryCoordinator, address __stakeRegistry); + + function BLOCK_STALE_MEASURE() external view returns (uint32); + function STORE_DURATION_BLOCKS() external view returns (uint32); + function THRESHOLD_DENOMINATOR() external view returns (uint256); + function avsDirectory() external view returns (address); + function batchId() external view returns (uint32); + function batchIdToBatchMetadataHash(uint32) external view returns (bytes32); + function blsApkRegistry() external view returns (address); + function checkSignatures(bytes32 msgHash, bytes memory quorumNumbers, uint32 referenceBlockNumber, NonSignerStakesAndSignature memory params) external view returns (QuorumStakeTotals memory, bytes32); + function confirmBatch(BatchHeader memory batchHeader, NonSignerStakesAndSignature memory nonSignerStakesAndSignature) external; + function createAVSRewardsSubmission(RewardsSubmission[] memory rewardsSubmissions) external; + function delegation() external view returns (address); + function deregisterOperatorFromAVS(address operator) external; + function getOperatorRestakedStrategies(address operator) external view returns (address[] memory); + function getRestakeableStrategies() external view returns (address[] memory); + function initialize(address _pauserRegistry, uint256 _initialPausedStatus, address _initialOwner, address[] memory _batchConfirmers, address _rewardsInitiator) external; + function isBatchConfirmer(address) external view returns (bool); + function latestServeUntilBlock(uint32 referenceBlockNumber) external view returns (uint32); + function owner() external view returns (address); + function pause(uint256 newPausedStatus) external; + function pauseAll() external; + function paused(uint8 index) external view returns (bool); + function paused() external view returns (uint256); + function pauserRegistry() external view returns (address); + function quorumAdversaryThresholdPercentages() external view returns (bytes memory); + function quorumConfirmationThresholdPercentages() external view returns (bytes memory); + function quorumNumbersRequired() external view returns (bytes memory); + function registerOperatorToAVS(address operator, SignatureWithSaltAndExpiry memory operatorSignature) external; + function registryCoordinator() external view returns (address); + function renounceOwnership() external; + function rewardsInitiator() external view returns (address); + function setBatchConfirmer(address _batchConfirmer) external; + function setPauserRegistry(address newPauserRegistry) external; + function setRewardsInitiator(address newRewardsInitiator) external; + function setStaleStakesForbidden(bool value) external; + function stakeRegistry() external view returns (address); + function staleStakesForbidden() external view returns (bool); + function taskNumber() external view returns (uint32); + function transferOwnership(address newOwner) external; + function trySignatureAndApkVerification(bytes32 msgHash, G1Point memory apk, G2Point memory apkG2, G1Point memory sigma) external view returns (bool pairingSuccessful, bool siganatureIsValid); + function unpause(uint256 newPausedStatus) external; + function updateAVSMetadataURI(string memory _metadataURI) external; +} +``` + +...which was generated by the following JSON ABI: +```json +[ + { + "type": "constructor", + "inputs": [ + { + "name": "__avsDirectory", + "type": "address", + "internalType": "contract IAVSDirectory" + }, + { + "name": "__rewardsCoordinator", + "type": "address", + "internalType": "contract IRewardsCoordinator" + }, + { + "name": "__registryCoordinator", + "type": "address", + "internalType": "contract IRegistryCoordinator" + }, + { + "name": "__stakeRegistry", + "type": "address", + "internalType": "contract IStakeRegistry" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "BLOCK_STALE_MEASURE", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "STORE_DURATION_BLOCKS", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "THRESHOLD_DENOMINATOR", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "avsDirectory", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "batchId", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "batchIdToBatchMetadataHash", + "inputs": [ + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "blsApkRegistry", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IBLSApkRegistry" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "checkSignatures", + "inputs": [ + { + "name": "msgHash", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "quorumNumbers", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "referenceBlockNumber", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "params", + "type": "tuple", + "internalType": "struct IBLSSignatureChecker.NonSignerStakesAndSignature", + "components": [ + { + "name": "nonSignerQuorumBitmapIndices", + "type": "uint32[]", + "internalType": "uint32[]" + }, + { + "name": "nonSignerPubkeys", + "type": "tuple[]", + "internalType": "struct BN254.G1Point[]", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "quorumApks", + "type": "tuple[]", + "internalType": "struct BN254.G1Point[]", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "apkG2", + "type": "tuple", + "internalType": "struct BN254.G2Point", + "components": [ + { + "name": "X", + "type": "uint256[2]", + "internalType": "uint256[2]" + }, + { + "name": "Y", + "type": "uint256[2]", + "internalType": "uint256[2]" + } + ] + }, + { + "name": "sigma", + "type": "tuple", + "internalType": "struct BN254.G1Point", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "quorumApkIndices", + "type": "uint32[]", + "internalType": "uint32[]" + }, + { + "name": "totalStakeIndices", + "type": "uint32[]", + "internalType": "uint32[]" + }, + { + "name": "nonSignerStakeIndices", + "type": "uint32[][]", + "internalType": "uint32[][]" + } + ] + } + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "internalType": "struct IBLSSignatureChecker.QuorumStakeTotals", + "components": [ + { + "name": "signedStakeForQuorum", + "type": "uint96[]", + "internalType": "uint96[]" + }, + { + "name": "totalStakeForQuorum", + "type": "uint96[]", + "internalType": "uint96[]" + } + ] + }, + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "confirmBatch", + "inputs": [ + { + "name": "batchHeader", + "type": "tuple", + "internalType": "struct IEigenDAServiceManager.BatchHeader", + "components": [ + { + "name": "blobHeadersRoot", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "quorumNumbers", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "signedStakeForQuorums", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "referenceBlockNumber", + "type": "uint32", + "internalType": "uint32" + } + ] + }, + { + "name": "nonSignerStakesAndSignature", + "type": "tuple", + "internalType": "struct IBLSSignatureChecker.NonSignerStakesAndSignature", + "components": [ + { + "name": "nonSignerQuorumBitmapIndices", + "type": "uint32[]", + "internalType": "uint32[]" + }, + { + "name": "nonSignerPubkeys", + "type": "tuple[]", + "internalType": "struct BN254.G1Point[]", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "quorumApks", + "type": "tuple[]", + "internalType": "struct BN254.G1Point[]", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "apkG2", + "type": "tuple", + "internalType": "struct BN254.G2Point", + "components": [ + { + "name": "X", + "type": "uint256[2]", + "internalType": "uint256[2]" + }, + { + "name": "Y", + "type": "uint256[2]", + "internalType": "uint256[2]" + } + ] + }, + { + "name": "sigma", + "type": "tuple", + "internalType": "struct BN254.G1Point", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "quorumApkIndices", + "type": "uint32[]", + "internalType": "uint32[]" + }, + { + "name": "totalStakeIndices", + "type": "uint32[]", + "internalType": "uint32[]" + }, + { + "name": "nonSignerStakeIndices", + "type": "uint32[][]", + "internalType": "uint32[][]" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createAVSRewardsSubmission", + "inputs": [ + { + "name": "rewardsSubmissions", + "type": "tuple[]", + "internalType": "struct IRewardsCoordinator.RewardsSubmission[]", + "components": [ + { + "name": "strategiesAndMultipliers", + "type": "tuple[]", + "internalType": "struct IRewardsCoordinator.StrategyAndMultiplier[]", + "components": [ + { + "name": "strategy", + "type": "address", + "internalType": "contract IStrategy" + }, + { + "name": "multiplier", + "type": "uint96", + "internalType": "uint96" + } + ] + }, + { + "name": "token", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "startTimestamp", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "duration", + "type": "uint32", + "internalType": "uint32" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "delegation", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IDelegationManager" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "deregisterOperatorFromAVS", + "inputs": [ + { + "name": "operator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getOperatorRestakedStrategies", + "inputs": [ + { + "name": "operator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getRestakeableStrategies", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address[]", + "internalType": "address[]" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "initialize", + "inputs": [ + { + "name": "_pauserRegistry", + "type": "address", + "internalType": "contract IPauserRegistry" + }, + { + "name": "_initialPausedStatus", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "_initialOwner", + "type": "address", + "internalType": "address" + }, + { + "name": "_batchConfirmers", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "_rewardsInitiator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "isBatchConfirmer", + "inputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "latestServeUntilBlock", + "inputs": [ + { + "name": "referenceBlockNumber", + "type": "uint32", + "internalType": "uint32" + } + ], + "outputs": [ + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pause", + "inputs": [ + { + "name": "newPausedStatus", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "pauseAll", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "paused", + "inputs": [ + { + "name": "index", + "type": "uint8", + "internalType": "uint8" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "paused", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "pauserRegistry", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IPauserRegistry" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "quorumAdversaryThresholdPercentages", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes", + "internalType": "bytes" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "quorumConfirmationThresholdPercentages", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes", + "internalType": "bytes" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "quorumNumbersRequired", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes", + "internalType": "bytes" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "registerOperatorToAVS", + "inputs": [ + { + "name": "operator", + "type": "address", + "internalType": "address" + }, + { + "name": "operatorSignature", + "type": "tuple", + "internalType": "struct ISignatureUtils.SignatureWithSaltAndExpiry", + "components": [ + { + "name": "signature", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "salt", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "expiry", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "registryCoordinator", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IRegistryCoordinator" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "rewardsInitiator", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setBatchConfirmer", + "inputs": [ + { + "name": "_batchConfirmer", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setPauserRegistry", + "inputs": [ + { + "name": "newPauserRegistry", + "type": "address", + "internalType": "contract IPauserRegistry" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setRewardsInitiator", + "inputs": [ + { + "name": "newRewardsInitiator", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setStaleStakesForbidden", + "inputs": [ + { + "name": "value", + "type": "bool", + "internalType": "bool" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "stakeRegistry", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "contract IStakeRegistry" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "staleStakesForbidden", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "taskNumber", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint32", + "internalType": "uint32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "trySignatureAndApkVerification", + "inputs": [ + { + "name": "msgHash", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "apk", + "type": "tuple", + "internalType": "struct BN254.G1Point", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + }, + { + "name": "apkG2", + "type": "tuple", + "internalType": "struct BN254.G2Point", + "components": [ + { + "name": "X", + "type": "uint256[2]", + "internalType": "uint256[2]" + }, + { + "name": "Y", + "type": "uint256[2]", + "internalType": "uint256[2]" + } + ] + }, + { + "name": "sigma", + "type": "tuple", + "internalType": "struct BN254.G1Point", + "components": [ + { + "name": "X", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "Y", + "type": "uint256", + "internalType": "uint256" + } + ] + } + ], + "outputs": [ + { + "name": "pairingSuccessful", + "type": "bool", + "internalType": "bool" + }, + { + "name": "siganatureIsValid", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "unpause", + "inputs": [ + { + "name": "newPausedStatus", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "updateAVSMetadataURI", + "inputs": [ + { + "name": "_metadataURI", + "type": "string", + "internalType": "string" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "BatchConfirmed", + "inputs": [ + { + "name": "batchHeaderHash", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "batchId", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BatchConfirmerStatusChanged", + "inputs": [ + { + "name": "batchConfirmer", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "status", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Initialized", + "inputs": [ + { + "name": "version", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Paused", + "inputs": [ + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newPausedStatus", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "PauserRegistrySet", + "inputs": [ + { + "name": "pauserRegistry", + "type": "address", + "indexed": false, + "internalType": "contract IPauserRegistry" + }, + { + "name": "newPauserRegistry", + "type": "address", + "indexed": false, + "internalType": "contract IPauserRegistry" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "RewardsInitiatorUpdated", + "inputs": [ + { + "name": "prevRewardsInitiator", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "newRewardsInitiator", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "StaleStakesForbiddenUpdate", + "inputs": [ + { + "name": "value", + "type": "bool", + "indexed": false, + "internalType": "bool" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Unpaused", + "inputs": [ + { + "name": "account", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newPausedStatus", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + } +] +```*/ +#[allow(non_camel_case_types, non_snake_case, clippy::style)] +pub mod EigenDAServiceManager { + use alloy::sol_types as alloy_sol_types; + + #[allow(unused_imports)] + use super::*; + /// The creation / init bytecode of the contract. + /// + /// ```text + ///0x6101806040523480156200001257600080fd5b5060405162005871380380620058718339810160408190526200003591620002e5565b6001600160a01b0380851660805280841660a05280831660c052811660e0528184848284620000636200020a565b50505050806001600160a01b0316610100816001600160a01b031681525050806001600160a01b031663683048356040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e791906200034d565b6001600160a01b0316610120816001600160a01b031681525050806001600160a01b0316635df459466040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000140573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200016691906200034d565b6001600160a01b0316610140816001600160a01b031681525050610120516001600160a01b031663df5cf7236040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001c2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001e891906200034d565b6001600160a01b03166101605250620002006200020a565b5050505062000374565b603254610100900460ff1615620002775760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60325460ff9081161015620002ca576032805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b0381168114620002e257600080fd5b50565b60008060008060808587031215620002fc57600080fd5b84516200030981620002cc565b60208601519094506200031c81620002cc565b60408601519093506200032f81620002cc565b60608601519092506200034281620002cc565b939692955090935050565b6000602082840312156200036057600080fd5b81516200036d81620002cc565b9392505050565b60805160a05160c05160e051610100516101205161014051610160516153ef6200048260003960008181610569015261179501526000818161036c01526119770152600081816103be01528181611b4d0152611d0f01526000818161040b01528181610eb101528181611460015281816115f80152611832015260008181610bc901528181610d2401528181610dbb0152818161298001528181612b030152612ba20152600081816109f401528181610a8301528181610b03015281816126e4015281816127a8015281816128be0152612a5e01526000818161305f0152818161311b01526132070152600081816103e20152818161273801528181612804015261288301526153ef6000f3fe608060405234801561001057600080fd5b50600436106102535760003560e01c80637794965a11610146578063df5cf723116100c3578063ef02445811610087578063ef024458146105e3578063f1220983146105eb578063f2fde38b146105fe578063fabc1cbc14610611578063fc299dee14610624578063fce36c7d1461063757600080fd5b8063df5cf72314610564578063e15234ff1461058b578063e481af9d146105a8578063eaefd27d146105b0578063eccbbfc9146105c357600080fd5b8063a364f4da1161010a578063a364f4da146104ee578063a5b7890a14610501578063a98fb35514610524578063b98d090814610537578063bafa91071461054457600080fd5b80637794965a146104775780638687feae1461048a578063886f1195146104b75780638da5cb5b146104ca5780639926ee7d146104db57600080fd5b80635df45946116101d45780636d14a987116101985780636d14a987146104065780636efb46361461042d578063715018a61461044e57806372d18e8d14610456578063775bbcb51461046457600080fd5b80635df45946146103675780635e033476146103a65780635e8b3f2d146103b057806368304835146103b95780636b3aa72e146103e057600080fd5b8063416c7e5e1161021b578063416c7e5e146102e25780634972134a146102f5578063595c6a671461031a5780635ac86ab7146103225780635c975abb1461035557600080fd5b806310d67a2f14610258578063136439dd1461026d578063171f1d5b1461028057806333cfb7b7146102af5780633bc28c8c146102cf575b600080fd5b61026b6102663660046141af565b61064a565b005b61026b61027b3660046141cc565b610706565b61029361028e366004614336565b610845565b6040805192151583529015156020830152015b60405180910390f35b6102c26102bd3660046141af565b6109cf565b6040516102a69190614392565b61026b6102dd3660046141af565b610e9e565b61026b6102f03660046143ed565b610eaf565b6000546103059063ffffffff1681565b60405163ffffffff90911681526020016102a6565b61026b610fe6565b610345610330366004614419565b60fc54600160ff9092169190911b9081161490565b60405190151581526020016102a6565b60fc545b6040519081526020016102a6565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102a6565b610305620189c081565b61030561012c81565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000061038e565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b61044061043b3660046146ec565b6110ad565b6040516102a69291906147df565b61026b611fc4565b60005463ffffffff16610305565b61026b610472366004614828565b611fd8565b61026b610485366004614903565b612141565b6104aa604051806040016040528060018152602001602160f81b81525081565b6040516102a691906149bb565b60fb5461038e906001600160a01b031681565b6065546001600160a01b031661038e565b61026b6104e9366004614a45565b6126d9565b61026b6104fc3660046141af565b61279d565b61034561050f3660046141af565b60026020526000908152604090205460ff1681565b61026b610532366004614af0565b612864565b60c9546103459060ff1681565b6104aa604051806040016040528060018152602001603760f81b81525081565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b6104aa604051806040016040528060018152602001600081525081565b6102c26128b8565b6103056105be366004614b40565b612c81565b6103596105d1366004614b40565b60016020526000908152604090205481565b610359606481565b61026b6105f93660046141af565b612ca3565b61026b61060c3660046141af565b612cb4565b61026b61061f3660046141cc565b612d2a565b60975461038e906001600160a01b031681565b61026b610645366004614b5b565b612e86565b60fb60009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561069d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c19190614bcf565b6001600160a01b0316336001600160a01b0316146106fa5760405162461bcd60e51b81526004016106f190614bec565b60405180910390fd5b6107038161323e565b50565b60fb5460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa15801561074e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107729190614c36565b61078e5760405162461bcd60e51b81526004016106f190614c53565b60fc54818116146108075760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e70617573653a20696e76616c696420617474656d70742060448201527f746f20756e70617573652066756e6374696f6e616c697479000000000000000060648201526084016106f1565b60fc81905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d906020015b60405180910390a250565b60008060007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018787600001518860200151886000015160006002811061088d5761088d614c9b565b60200201518951600160200201518a602001516000600281106108b2576108b2614c9b565b60200201518b602001516001600281106108ce576108ce614c9b565b602090810291909101518c518d83015160405161092b9a99989796959401988952602089019790975260408801959095526060870193909352608086019190915260a085015260c084015260e08301526101008201526101200190565b6040516020818303038152906040528051906020012060001c61094e9190614cb1565b90506109c16109676109608884613335565b86906133cc565b61096f613460565b6109b76109a8856109a2604080518082018252600080825260209182015281518083019092526001825260029082015290565b90613335565b6109b18c613520565b906133cc565b886201d4c06135b0565b909890975095505050505050565b6040516309aa152760e11b81526001600160a01b0382811660048301526060916000917f000000000000000000000000000000000000000000000000000000000000000016906313542a4e90602401602060405180830381865afa158015610a3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5f9190614cd3565b60405163871ef04960e01b8152600481018290529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063871ef04990602401602060405180830381865afa158015610aca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aee9190614cec565b90506001600160c01b0381161580610b8857507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639aa1653d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b839190614d15565b60ff16155b15610ba457505060408051600081526020810190915292915050565b6000610bb8826001600160c01b03166137d4565b90506000805b8251811015610c8e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633ca5a5f5848381518110610c0857610c08614c9b565b01602001516040516001600160e01b031960e084901b16815260f89190911c6004820152602401602060405180830381865afa158015610c4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c709190614cd3565b610c7a9083614d48565b915080610c8681614d60565b915050610bbe565b506000816001600160401b03811115610ca957610ca96141e5565b604051908082528060200260200182016040528015610cd2578160200160208202803683370190505b5090506000805b8451811015610e91576000858281518110610cf657610cf6614c9b565b0160200151604051633ca5a5f560e01b815260f89190911c6004820181905291506000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690633ca5a5f590602401602060405180830381865afa158015610d6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8f9190614cd3565b905060005b81811015610e7b576040516356e4026d60e11b815260ff84166004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063adc804da906044016040805180830381865afa158015610e09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2d9190614d90565b60000151868681518110610e4357610e43614c9b565b6001600160a01b039092166020928302919091019091015284610e6581614d60565b9550508080610e7390614d60565b915050610d94565b5050508080610e8990614d60565b915050610cd9565b5090979650505050505050565b610ea6613896565b610703816138f0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f319190614bcf565b6001600160a01b0316336001600160a01b031614610fdd5760405162461bcd60e51b815260206004820152605c60248201527f424c535369676e6174757265436865636b65722e6f6e6c79436f6f7264696e6160448201527f746f724f776e65723a2063616c6c6572206973206e6f7420746865206f776e6560648201527f72206f6620746865207265676973747279436f6f7264696e61746f7200000000608482015260a4016106f1565b61070381613959565b60fb5460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110529190614c36565b61106e5760405162461bcd60e51b81526004016106f190614c53565b60001960fc81905560405190815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2565b60408051808201909152606080825260208201526000846111245760405162461bcd60e51b8152602060048201526037602482015260008051602061539a83398151915260448201527f7265733a20656d7074792071756f72756d20696e70757400000000000000000060648201526084016106f1565b6040830151518514801561113c575060a08301515185145b801561114c575060c08301515185145b801561115c575060e08301515185145b6111c65760405162461bcd60e51b8152602060048201526041602482015260008051602061539a83398151915260448201527f7265733a20696e7075742071756f72756d206c656e677468206d69736d6174636064820152600d60fb1b608482015260a4016106f1565b8251516020840151511461123e5760405162461bcd60e51b81526020600482015260446024820181905260008051602061539a833981519152908201527f7265733a20696e707574206e6f6e7369676e6572206c656e677468206d69736d6064820152630c2e8c6d60e31b608482015260a4016106f1565b4363ffffffff168463ffffffff16106112ad5760405162461bcd60e51b815260206004820152603c602482015260008051602061539a83398151915260448201527f7265733a20696e76616c6964207265666572656e636520626c6f636b0000000060648201526084016106f1565b6040805180820182526000808252602080830191909152825180840190935260608084529083015290866001600160401b038111156112ee576112ee6141e5565b604051908082528060200260200182016040528015611317578160200160208202803683370190505b506020820152866001600160401b03811115611335576113356141e5565b60405190808252806020026020018201604052801561135e578160200160208202803683370190505b50815260408051808201909152606080825260208201528560200151516001600160401b03811115611392576113926141e5565b6040519080825280602002602001820160405280156113bb578160200160208202803683370190505b5081526020860151516001600160401b038111156113db576113db6141e5565b604051908082528060200260200182016040528015611404578160200160208202803683370190505b50816020018190525060006114d68a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051639aa1653d60e01b815290516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169350639aa1653d925060048083019260209291908290030181865afa1580156114ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d19190614d15565b6139a1565b905060005b876020015151811015611771576115208860200151828151811061150157611501614c9b565b6020026020010151805160009081526020918201519091526040902090565b8360200151828151811061153657611536614c9b565b602090810291909101015280156115f6576020830151611557600183614dd1565b8151811061156757611567614c9b565b602002602001015160001c8360200151828151811061158857611588614c9b565b602002602001015160001c116115f6576040805162461bcd60e51b815260206004820152602481019190915260008051602061539a83398151915260448201527f7265733a206e6f6e5369676e65725075626b657973206e6f7420736f7274656460648201526084016106f1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166304ec63518460200151838151811061163b5761163b614c9b565b60200260200101518b8b60000151858151811061165a5761165a614c9b565b60200260200101516040518463ffffffff1660e01b81526004016116979392919092835263ffffffff918216602084015216604082015260600190565b602060405180830381865afa1580156116b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d89190614cec565b6001600160c01b0316836000015182815181106116f7576116f7614c9b565b60200260200101818152505061175d610960611731848660000151858151811061172357611723614c9b565b602002602001015116613a32565b8a60200151848151811061174757611747614c9b565b6020026020010151613a5d90919063ffffffff16565b94508061176981614d60565b9150506114db565b505061177c83613b41565b60c95490935060ff16600081611793576000611815565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c448feb86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118159190614cd3565b905060005b8a811015611e93578215611975578963ffffffff16827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663249a0c428f8f8681811061187157611871614c9b565b60405160e085901b6001600160e01b031916815292013560f81c600483015250602401602060405180830381865afa1580156118b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d59190614cd3565b6118df9190614d48565b116119755760405162461bcd60e51b8152602060048201526066602482015260008051602061539a83398151915260448201527f7265733a205374616b6552656769737472792075706461746573206d7573742060648201527f62652077697468696e207769746864726177616c44656c6179426c6f636b732060848201526577696e646f7760d01b60a482015260c4016106f1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166368bccaac8d8d848181106119b6576119b6614c9b565b9050013560f81c60f81b60f81c8c8c60a0015185815181106119da576119da614c9b565b60209081029190910101516040516001600160e01b031960e086901b16815260ff909316600484015263ffffffff9182166024840152166044820152606401602060405180830381865afa158015611a36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5a9190614de8565b6001600160401b031916611a7d8a60400151838151811061150157611501614c9b565b67ffffffffffffffff191614611b195760405162461bcd60e51b8152602060048201526061602482015260008051602061539a83398151915260448201527f7265733a2071756f72756d41706b206861736820696e2073746f72616765206460648201527f6f6573206e6f74206d617463682070726f76696465642071756f72756d2061706084820152606b60f81b60a482015260c4016106f1565b611b4989604001518281518110611b3257611b32614c9b565b6020026020010151876133cc90919063ffffffff16565b95507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c8294c568d8d84818110611b8c57611b8c614c9b565b9050013560f81c60f81b60f81c8c8c60c001518581518110611bb057611bb0614c9b565b60209081029190910101516040516001600160e01b031960e086901b16815260ff909316600484015263ffffffff9182166024840152166044820152606401602060405180830381865afa158015611c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c309190614e13565b85602001518281518110611c4657611c46614c9b565b6001600160601b03909216602092830291909101820152850151805182908110611c7257611c72614c9b565b602002602001015185600001518281518110611c9057611c90614c9b565b60200260200101906001600160601b031690816001600160601b0316815250506000805b8a6020015151811015611e7e57611d0886600001518281518110611cda57611cda614c9b565b60200260200101518f8f86818110611cf457611cf4614c9b565b600192013560f81c9290921c811614919050565b15611e6c577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f2be94ae8f8f86818110611d4e57611d4e614c9b565b9050013560f81c60f81b60f81c8e89602001518581518110611d7257611d72614c9b565b60200260200101518f60e001518881518110611d9057611d90614c9b565b60200260200101518781518110611da957611da9614c9b565b60209081029190910101516040516001600160e01b031960e087901b16815260ff909416600485015263ffffffff92831660248501526044840191909152166064820152608401602060405180830381865afa158015611e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e319190614e13565b8751805185908110611e4557611e45614c9b565b60200260200101818151611e599190614e30565b6001600160601b03169052506001909101905b80611e7681614d60565b915050611cb4565b50508080611e8b90614d60565b91505061181a565b505050600080611ead8c868a606001518b60800151610845565b9150915081611f1e5760405162461bcd60e51b8152602060048201526043602482015260008051602061539a83398151915260448201527f7265733a2070616972696e6720707265636f6d70696c652063616c6c206661696064820152621b195960ea1b608482015260a4016106f1565b80611f7f5760405162461bcd60e51b8152602060048201526039602482015260008051602061539a83398151915260448201527f7265733a207369676e617475726520697320696e76616c69640000000000000060648201526084016106f1565b50506000878260200151604051602001611f9a929190614e58565b60408051808303601f190181529190528051602090910120929b929a509198505050505050505050565b611fcc613896565b611fd66000613bdc565b565b603254610100900460ff1615808015611ff85750603254600160ff909116105b806120125750303b158015612012575060325460ff166001145b6120755760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016106f1565b6032805460ff191660011790558015612098576032805461ff0019166101001790555b6120a28686613c2e565b6120ab84613bdc565b6120b4826138f0565b60005b83518110156120f2576120e28482815181106120d5576120d5614c9b565b6020026020010151613d18565b6120eb81614d60565b90506120b7565b508015612139576032805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60fc546000906001908116141561219a5760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e646578206973207061757365640000000000000060448201526064016106f1565b3360009081526002602052604090205460ff1661220e5760405162461bcd60e51b815260206004820152602c60248201527f6f6e6c794261746368436f6e6669726d65723a206e6f742066726f6d2062617460448201526b31b41031b7b73334b936b2b960a11b60648201526084016106f1565b32331461228b5760405162461bcd60e51b8152602060048201526051602482015260008051602061537a83398151915260448201527f63683a2068656164657220616e64206e6f6e7369676e65722064617461206d75606482015270737420626520696e2063616c6c6461746160781b608482015260a4016106f1565b4361229c6080850160608601614b40565b63ffffffff161061231b5760405162461bcd60e51b815260206004820152604f602482015260008051602061537a83398151915260448201527f63683a20737065636966696564207265666572656e6365426c6f636b4e756d6260648201526e657220697320696e2066757475726560881b608482015260a4016106f1565b63ffffffff431661012c6123356080860160608701614b40565b61233f9190614ea0565b63ffffffff1610156123c55760405162461bcd60e51b8152602060048201526055602482015260008051602061537a83398151915260448201527f63683a20737065636966696564207265666572656e6365426c6f636b4e756d62606482015274195c881a5cc81d1bdbc819985c881a5b881c185cdd605a1b608482015260a4016106f1565b6123d26040840184614ec8565b90506123e16020850185614ec8565b9050146124795760405162461bcd60e51b8152602060048201526066602482015260008051602061537a83398151915260448201527f63683a2071756f72756d4e756d6265727320616e64207369676e65645374616b60648201527f65466f7251756f72756d73206d757374206265206f66207468652073616d65206084820152650d8cadccee8d60d31b60a482015260c4016106f1565b600061248c61248785614f15565b613d7b565b90506000806124b8836124a26020890189614ec8565b6124b260808b0160608c01614b40565b896110ad565b9150915060005b6124cc6040880188614ec8565b905081101561260e576124e26040880188614ec8565b828181106124f2576124f2614c9b565b9050013560f81c60f81b60f81c60ff168360200151828151811061251857612518614c9b565b602002602001015161252a9190614fb5565b6001600160601b031660648460000151838151811061254b5761254b614c9b565b60200260200101516001600160601b03166125669190614fe4565b10156125fc5760405162461bcd60e51b81526020600482015260646024820181905260008051602061537a83398151915260448301527f63683a207369676e61746f7269657320646f206e6f74206f776e206174206c65908201527f617374207468726573686f6c642070657263656e74616765206f6620612071756084820152636f72756d60e01b60a482015260c4016106f1565b8061260681614d60565b9150506124bf565b506000805463ffffffff169061262388613df6565b6040805160208082018490528183018790524360e01b6001600160e01b0319166060830152825160448184030181526064830180855281519183019190912063ffffffff881660008181526001909452928590205552905191925086917fc75557c4ad49697e231449688be13ef11cb6be8ed0d18819d8dde074a5a16f8a9181900360840190a26126b5826001614ea0565b6000805463ffffffff191663ffffffff929092169190911790555050505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146127215760405162461bcd60e51b81526004016106f190615003565b604051639926ee7d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639926ee7d9061276f908590859060040161507b565b600060405180830381600087803b15801561278957600080fd5b505af1158015612139573d6000803e3d6000fd5b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146127e55760405162461bcd60e51b81526004016106f190615003565b6040516351b27a6d60e11b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063a364f4da906024015b600060405180830381600087803b15801561284957600080fd5b505af115801561285d573d6000803e3d6000fd5b5050505050565b61286c613896565b60405163a98fb35560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a98fb3559061282f9084906004016149bb565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639aa1653d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561291a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293e9190614d15565b60ff1690508061295c57505060408051600081526020810190915290565b6000805b82811015612a1157604051633ca5a5f560e01b815260ff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633ca5a5f590602401602060405180830381865afa1580156129cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f39190614cd3565b6129fd9083614d48565b915080612a0981614d60565b915050612960565b506000816001600160401b03811115612a2c57612a2c6141e5565b604051908082528060200260200182016040528015612a55578160200160208202803683370190505b5090506000805b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639aa1653d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612aba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ade9190614d15565b60ff16811015612c7757604051633ca5a5f560e01b815260ff821660048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633ca5a5f590602401602060405180830381865afa158015612b52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b769190614cd3565b905060005b81811015612c62576040516356e4026d60e11b815260ff84166004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063adc804da906044016040805180830381865afa158015612bf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c149190614d90565b60000151858581518110612c2a57612c2a614c9b565b6001600160a01b039092166020928302919091019091015283612c4c81614d60565b9450508080612c5a90614d60565b915050612b7b565b50508080612c6f90614d60565b915050612a5c565b5090949350505050565b600061012c612c93620189c084614ea0565b612c9d9190614ea0565b92915050565b612cab613896565b61070381613d18565b612cbc613896565b6001600160a01b038116612d215760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106f1565b61070381613bdc565b60fb60009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612da19190614bcf565b6001600160a01b0316336001600160a01b031614612dd15760405162461bcd60e51b81526004016106f190614bec565b60fc5419811960fc54191614612e4f5760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e756e70617573653a20696e76616c696420617474656d7060448201527f7420746f2070617573652066756e6374696f6e616c697479000000000000000060648201526084016106f1565b60fc81905560405181815233907f3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c9060200161083a565b6097546001600160a01b03163314612f1b5760405162461bcd60e51b815260206004820152604c60248201527f536572766963654d616e61676572426173652e6f6e6c7952657761726473496e60448201527f69746961746f723a2063616c6c6572206973206e6f742074686520726577617260648201526b32399034b734ba34b0ba37b960a11b608482015260a4016106f1565b60005b818110156131ef57828282818110612f3857612f38614c9b565b9050602002810190612f4a91906150c6565b612f5b9060408101906020016141af565b6001600160a01b03166323b872dd3330868686818110612f7d57612f7d614c9b565b9050602002810190612f8f91906150c6565b604080516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152013560448201526064016020604051808303816000875af1158015612fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061300a9190614c36565b50600083838381811061301f5761301f614c9b565b905060200281019061303191906150c6565b6130429060408101906020016141af565b604051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152919091169063dd62ed3e90604401602060405180830381865afa1580156130b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130d49190614cd3565b90508383838181106130e8576130e8614c9b565b90506020028101906130fa91906150c6565b61310b9060408101906020016141af565b6001600160a01b031663095ea7b37f00000000000000000000000000000000000000000000000000000000000000008387878781811061314d5761314d614c9b565b905060200281019061315f91906150c6565b6040013561316d9190614d48565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156131b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131dc9190614c36565b5050806131e890614d60565b9050612f1e565b5060405163fce36c7d60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063fce36c7d9061276f9085908590600401615141565b6001600160a01b0381166132cc5760405162461bcd60e51b815260206004820152604960248201527f5061757361626c652e5f73657450617573657252656769737472793a206e657760448201527f50617573657252656769737472792063616e6e6f7420626520746865207a65726064820152686f206164647265737360b81b608482015260a4016106f1565b60fb54604080516001600160a01b03928316815291831660208301527f6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6910160405180910390a160fb80546001600160a01b0319166001600160a01b0392909216919091179055565b60408051808201909152600080825260208201526133516140c0565b835181526020808501519082015260408082018490526000908360608460076107d05a03fa905080801561338457613386565bfe5b50806133c45760405162461bcd60e51b815260206004820152600d60248201526c1958cb5b5d5b0b59985a5b1959609a1b60448201526064016106f1565b505092915050565b60408051808201909152600080825260208201526133e86140de565b835181526020808501518183015283516040808401919091529084015160608301526000908360808460066107d05a03fa90508080156133845750806133c45760405162461bcd60e51b815260206004820152600d60248201526c1958cb5859190b59985a5b1959609a1b60448201526064016106f1565b6134686140fc565b50604080516080810182527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c28183019081527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6060830152815281518083019092527f275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec82527f1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d60208381019190915281019190915290565b60408051808201909152600080825260208201526000808061355060008051602061535a83398151915286614cb1565b90505b61355c81613e09565b909350915060008051602061535a833981519152828309831415613596576040805180820190915290815260208101919091529392505050565b60008051602061535a833981519152600182089050613553565b6040805180820182528681526020808201869052825180840190935286835282018490526000918291906135e2614121565b60005b60028110156137a75760006135fb826006614fe4565b905084826002811061360f5761360f614c9b565b60200201515183613621836000614d48565b600c811061363157613631614c9b565b602002015284826002811061364857613648614c9b565b6020020151602001518382600161365f9190614d48565b600c811061366f5761366f614c9b565b602002015283826002811061368657613686614c9b565b6020020151515183613699836002614d48565b600c81106136a9576136a9614c9b565b60200201528382600281106136c0576136c0614c9b565b60200201515160016020020151836136d9836003614d48565b600c81106136e9576136e9614c9b565b602002015283826002811061370057613700614c9b565b60200201516020015160006002811061371b5761371b614c9b565b60200201518361372c836004614d48565b600c811061373c5761373c614c9b565b602002015283826002811061375357613753614c9b565b60200201516020015160016002811061376e5761376e614c9b565b60200201518361377f836005614d48565b600c811061378f5761378f614c9b565b6020020152508061379f81614d60565b9150506135e5565b506137b0614140565b60006020826101808560088cfa9151919c9115159b50909950505050505050505050565b60606000806137e284613a32565b61ffff166001600160401b038111156137fd576137fd6141e5565b6040519080825280601f01601f191660200182016040528015613827576020820181803683370190505b5090506000805b82518210801561383f575061010081105b15612c77576001811b935085841615613886578060f81b83838151811061386857613868614c9b565b60200101906001600160f81b031916908160001a9053508160010191505b61388f81614d60565b905061382e565b6065546001600160a01b03163314611fd65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106f1565b609754604080516001600160a01b03928316815291831660208301527fe11cddf1816a43318ca175bbc52cd0185436e9cbead7c83acc54a73e461717e3910160405180910390a1609780546001600160a01b0319166001600160a01b0392909216919091179055565b60c9805460ff19168215159081179091556040519081527f40e4ed880a29e0f6ddce307457fb75cddf4feef7d3ecb0301bfdf4976a0e2dfc906020015b60405180910390a150565b6000806139ad84613e8b565b9050808360ff166001901b11613a2b5760405162461bcd60e51b815260206004820152603f60248201527f4269746d61705574696c732e6f72646572656442797465734172726179546f4260448201527f69746d61703a206269746d61702065786365656473206d61782076616c75650060648201526084016106f1565b9392505050565b6000805b8215612c9d57613a47600184614dd1565b9092169180613a558161524e565b915050613a36565b60408051808201909152600080825260208201526102008261ffff1610613ab95760405162461bcd60e51b815260206004820152601060248201526f7363616c61722d746f6f2d6c6172676560801b60448201526064016106f1565b8161ffff1660011415613acd575081612c9d565b6040805180820190915260008082526020820181905284906001905b8161ffff168661ffff1610613b3657600161ffff871660ff83161c81161415613b1957613b1684846133cc565b93505b613b2383846133cc565b92506201fffe600192831b169101613ae9565b509195945050505050565b60408051808201909152600080825260208201528151158015613b6657506020820151155b15613b84575050604080518082019091526000808252602082015290565b60405180604001604052808360000151815260200160008051602061535a8339815191528460200151613bb79190614cb1565b613bcf9060008051602061535a833981519152614dd1565b905292915050565b919050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60fb546001600160a01b0316158015613c4f57506001600160a01b03821615155b613cd15760405162461bcd60e51b815260206004820152604760248201527f5061757361626c652e5f696e697469616c697a655061757365723a205f696e6960448201527f7469616c697a6550617573657228292063616e206f6e6c792062652063616c6c6064820152666564206f6e636560c81b608482015260a4016106f1565b60fc81905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2613d148261323e565b5050565b6001600160a01b038116600081815260026020908152604091829020805460ff8082161560ff1990921682179092558351948552161515908301527f5c3265f5fb462ef4930fe47beaa183647c97f19ba545b761f41bc8cd4621d4149101613996565b6000613db882604080518082019091526000808252602082015250604080518082019091528151815260609091015163ffffffff16602082015290565b6040805182516020808301919091529092015163ffffffff16908201526060015b604051602081830303815290604052805190602001209050919050565b600081604051602001613dd991906152de565b6000808060008051602061535a833981519152600360008051602061535a8339815191528660008051602061535a833981519152888909090890506000613e7f827f0c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5260008051602061535a833981519152614018565b91959194509092505050565b600061010082511115613f145760405162461bcd60e51b8152602060048201526044602482018190527f4269746d61705574696c732e6f72646572656442797465734172726179546f42908201527f69746d61703a206f7264657265644279746573417272617920697320746f6f206064820152636c6f6e6760e01b608482015260a4016106f1565b8151613f2257506000919050565b60008083600081518110613f3857613f38614c9b565b0160200151600160f89190911c81901b92505b845181101561400f57848181518110613f6657613f66614c9b565b0160200151600160f89190911c1b9150828211613ffb5760405162461bcd60e51b815260206004820152604760248201527f4269746d61705574696c732e6f72646572656442797465734172726179546f4260448201527f69746d61703a206f72646572656442797465734172726179206973206e6f74206064820152661bdc99195c995960ca1b608482015260a4016106f1565b9181179161400881614d60565b9050613f4b565b50909392505050565b600080614023614140565b61402b61415e565b602080825281810181905260408201819052606082018890526080820187905260a082018690528260c08360056107d05a03fa92508280156133845750826140b55760405162461bcd60e51b815260206004820152601a60248201527f424e3235342e6578704d6f643a2063616c6c206661696c75726500000000000060448201526064016106f1565b505195945050505050565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806040016040528061410f61417c565b815260200161411c61417c565b905290565b604051806101800160405280600c906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180604001604052806002906020820280368337509192915050565b6001600160a01b038116811461070357600080fd5b6000602082840312156141c157600080fd5b8135613a2b8161419a565b6000602082840312156141de57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561421d5761421d6141e5565b60405290565b60405161010081016001600160401b038111828210171561421d5761421d6141e5565b604051601f8201601f191681016001600160401b038111828210171561426e5761426e6141e5565b604052919050565b60006040828403121561428857600080fd5b6142906141fb565b9050813581526020820135602082015292915050565b600082601f8301126142b757600080fd5b6142bf6141fb565b8060408401858111156142d157600080fd5b845b818110156142eb5780358452602093840193016142d3565b509095945050505050565b60006080828403121561430857600080fd5b6143106141fb565b905061431c83836142a6565b815261432b83604084016142a6565b602082015292915050565b600080600080610120858703121561434d57600080fd5b8435935061435e8660208701614276565b925061436d86606087016142f6565b915061437c8660e08701614276565b905092959194509250565b8035613bd78161419a565b6020808252825182820181905260009190848201906040850190845b818110156143d35783516001600160a01b0316835292840192918401916001016143ae565b50909695505050505050565b801515811461070357600080fd5b6000602082840312156143ff57600080fd5b8135613a2b816143df565b60ff8116811461070357600080fd5b60006020828403121561442b57600080fd5b8135613a2b8161440a565b803563ffffffff81168114613bd757600080fd5b60006001600160401b03821115614463576144636141e5565b5060051b60200190565b600082601f83011261447e57600080fd5b8135602061449361448e8361444a565b614246565b82815260059290921b840181019181810190868411156144b257600080fd5b8286015b848110156144d4576144c781614436565b83529183019183016144b6565b509695505050505050565b600082601f8301126144f057600080fd5b8135602061450061448e8361444a565b82815260069290921b8401810191818101908684111561451f57600080fd5b8286015b848110156144d4576145358882614276565b835291830191604001614523565b600082601f83011261455457600080fd5b8135602061456461448e8361444a565b82815260059290921b8401810191818101908684111561458357600080fd5b8286015b848110156144d45780356001600160401b038111156145a65760008081fd5b6145b48986838b010161446d565b845250918301918301614587565b600061018082840312156145d557600080fd5b6145dd614223565b905081356001600160401b03808211156145f657600080fd5b6146028583860161446d565b8352602084013591508082111561461857600080fd5b614624858386016144df565b6020840152604084013591508082111561463d57600080fd5b614649858386016144df565b604084015261465b85606086016142f6565b606084015261466d8560e08601614276565b608084015261012084013591508082111561468757600080fd5b6146938583860161446d565b60a08401526101408401359150808211156146ad57600080fd5b6146b98583860161446d565b60c08401526101608401359150808211156146d357600080fd5b506146e084828501614543565b60e08301525092915050565b60008060008060006080868803121561470457600080fd5b8535945060208601356001600160401b038082111561472257600080fd5b818801915088601f83011261473657600080fd5b81358181111561474557600080fd5b89602082850101111561475757600080fd5b602083019650945061476b60408901614436565b9350606088013591508082111561478157600080fd5b5061478e888289016145c2565b9150509295509295909350565b600081518084526020808501945080840160005b838110156147d45781516001600160601b0316875295820195908201906001016147af565b509495945050505050565b60408152600083516040808401526147fa608084018261479b565b90506020850151603f19848303016060850152614817828261479b565b925050508260208301529392505050565b600080600080600060a0868803121561484057600080fd5b853561484b8161419a565b9450602086810135945060408701356148638161419a565b935060608701356001600160401b0381111561487e57600080fd5b8701601f8101891361488f57600080fd5b803561489d61448e8261444a565b81815260059190911b8201830190838101908b8311156148bc57600080fd5b928401925b828410156148e35783356148d48161419a565b825292840192908401906148c1565b80965050505050506148f760808701614387565b90509295509295909350565b6000806040838503121561491657600080fd5b82356001600160401b038082111561492d57600080fd5b908401906080828703121561494157600080fd5b9092506020840135908082111561495757600080fd5b50614964858286016145c2565b9150509250929050565b6000815180845260005b8181101561499457602081850181015186830182015201614978565b818111156149a6576000602083870101525b50601f01601f19169290920160200192915050565b602081526000613a2b602083018461496e565b60006001600160401b038311156149e7576149e76141e5565b6149fa601f8401601f1916602001614246565b9050828152838383011115614a0e57600080fd5b828260208301376000602084830101529392505050565b600082601f830112614a3657600080fd5b613a2b838335602085016149ce565b60008060408385031215614a5857600080fd5b8235614a638161419a565b915060208301356001600160401b0380821115614a7f57600080fd5b9084019060608287031215614a9357600080fd5b604051606081018181108382111715614aae57614aae6141e5565b604052823582811115614ac057600080fd5b614acc88828601614a25565b82525060208301356020820152604083013560408201528093505050509250929050565b600060208284031215614b0257600080fd5b81356001600160401b03811115614b1857600080fd5b8201601f81018413614b2957600080fd5b614b38848235602084016149ce565b949350505050565b600060208284031215614b5257600080fd5b613a2b82614436565b60008060208385031215614b6e57600080fd5b82356001600160401b0380821115614b8557600080fd5b818501915085601f830112614b9957600080fd5b813581811115614ba857600080fd5b8660208260051b8501011115614bbd57600080fd5b60209290920196919550909350505050565b600060208284031215614be157600080fd5b8151613a2b8161419a565b6020808252602a908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526939903ab73830bab9b2b960b11b606082015260800190565b600060208284031215614c4857600080fd5b8151613a2b816143df565b60208082526028908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526739903830bab9b2b960c11b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600082614cce57634e487b7160e01b600052601260045260246000fd5b500690565b600060208284031215614ce557600080fd5b5051919050565b600060208284031215614cfe57600080fd5b81516001600160c01b0381168114613a2b57600080fd5b600060208284031215614d2757600080fd5b8151613a2b8161440a565b634e487b7160e01b600052601160045260246000fd5b60008219821115614d5b57614d5b614d32565b500190565b6000600019821415614d7457614d74614d32565b5060010190565b6001600160601b038116811461070357600080fd5b600060408284031215614da257600080fd5b614daa6141fb565b8251614db58161419a565b81526020830151614dc581614d7b565b60208201529392505050565b600082821015614de357614de3614d32565b500390565b600060208284031215614dfa57600080fd5b815167ffffffffffffffff1981168114613a2b57600080fd5b600060208284031215614e2557600080fd5b8151613a2b81614d7b565b60006001600160601b0383811690831681811015614e5057614e50614d32565b039392505050565b63ffffffff60e01b8360e01b1681526000600482018351602080860160005b83811015614e9357815185529382019390820190600101614e77565b5092979650505050505050565b600063ffffffff808316818516808303821115614ebf57614ebf614d32565b01949350505050565b6000808335601e19843603018112614edf57600080fd5b8301803591506001600160401b03821115614ef957600080fd5b602001915036819003821315614f0e57600080fd5b9250929050565b600060808236031215614f2757600080fd5b604051608081016001600160401b038282108183111715614f4a57614f4a6141e5565b81604052843583526020850135915080821115614f6657600080fd5b614f7236838701614a25565b60208401526040850135915080821115614f8b57600080fd5b50614f9836828601614a25565b604083015250614faa60608401614436565b606082015292915050565b60006001600160601b0380831681851681830481118215151615614fdb57614fdb614d32565b02949350505050565b6000816000190483118215151615614ffe57614ffe614d32565b500290565b60208082526052908201527f536572766963654d616e61676572426173652e6f6e6c7952656769737472794360408201527f6f6f7264696e61746f723a2063616c6c6572206973206e6f742074686520726560608201527133b4b9ba393c9031b7b7b93234b730ba37b960711b608082015260a00190565b60018060a01b03831681526040602082015260008251606060408401526150a560a084018261496e565b90506020840151606084015260408401516080840152809150509392505050565b60008235609e198336030181126150dc57600080fd5b9190910192915050565b8183526000602080850194508260005b858110156147d45781356151098161419a565b6001600160a01b031687528183013561512181614d7b565b6001600160601b03168784015260409687019691909101906001016150f6565b60208082528181018390526000906040808401600586901b8501820187855b8881101561524057878303603f190184528135368b9003609e1901811261518657600080fd5b8a0160a0813536839003601e1901811261519f57600080fd5b820180356001600160401b038111156151b757600080fd5b8060061b36038413156151c957600080fd5b8287526151db838801828c85016150e6565b925050506151ea888301614387565b6001600160a01b0316888601528187013587860152606061520c818401614436565b63ffffffff16908601526080615223838201614436565b63ffffffff16950194909452509285019290850190600101615160565b509098975050505050505050565b600061ffff8083168181141561526657615266614d32565b6001019392505050565b6000808335601e1984360301811261528757600080fd5b83016020810192503590506001600160401b038111156152a657600080fd5b803603831315614f0e57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081528135602082015260006152f86020840184615270565b6080604085015261530d60a0850182846152b5565b91505061531d6040850185615270565b848303601f190160608601526153348382846152b5565b9250505063ffffffff61534960608601614436565b166080840152809150509291505056fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47456967656e4441536572766963654d616e616765722e636f6e6669726d426174424c535369676e6174757265436865636b65722e636865636b5369676e617475a264697066735822122008275249ec223e380ed13f4b0596eded1ae53958c6cf6885326bd25b493a845a64736f6c634300080c0033 + /// ``` + #[rustfmt::skip] + #[allow(clippy::all)] + pub static BYTECODE: alloy_sol_types::private::Bytes = alloy_sol_types::private::Bytes::from_static( + b"a\x01\x80`@R4\x80\x15b\0\0\x12W`\0\x80\xFD[P`@Qb\0Xq8\x03\x80b\0Xq\x839\x81\x01`@\x81\x90Rb\0\x005\x91b\0\x02\xE5V[`\x01`\x01`\xA0\x1B\x03\x80\x85\x16`\x80R\x80\x84\x16`\xA0R\x80\x83\x16`\xC0R\x81\x16`\xE0R\x81\x84\x84\x82\x84b\0\0cb\0\x02\nV[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16a\x01\0\x81`\x01`\x01`\xA0\x1B\x03\x16\x81RPP\x80`\x01`\x01`\xA0\x1B\x03\x16ch0H5`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15b\0\0\xC1W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90b\0\0\xE7\x91\x90b\0\x03MV[`\x01`\x01`\xA0\x1B\x03\x16a\x01 \x81`\x01`\x01`\xA0\x1B\x03\x16\x81RPP\x80`\x01`\x01`\xA0\x1B\x03\x16c]\xF4YF`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15b\0\x01@W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90b\0\x01f\x91\x90b\0\x03MV[`\x01`\x01`\xA0\x1B\x03\x16a\x01@\x81`\x01`\x01`\xA0\x1B\x03\x16\x81RPPa\x01 Q`\x01`\x01`\xA0\x1B\x03\x16c\xDF\\\xF7#`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15b\0\x01\xC2W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90b\0\x01\xE8\x91\x90b\0\x03MV[`\x01`\x01`\xA0\x1B\x03\x16a\x01`RPb\0\x02\0b\0\x02\nV[PPPPb\0\x03tV[`2Ta\x01\0\x90\x04`\xFF\x16\x15b\0\x02wW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`'`$\x82\x01R\x7FInitializable: contract is initi`D\x82\x01Rfalizing`\xC8\x1B`d\x82\x01R`\x84\x01`@Q\x80\x91\x03\x90\xFD[`2T`\xFF\x90\x81\x16\x10\x15b\0\x02\xCAW`2\x80T`\xFF\x19\x16`\xFF\x90\x81\x17\x90\x91U`@Q\x90\x81R\x7F\x7F&\xB8?\xF9n\x1F+jh/\x138R\xF6y\x8A\t\xC4e\xDA\x95\x92\x14`\xCE\xFB8G@$\x98\x90` \x01`@Q\x80\x91\x03\x90\xA1[V[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14b\0\x02\xE2W`\0\x80\xFD[PV[`\0\x80`\0\x80`\x80\x85\x87\x03\x12\x15b\0\x02\xFCW`\0\x80\xFD[\x84Qb\0\x03\t\x81b\0\x02\xCCV[` \x86\x01Q\x90\x94Pb\0\x03\x1C\x81b\0\x02\xCCV[`@\x86\x01Q\x90\x93Pb\0\x03/\x81b\0\x02\xCCV[``\x86\x01Q\x90\x92Pb\0\x03B\x81b\0\x02\xCCV[\x93\x96\x92\x95P\x90\x93PPV[`\0` \x82\x84\x03\x12\x15b\0\x03`W`\0\x80\xFD[\x81Qb\0\x03m\x81b\0\x02\xCCV[\x93\x92PPPV[`\x80Q`\xA0Q`\xC0Q`\xE0Qa\x01\0Qa\x01 Qa\x01@Qa\x01`QaS\xEFb\0\x04\x82`\09`\0\x81\x81a\x05i\x01Ra\x17\x95\x01R`\0\x81\x81a\x03l\x01Ra\x19w\x01R`\0\x81\x81a\x03\xBE\x01R\x81\x81a\x1BM\x01Ra\x1D\x0F\x01R`\0\x81\x81a\x04\x0B\x01R\x81\x81a\x0E\xB1\x01R\x81\x81a\x14`\x01R\x81\x81a\x15\xF8\x01Ra\x182\x01R`\0\x81\x81a\x0B\xC9\x01R\x81\x81a\r$\x01R\x81\x81a\r\xBB\x01R\x81\x81a)\x80\x01R\x81\x81a+\x03\x01Ra+\xA2\x01R`\0\x81\x81a\t\xF4\x01R\x81\x81a\n\x83\x01R\x81\x81a\x0B\x03\x01R\x81\x81a&\xE4\x01R\x81\x81a'\xA8\x01R\x81\x81a(\xBE\x01Ra*^\x01R`\0\x81\x81a0_\x01R\x81\x81a1\x1B\x01Ra2\x07\x01R`\0\x81\x81a\x03\xE2\x01R\x81\x81a'8\x01R\x81\x81a(\x04\x01Ra(\x83\x01RaS\xEF`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x02SW`\x005`\xE0\x1C\x80cw\x94\x96Z\x11a\x01FW\x80c\xDF\\\xF7#\x11a\0\xC3W\x80c\xEF\x02DX\x11a\0\x87W\x80c\xEF\x02DX\x14a\x05\xE3W\x80c\xF1\"\t\x83\x14a\x05\xEBW\x80c\xF2\xFD\xE3\x8B\x14a\x05\xFEW\x80c\xFA\xBC\x1C\xBC\x14a\x06\x11W\x80c\xFC)\x9D\xEE\x14a\x06$W\x80c\xFC\xE3l}\x14a\x067W`\0\x80\xFD[\x80c\xDF\\\xF7#\x14a\x05dW\x80c\xE1R4\xFF\x14a\x05\x8BW\x80c\xE4\x81\xAF\x9D\x14a\x05\xA8W\x80c\xEA\xEF\xD2}\x14a\x05\xB0W\x80c\xEC\xCB\xBF\xC9\x14a\x05\xC3W`\0\x80\xFD[\x80c\xA3d\xF4\xDA\x11a\x01\nW\x80c\xA3d\xF4\xDA\x14a\x04\xEEW\x80c\xA5\xB7\x89\n\x14a\x05\x01W\x80c\xA9\x8F\xB3U\x14a\x05$W\x80c\xB9\x8D\t\x08\x14a\x057W\x80c\xBA\xFA\x91\x07\x14a\x05DW`\0\x80\xFD[\x80cw\x94\x96Z\x14a\x04wW\x80c\x86\x87\xFE\xAE\x14a\x04\x8AW\x80c\x88o\x11\x95\x14a\x04\xB7W\x80c\x8D\xA5\xCB[\x14a\x04\xCAW\x80c\x99&\xEE}\x14a\x04\xDBW`\0\x80\xFD[\x80c]\xF4YF\x11a\x01\xD4W\x80cm\x14\xA9\x87\x11a\x01\x98W\x80cm\x14\xA9\x87\x14a\x04\x06W\x80cn\xFBF6\x14a\x04-W\x80cqP\x18\xA6\x14a\x04NW\x80cr\xD1\x8E\x8D\x14a\x04VW\x80cw[\xBC\xB5\x14a\x04dW`\0\x80\xFD[\x80c]\xF4YF\x14a\x03gW\x80c^\x034v\x14a\x03\xA6W\x80c^\x8B?-\x14a\x03\xB0W\x80ch0H5\x14a\x03\xB9W\x80ck:\xA7.\x14a\x03\xE0W`\0\x80\xFD[\x80cAl~^\x11a\x02\x1BW\x80cAl~^\x14a\x02\xE2W\x80cIr\x13J\x14a\x02\xF5W\x80cY\\jg\x14a\x03\x1AW\x80cZ\xC8j\xB7\x14a\x03\"W\x80c\\\x97Z\xBB\x14a\x03UW`\0\x80\xFD[\x80c\x10\xD6z/\x14a\x02XW\x80c\x13d9\xDD\x14a\x02mW\x80c\x17\x1F\x1D[\x14a\x02\x80W\x80c3\xCF\xB7\xB7\x14a\x02\xAFW\x80c;\xC2\x8C\x8C\x14a\x02\xCFW[`\0\x80\xFD[a\x02ka\x02f6`\x04aA\xAFV[a\x06JV[\0[a\x02ka\x02{6`\x04aA\xCCV[a\x07\x06V[a\x02\x93a\x02\x8E6`\x04aC6V[a\x08EV[`@\x80Q\x92\x15\x15\x83R\x90\x15\x15` \x83\x01R\x01[`@Q\x80\x91\x03\x90\xF3[a\x02\xC2a\x02\xBD6`\x04aA\xAFV[a\t\xCFV[`@Qa\x02\xA6\x91\x90aC\x92V[a\x02ka\x02\xDD6`\x04aA\xAFV[a\x0E\x9EV[a\x02ka\x02\xF06`\x04aC\xEDV[a\x0E\xAFV[`\0Ta\x03\x05\x90c\xFF\xFF\xFF\xFF\x16\x81V[`@Qc\xFF\xFF\xFF\xFF\x90\x91\x16\x81R` \x01a\x02\xA6V[a\x02ka\x0F\xE6V[a\x03Ea\x0306`\x04aD\x19V[`\xFCT`\x01`\xFF\x90\x92\x16\x91\x90\x91\x1B\x90\x81\x16\x14\x90V[`@Q\x90\x15\x15\x81R` \x01a\x02\xA6V[`\xFCT[`@Q\x90\x81R` \x01a\x02\xA6V[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x02\xA6V[a\x03\x05b\x01\x89\xC0\x81V[a\x03\x05a\x01,\x81V[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0a\x03\x8EV[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[a\x04@a\x04;6`\x04aF\xECV[a\x10\xADV[`@Qa\x02\xA6\x92\x91\x90aG\xDFV[a\x02ka\x1F\xC4V[`\0Tc\xFF\xFF\xFF\xFF\x16a\x03\x05V[a\x02ka\x04r6`\x04aH(V[a\x1F\xD8V[a\x02ka\x04\x856`\x04aI\x03V[a!AV[a\x04\xAA`@Q\x80`@\x01`@R\x80`\x01\x81R` \x01`!`\xF8\x1B\x81RP\x81V[`@Qa\x02\xA6\x91\x90aI\xBBV[`\xFBTa\x03\x8E\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`eT`\x01`\x01`\xA0\x1B\x03\x16a\x03\x8EV[a\x02ka\x04\xE96`\x04aJEV[a&\xD9V[a\x02ka\x04\xFC6`\x04aA\xAFV[a'\x9DV[a\x03Ea\x05\x0F6`\x04aA\xAFV[`\x02` R`\0\x90\x81R`@\x90 T`\xFF\x16\x81V[a\x02ka\x0526`\x04aJ\xF0V[a(dV[`\xC9Ta\x03E\x90`\xFF\x16\x81V[a\x04\xAA`@Q\x80`@\x01`@R\x80`\x01\x81R` \x01`7`\xF8\x1B\x81RP\x81V[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[a\x04\xAA`@Q\x80`@\x01`@R\x80`\x01\x81R` \x01`\0\x81RP\x81V[a\x02\xC2a(\xB8V[a\x03\x05a\x05\xBE6`\x04aK@V[a,\x81V[a\x03Ya\x05\xD16`\x04aK@V[`\x01` R`\0\x90\x81R`@\x90 T\x81V[a\x03Y`d\x81V[a\x02ka\x05\xF96`\x04aA\xAFV[a,\xA3V[a\x02ka\x06\x0C6`\x04aA\xAFV[a,\xB4V[a\x02ka\x06\x1F6`\x04aA\xCCV[a-*V[`\x97Ta\x03\x8E\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[a\x02ka\x06E6`\x04aK[V[a.\x86V[`\xFB`\0\x90T\x90a\x01\0\n\x90\x04`\x01`\x01`\xA0\x1B\x03\x16`\x01`\x01`\xA0\x1B\x03\x16c\xEA\xB6mz`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06\x9DW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06\xC1\x91\x90aK\xCFV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\xFAW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aK\xECV[`@Q\x80\x91\x03\x90\xFD[a\x07\x03\x81a2>V[PV[`\xFBT`@Qc#}\xFBG`\xE1\x1B\x81R3`\x04\x82\x01R`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x90cF\xFB\xF6\x8E\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07NW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07r\x91\x90aL6V[a\x07\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aLSV[`\xFCT\x81\x81\x16\x14a\x08\x07W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7FPausable.pause: invalid attempt `D\x82\x01R\x7Fto unpause functionality\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`\xFC\x81\x90U`@Q\x81\x81R3\x90\x7F\xAB@\xA3t\xBCQ\xDE7\"\0\xA8\xBC\x98\x1A\xF8\xC9\xEC\xDC\x08\xDF\xDA\xEF\x0B\xB6\xE0\x9F\x88\xF3\xC6\x16\xEF=\x90` \x01[`@Q\x80\x91\x03\x90\xA2PV[`\0\x80`\0\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x87\x87`\0\x01Q\x88` \x01Q\x88`\0\x01Q`\0`\x02\x81\x10a\x08\x8DWa\x08\x8DaL\x9BV[` \x02\x01Q\x89Q`\x01` \x02\x01Q\x8A` \x01Q`\0`\x02\x81\x10a\x08\xB2Wa\x08\xB2aL\x9BV[` \x02\x01Q\x8B` \x01Q`\x01`\x02\x81\x10a\x08\xCEWa\x08\xCEaL\x9BV[` \x90\x81\x02\x91\x90\x91\x01Q\x8CQ\x8D\x83\x01Q`@Qa\t+\x9A\x99\x98\x97\x96\x95\x94\x01\x98\x89R` \x89\x01\x97\x90\x97R`@\x88\x01\x95\x90\x95R``\x87\x01\x93\x90\x93R`\x80\x86\x01\x91\x90\x91R`\xA0\x85\x01R`\xC0\x84\x01R`\xE0\x83\x01Ra\x01\0\x82\x01Ra\x01 \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 `\0\x1Ca\tN\x91\x90aL\xB1V[\x90Pa\t\xC1a\tga\t`\x88\x84a35V[\x86\x90a3\xCCV[a\toa4`V[a\t\xB7a\t\xA8\x85a\t\xA2`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x91\x82\x01R\x81Q\x80\x83\x01\x90\x92R`\x01\x82R`\x02\x90\x82\x01R\x90V[\x90a35V[a\t\xB1\x8Ca5 V[\x90a3\xCCV[\x88b\x01\xD4\xC0a5\xB0V[\x90\x98\x90\x97P\x95PPPPPPV[`@Qc\t\xAA\x15'`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x82\x81\x16`\x04\x83\x01R``\x91`\0\x91\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\x13T*N\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\n;W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n_\x91\x90aL\xD3V[`@Qc\x87\x1E\xF0I`\xE0\x1B\x81R`\x04\x81\x01\x82\x90R\x90\x91P`\0\x90`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\x87\x1E\xF0I\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\n\xCAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xEE\x91\x90aL\xECV[\x90P`\x01`\x01`\xC0\x1B\x03\x81\x16\x15\x80a\x0B\x88WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x9A\xA1e=`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0B_W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0B\x83\x91\x90aM\x15V[`\xFF\x16\x15[\x15a\x0B\xA4WPP`@\x80Q`\0\x81R` \x81\x01\x90\x91R\x92\x91PPV[`\0a\x0B\xB8\x82`\x01`\x01`\xC0\x1B\x03\x16a7\xD4V[\x90P`\0\x80[\x82Q\x81\x10\x15a\x0C\x8EW\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c<\xA5\xA5\xF5\x84\x83\x81Q\x81\x10a\x0C\x08Wa\x0C\x08aL\x9BV[\x01` \x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x84\x90\x1B\x16\x81R`\xF8\x91\x90\x91\x1C`\x04\x82\x01R`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0CLW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0Cp\x91\x90aL\xD3V[a\x0Cz\x90\x83aMHV[\x91P\x80a\x0C\x86\x81aM`V[\x91PPa\x0B\xBEV[P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xA9Wa\x0C\xA9aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0C\xD2W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P`\0\x80[\x84Q\x81\x10\x15a\x0E\x91W`\0\x85\x82\x81Q\x81\x10a\x0C\xF6Wa\x0C\xF6aL\x9BV[\x01` \x01Q`@Qc<\xA5\xA5\xF5`\xE0\x1B\x81R`\xF8\x91\x90\x91\x1C`\x04\x82\x01\x81\x90R\x91P`\0\x90`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c<\xA5\xA5\xF5\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\rkW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\r\x8F\x91\x90aL\xD3V[\x90P`\0[\x81\x81\x10\x15a\x0E{W`@QcV\xE4\x02m`\xE1\x1B\x81R`\xFF\x84\x16`\x04\x82\x01R`$\x81\x01\x82\x90R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c\xAD\xC8\x04\xDA\x90`D\x01`@\x80Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\tW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E-\x91\x90aM\x90V[`\0\x01Q\x86\x86\x81Q\x81\x10a\x0ECWa\x0ECaL\x9BV[`\x01`\x01`\xA0\x1B\x03\x90\x92\x16` \x92\x83\x02\x91\x90\x91\x01\x90\x91\x01R\x84a\x0Ee\x81aM`V[\x95PP\x80\x80a\x0Es\x90aM`V[\x91PPa\r\x94V[PPP\x80\x80a\x0E\x89\x90aM`V[\x91PPa\x0C\xD9V[P\x90\x97\x96PPPPPPPV[a\x0E\xA6a8\x96V[a\x07\x03\x81a8\xF0V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x8D\xA5\xCB[`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0F\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0F1\x91\x90aK\xCFV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x0F\xDDW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\\`$\x82\x01R\x7FBLSSignatureChecker.onlyCoordina`D\x82\x01R\x7FtorOwner: caller is not the owne`d\x82\x01R\x7Fr of the registryCoordinator\0\0\0\0`\x84\x82\x01R`\xA4\x01a\x06\xF1V[a\x07\x03\x81a9YV[`\xFBT`@Qc#}\xFBG`\xE1\x1B\x81R3`\x04\x82\x01R`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x90cF\xFB\xF6\x8E\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x10.W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10R\x91\x90aL6V[a\x10nW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aLSV[`\0\x19`\xFC\x81\x90U`@Q\x90\x81R3\x90\x7F\xAB@\xA3t\xBCQ\xDE7\"\0\xA8\xBC\x98\x1A\xF8\xC9\xEC\xDC\x08\xDF\xDA\xEF\x0B\xB6\xE0\x9F\x88\xF3\xC6\x16\xEF=\x90` \x01`@Q\x80\x91\x03\x90\xA2V[`@\x80Q\x80\x82\x01\x90\x91R``\x80\x82R` \x82\x01R`\0\x84a\x11$W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: empty quorum input\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`@\x83\x01QQ\x85\x14\x80\x15a\x11W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`D`$\x82\x01\x81\x90R`\0\x80Q` aS\x9A\x839\x81Q\x91R\x90\x82\x01R\x7Fres: input nonsigner length mism`d\x82\x01Rc\x0C.\x8Cm`\xE3\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[Cc\xFF\xFF\xFF\xFF\x16\x84c\xFF\xFF\xFF\xFF\x16\x10a\x12\xADW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`<`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: invalid reference block\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x80\x83\x01\x91\x90\x91R\x82Q\x80\x84\x01\x90\x93R``\x80\x84R\x90\x83\x01R\x90\x86`\x01`\x01`@\x1B\x03\x81\x11\x15a\x12\xEEWa\x12\xEEaA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x13\x17W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P` \x82\x01R\x86`\x01`\x01`@\x1B\x03\x81\x11\x15a\x135Wa\x135aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x13^W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R`@\x80Q\x80\x82\x01\x90\x91R``\x80\x82R` \x82\x01R\x85` \x01QQ`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x92Wa\x13\x92aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x13\xBBW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x86\x01QQ`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\xDBWa\x13\xDBaA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x14\x04W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81` \x01\x81\x90RP`\0a\x14\xD6\x8A\x8A\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847`\0\x92\x01\x91\x90\x91RPP`@\x80Qc\x9A\xA1e=`\xE0\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x93Pc\x9A\xA1e=\x92P`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x14\xADW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x14\xD1\x91\x90aM\x15V[a9\xA1V[\x90P`\0[\x87` \x01QQ\x81\x10\x15a\x17qWa\x15 \x88` \x01Q\x82\x81Q\x81\x10a\x15\x01Wa\x15\x01aL\x9BV[` \x02` \x01\x01Q\x80Q`\0\x90\x81R` \x91\x82\x01Q\x90\x91R`@\x90 \x90V[\x83` \x01Q\x82\x81Q\x81\x10a\x156Wa\x156aL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01R\x80\x15a\x15\xF6W` \x83\x01Qa\x15W`\x01\x83aM\xD1V[\x81Q\x81\x10a\x15gWa\x15gaL\x9BV[` \x02` \x01\x01Q`\0\x1C\x83` \x01Q\x82\x81Q\x81\x10a\x15\x88Wa\x15\x88aL\x9BV[` \x02` \x01\x01Q`\0\x1C\x11a\x15\xF6W`@\x80QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`$\x81\x01\x91\x90\x91R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: nonSignerPubkeys not sorted`d\x82\x01R`\x84\x01a\x06\xF1V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x04\xECcQ\x84` \x01Q\x83\x81Q\x81\x10a\x16;Wa\x16;aL\x9BV[` \x02` \x01\x01Q\x8B\x8B`\0\x01Q\x85\x81Q\x81\x10a\x16ZWa\x16ZaL\x9BV[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x16\x97\x93\x92\x91\x90\x92\x83Rc\xFF\xFF\xFF\xFF\x91\x82\x16` \x84\x01R\x16`@\x82\x01R``\x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x16\xB4W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x16\xD8\x91\x90aL\xECV[`\x01`\x01`\xC0\x1B\x03\x16\x83`\0\x01Q\x82\x81Q\x81\x10a\x16\xF7Wa\x16\xF7aL\x9BV[` \x02` \x01\x01\x81\x81RPPa\x17]a\t`a\x171\x84\x86`\0\x01Q\x85\x81Q\x81\x10a\x17#Wa\x17#aL\x9BV[` \x02` \x01\x01Q\x16a:2V[\x8A` \x01Q\x84\x81Q\x81\x10a\x17GWa\x17GaL\x9BV[` \x02` \x01\x01Qa:]\x90\x91\x90c\xFF\xFF\xFF\xFF\x16V[\x94P\x80a\x17i\x81aM`V[\x91PPa\x14\xDBV[PPa\x17|\x83a;AV[`\xC9T\x90\x93P`\xFF\x16`\0\x81a\x17\x93W`\0a\x18\x15V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\xC4H\xFE\xB8`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xF1W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x18\x15\x91\x90aL\xD3V[\x90P`\0[\x8A\x81\x10\x15a\x1E\x93W\x82\x15a\x19uW\x89c\xFF\xFF\xFF\xFF\x16\x82\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c$\x9A\x0CB\x8F\x8F\x86\x81\x81\x10a\x18qWa\x18qaL\x9BV[`@Q`\xE0\x85\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81R\x92\x015`\xF8\x1C`\x04\x83\x01RP`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x18\xB1W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x18\xD5\x91\x90aL\xD3V[a\x18\xDF\x91\x90aMHV[\x11a\x19uW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`f`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: StakeRegistry updates must `d\x82\x01R\x7Fbe within withdrawalDelayBlocks `\x84\x82\x01Rewindow`\xD0\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16ch\xBC\xCA\xAC\x8D\x8D\x84\x81\x81\x10a\x19\xB6Wa\x19\xB6aL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C\x8C\x8C`\xA0\x01Q\x85\x81Q\x81\x10a\x19\xDAWa\x19\xDAaL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x86\x90\x1B\x16\x81R`\xFF\x90\x93\x16`\x04\x84\x01Rc\xFF\xFF\xFF\xFF\x91\x82\x16`$\x84\x01R\x16`D\x82\x01R`d\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1AZ\x91\x90aM\xE8V[`\x01`\x01`@\x1B\x03\x19\x16a\x1A}\x8A`@\x01Q\x83\x81Q\x81\x10a\x15\x01Wa\x15\x01aL\x9BV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x19\x16\x14a\x1B\x19W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`a`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: quorumApk hash in storage d`d\x82\x01R\x7Foes not match provided quorum ap`\x84\x82\x01R`k`\xF8\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[a\x1BI\x89`@\x01Q\x82\x81Q\x81\x10a\x1B2Wa\x1B2aL\x9BV[` \x02` \x01\x01Q\x87a3\xCC\x90\x91\x90c\xFF\xFF\xFF\xFF\x16V[\x95P\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\xC8)LV\x8D\x8D\x84\x81\x81\x10a\x1B\x8CWa\x1B\x8CaL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C\x8C\x8C`\xC0\x01Q\x85\x81Q\x81\x10a\x1B\xB0Wa\x1B\xB0aL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x86\x90\x1B\x16\x81R`\xFF\x90\x93\x16`\x04\x84\x01Rc\xFF\xFF\xFF\xFF\x91\x82\x16`$\x84\x01R\x16`D\x82\x01R`d\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1C\x0CW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1C0\x91\x90aN\x13V[\x85` \x01Q\x82\x81Q\x81\x10a\x1CFWa\x1CFaL\x9BV[`\x01`\x01``\x1B\x03\x90\x92\x16` \x92\x83\x02\x91\x90\x91\x01\x82\x01R\x85\x01Q\x80Q\x82\x90\x81\x10a\x1CrWa\x1CraL\x9BV[` \x02` \x01\x01Q\x85`\0\x01Q\x82\x81Q\x81\x10a\x1C\x90Wa\x1C\x90aL\x9BV[` \x02` \x01\x01\x90`\x01`\x01``\x1B\x03\x16\x90\x81`\x01`\x01``\x1B\x03\x16\x81RPP`\0\x80[\x8A` \x01QQ\x81\x10\x15a\x1E~Wa\x1D\x08\x86`\0\x01Q\x82\x81Q\x81\x10a\x1C\xDAWa\x1C\xDAaL\x9BV[` \x02` \x01\x01Q\x8F\x8F\x86\x81\x81\x10a\x1C\xF4Wa\x1C\xF4aL\x9BV[`\x01\x92\x015`\xF8\x1C\x92\x90\x92\x1C\x81\x16\x14\x91\x90PV[\x15a\x1ElW\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\xF2\xBE\x94\xAE\x8F\x8F\x86\x81\x81\x10a\x1DNWa\x1DNaL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C\x8E\x89` \x01Q\x85\x81Q\x81\x10a\x1DrWa\x1DraL\x9BV[` \x02` \x01\x01Q\x8F`\xE0\x01Q\x88\x81Q\x81\x10a\x1D\x90Wa\x1D\x90aL\x9BV[` \x02` \x01\x01Q\x87\x81Q\x81\x10a\x1D\xA9Wa\x1D\xA9aL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x87\x90\x1B\x16\x81R`\xFF\x90\x94\x16`\x04\x85\x01Rc\xFF\xFF\xFF\xFF\x92\x83\x16`$\x85\x01R`D\x84\x01\x91\x90\x91R\x16`d\x82\x01R`\x84\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1E\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1E1\x91\x90aN\x13V[\x87Q\x80Q\x85\x90\x81\x10a\x1EEWa\x1EEaL\x9BV[` \x02` \x01\x01\x81\x81Qa\x1EY\x91\x90aN0V[`\x01`\x01``\x1B\x03\x16\x90RP`\x01\x90\x91\x01\x90[\x80a\x1Ev\x81aM`V[\x91PPa\x1C\xB4V[PP\x80\x80a\x1E\x8B\x90aM`V[\x91PPa\x18\x1AV[PPP`\0\x80a\x1E\xAD\x8C\x86\x8A``\x01Q\x8B`\x80\x01Qa\x08EV[\x91P\x91P\x81a\x1F\x1EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`C`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: pairing precompile call fai`d\x82\x01Rb\x1B\x19Y`\xEA\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[\x80a\x1F\x7FW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`9`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: signature is invalid\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[PP`\0\x87\x82` \x01Q`@Q` \x01a\x1F\x9A\x92\x91\x90aNXV[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x80Q` \x90\x91\x01 \x92\x9B\x92\x9AP\x91\x98PPPPPPPPPV[a\x1F\xCCa8\x96V[a\x1F\xD6`\0a;\xDCV[V[`2Ta\x01\0\x90\x04`\xFF\x16\x15\x80\x80\x15a\x1F\xF8WP`2T`\x01`\xFF\x90\x91\x16\x10[\x80a \x12WP0;\x15\x80\x15a \x12WP`2T`\xFF\x16`\x01\x14[a uW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`.`$\x82\x01R\x7FInitializable: contract is alrea`D\x82\x01Rm\x19\x1EH\x1A[\x9A]\x1AX[\x1A^\x99Y`\x92\x1B`d\x82\x01R`\x84\x01a\x06\xF1V[`2\x80T`\xFF\x19\x16`\x01\x17\x90U\x80\x15a \x98W`2\x80Ta\xFF\0\x19\x16a\x01\0\x17\x90U[a \xA2\x86\x86a<.V[a \xAB\x84a;\xDCV[a \xB4\x82a8\xF0V[`\0[\x83Q\x81\x10\x15a \xF2Wa \xE2\x84\x82\x81Q\x81\x10a \xD5Wa \xD5aL\x9BV[` \x02` \x01\x01Qa=\x18V[a \xEB\x81aM`V[\x90Pa \xB7V[P\x80\x15a!9W`2\x80Ta\xFF\0\x19\x16\x90U`@Q`\x01\x81R\x7F\x7F&\xB8?\xF9n\x1F+jh/\x138R\xF6y\x8A\t\xC4e\xDA\x95\x92\x14`\xCE\xFB8G@$\x98\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPV[`\xFCT`\0\x90`\x01\x90\x81\x16\x14\x15a!\x9AW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01R\x7FPausable: index is paused\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\x06\xF1V[3`\0\x90\x81R`\x02` R`@\x90 T`\xFF\x16a\"\x0EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`,`$\x82\x01R\x7FonlyBatchConfirmer: not from bat`D\x82\x01Rk1\xB4\x101\xB7\xB734\xB96\xB2\xB9`\xA1\x1B`d\x82\x01R`\x84\x01a\x06\xF1V[23\x14a\"\x8BW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`Q`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: header and nonsigner data mu`d\x82\x01Rpst be in calldata`x\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[Ca\"\x9C`\x80\x85\x01``\x86\x01aK@V[c\xFF\xFF\xFF\xFF\x16\x10a#\x1BW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`O`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: specified referenceBlockNumb`d\x82\x01Rner is in future`\x88\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[c\xFF\xFF\xFF\xFFC\x16a\x01,a#5`\x80\x86\x01``\x87\x01aK@V[a#?\x91\x90aN\xA0V[c\xFF\xFF\xFF\xFF\x16\x10\x15a#\xC5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`U`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: specified referenceBlockNumb`d\x82\x01Rt\x19\\\x88\x1A\\\xC8\x1D\x1B\xDB\xC8\x19\x98\\\x88\x1A[\x88\x1C\x18\\\xDD`Z\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[a#\xD2`@\x84\x01\x84aN\xC8V[\x90Pa#\xE1` \x85\x01\x85aN\xC8V[\x90P\x14a$yW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`f`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: quorumNumbers and signedStak`d\x82\x01R\x7FeForQuorums must be of the same `\x84\x82\x01Re\r\x8C\xAD\xCC\xEE\x8D`\xD3\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[`\0a$\x8Ca$\x87\x85aO\x15V[a={V[\x90P`\0\x80a$\xB8\x83a$\xA2` \x89\x01\x89aN\xC8V[a$\xB2`\x80\x8B\x01``\x8C\x01aK@V[\x89a\x10\xADV[\x91P\x91P`\0[a$\xCC`@\x88\x01\x88aN\xC8V[\x90P\x81\x10\x15a&\x0EWa$\xE2`@\x88\x01\x88aN\xC8V[\x82\x81\x81\x10a$\xF2Wa$\xF2aL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C`\xFF\x16\x83` \x01Q\x82\x81Q\x81\x10a%\x18Wa%\x18aL\x9BV[` \x02` \x01\x01Qa%*\x91\x90aO\xB5V[`\x01`\x01``\x1B\x03\x16`d\x84`\0\x01Q\x83\x81Q\x81\x10a%KWa%KaL\x9BV[` \x02` \x01\x01Q`\x01`\x01``\x1B\x03\x16a%f\x91\x90aO\xE4V[\x10\x15a%\xFCW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`d`$\x82\x01\x81\x90R`\0\x80Q` aSz\x839\x81Q\x91R`D\x83\x01R\x7Fch: signatories do not own at le\x90\x82\x01R\x7Fast threshold percentage of a qu`\x84\x82\x01Rcorum`\xE0\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[\x80a&\x06\x81aM`V[\x91PPa$\xBFV[P`\0\x80Tc\xFF\xFF\xFF\xFF\x16\x90a&#\x88a=\xF6V[`@\x80Q` \x80\x82\x01\x84\x90R\x81\x83\x01\x87\x90RC`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16``\x83\x01R\x82Q`D\x81\x84\x03\x01\x81R`d\x83\x01\x80\x85R\x81Q\x91\x83\x01\x91\x90\x91 c\xFF\xFF\xFF\xFF\x88\x16`\0\x81\x81R`\x01\x90\x94R\x92\x85\x90 UR\x90Q\x91\x92P\x86\x91\x7F\xC7UW\xC4\xADIi~#\x14Ih\x8B\xE1>\xF1\x1C\xB6\xBE\x8E\xD0\xD1\x88\x19\xD8\xDD\xE0t\xA5\xA1o\x8A\x91\x81\x90\x03`\x84\x01\x90\xA2a&\xB5\x82`\x01aN\xA0V[`\0\x80Tc\xFF\xFF\xFF\xFF\x19\x16c\xFF\xFF\xFF\xFF\x92\x90\x92\x16\x91\x90\x91\x17\x90UPPPPPPPPV[3`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a'!W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aP\x03V[`@Qc\x99&\xEE}`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\x99&\xEE}\x90a'o\x90\x85\x90\x85\x90`\x04\x01aP{V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a'\x89W`\0\x80\xFD[PZ\xF1\x15\x80\x15a!9W=`\0\x80>=`\0\xFD[3`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a'\xE5W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aP\x03V[`@QcQ\xB2zm`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x82\x81\x16`\x04\x83\x01R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\xA3d\xF4\xDA\x90`$\x01[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a(IW`\0\x80\xFD[PZ\xF1\x15\x80\x15a(]W=`\0\x80>=`\0\xFD[PPPPPV[a(la8\x96V[`@Qc\xA9\x8F\xB3U`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\xA9\x8F\xB3U\x90a(/\x90\x84\x90`\x04\x01aI\xBBV[```\0\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x9A\xA1e=`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a)\x1AW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a)>\x91\x90aM\x15V[`\xFF\x16\x90P\x80a)\\WPP`@\x80Q`\0\x81R` \x81\x01\x90\x91R\x90V[`\0\x80[\x82\x81\x10\x15a*\x11W`@Qc<\xA5\xA5\xF5`\xE0\x1B\x81R`\xFF\x82\x16`\x04\x82\x01R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c<\xA5\xA5\xF5\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a)\xCFW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a)\xF3\x91\x90aL\xD3V[a)\xFD\x90\x83aMHV[\x91P\x80a*\t\x81aM`V[\x91PPa)`V[P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a*,Wa*,aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a*UW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P`\0\x80[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x9A\xA1e=`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a*\xBAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a*\xDE\x91\x90aM\x15V[`\xFF\x16\x81\x10\x15a,wW`@Qc<\xA5\xA5\xF5`\xE0\x1B\x81R`\xFF\x82\x16`\x04\x82\x01R`\0\x90\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c<\xA5\xA5\xF5\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a+RW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a+v\x91\x90aL\xD3V[\x90P`\0[\x81\x81\x10\x15a,bW`@QcV\xE4\x02m`\xE1\x1B\x81R`\xFF\x84\x16`\x04\x82\x01R`$\x81\x01\x82\x90R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c\xAD\xC8\x04\xDA\x90`D\x01`@\x80Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a+\xF0W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a,\x14\x91\x90aM\x90V[`\0\x01Q\x85\x85\x81Q\x81\x10a,*Wa,*aL\x9BV[`\x01`\x01`\xA0\x1B\x03\x90\x92\x16` \x92\x83\x02\x91\x90\x91\x01\x90\x91\x01R\x83a,L\x81aM`V[\x94PP\x80\x80a,Z\x90aM`V[\x91PPa+{V[PP\x80\x80a,o\x90aM`V[\x91PPa*\\V[P\x90\x94\x93PPPPV[`\0a\x01,a,\x93b\x01\x89\xC0\x84aN\xA0V[a,\x9D\x91\x90aN\xA0V[\x92\x91PPV[a,\xABa8\x96V[a\x07\x03\x81a=\x18V[a,\xBCa8\x96V[`\x01`\x01`\xA0\x1B\x03\x81\x16a-!W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\xF1V[a\x07\x03\x81a;\xDCV[`\xFB`\0\x90T\x90a\x01\0\n\x90\x04`\x01`\x01`\xA0\x1B\x03\x16`\x01`\x01`\xA0\x1B\x03\x16c\xEA\xB6mz`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a-}W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a-\xA1\x91\x90aK\xCFV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a-\xD1W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aK\xECV[`\xFCT\x19\x81\x19`\xFCT\x19\x16\x14a.OW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7FPausable.unpause: invalid attemp`D\x82\x01R\x7Ft to pause functionality\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`\xFC\x81\x90U`@Q\x81\x81R3\x90\x7F5\x82\xD1\x82\x8E&\xBFV\xBD\x80\x15\x02\xBC\x02\x1A\xC0\xBC\x8A\xFBW\xC8&\xE4\x98kEY<\x8F\xAD8\x9C\x90` \x01a\x08:V[`\x97T`\x01`\x01`\xA0\x1B\x03\x163\x14a/\x1BW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`L`$\x82\x01R\x7FServiceManagerBase.onlyRewardsIn`D\x82\x01R\x7Fitiator: caller is not the rewar`d\x82\x01Rk29\x904\xB74\xBA4\xB0\xBA7\xB9`\xA1\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[`\0[\x81\x81\x10\x15a1\xEFW\x82\x82\x82\x81\x81\x10a/8Wa/8aL\x9BV[\x90P` \x02\x81\x01\x90a/J\x91\x90aP\xC6V[a/[\x90`@\x81\x01\x90` \x01aA\xAFV[`\x01`\x01`\xA0\x1B\x03\x16c#\xB8r\xDD30\x86\x86\x86\x81\x81\x10a/}Wa/}aL\x9BV[\x90P` \x02\x81\x01\x90a/\x8F\x91\x90aP\xC6V[`@\x80Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x87\x90\x1B\x16\x81R`\x01`\x01`\xA0\x1B\x03\x94\x85\x16`\x04\x82\x01R\x93\x90\x92\x16`$\x84\x01R\x015`D\x82\x01R`d\x01` `@Q\x80\x83\x03\x81`\0\x87Z\xF1\x15\x80\x15a/\xE6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a0\n\x91\x90aL6V[P`\0\x83\x83\x83\x81\x81\x10a0\x1FWa0\x1FaL\x9BV[\x90P` \x02\x81\x01\x90a01\x91\x90aP\xC6V[a0B\x90`@\x81\x01\x90` \x01aA\xAFV[`@Qcn\xB1v\x9F`\xE1\x1B\x81R0`\x04\x82\x01R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`$\x83\x01R\x91\x90\x91\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a0\xB0W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a0\xD4\x91\x90aL\xD3V[\x90P\x83\x83\x83\x81\x81\x10a0\xE8Wa0\xE8aL\x9BV[\x90P` \x02\x81\x01\x90a0\xFA\x91\x90aP\xC6V[a1\x0B\x90`@\x81\x01\x90` \x01aA\xAFV[`\x01`\x01`\xA0\x1B\x03\x16c\t^\xA7\xB3\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x83\x87\x87\x87\x81\x81\x10a1MWa1MaL\x9BV[\x90P` \x02\x81\x01\x90a1_\x91\x90aP\xC6V[`@\x015a1m\x91\x90aMHV[`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x85\x90\x1B\x16\x81R`\x01`\x01`\xA0\x1B\x03\x90\x92\x16`\x04\x83\x01R`$\x82\x01R`D\x01` `@Q\x80\x83\x03\x81`\0\x87Z\xF1\x15\x80\x15a1\xB8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a1\xDC\x91\x90aL6V[PP\x80a1\xE8\x90aM`V[\x90Pa/\x1EV[P`@Qc\xFC\xE3l}`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\xFC\xE3l}\x90a'o\x90\x85\x90\x85\x90`\x04\x01aQAV[`\x01`\x01`\xA0\x1B\x03\x81\x16a2\xCCW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`I`$\x82\x01R\x7FPausable._setPauserRegistry: new`D\x82\x01R\x7FPauserRegistry cannot be the zer`d\x82\x01Rho address`\xB8\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[`\xFBT`@\x80Q`\x01`\x01`\xA0\x1B\x03\x92\x83\x16\x81R\x91\x83\x16` \x83\x01R\x7Fn\x9F\xCDS\x98\x96\xFC\xA6\x0E\x8B\x0F\x01\xDDX\x023\xE4\x8Ak\x0F}\xF0\x13\xB8\x9B\xA7\xF5e\x86\x9A\xCD\xB6\x91\x01`@Q\x80\x91\x03\x90\xA1`\xFB\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01Ra3Qa@\xC0V[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x80\x82\x01\x84\x90R`\0\x90\x83``\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80\x15a3\x84Wa3\x86V[\xFE[P\x80a3\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\r`$\x82\x01Rl\x19X\xCB[][\x0BY\x98Z[\x19Y`\x9A\x1B`D\x82\x01R`d\x01a\x06\xF1V[PP\x92\x91PPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01Ra3\xE8a@\xDEV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x80\x84\x01\x91\x90\x91R\x90\x84\x01Q``\x83\x01R`\0\x90\x83`\x80\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80\x15a3\x84WP\x80a3\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\r`$\x82\x01Rl\x19X\xCBXY\x19\x0BY\x98Z[\x19Y`\x9A\x1B`D\x82\x01R`d\x01a\x06\xF1V[a4ha@\xFCV[P`@\x80Q`\x80\x81\x01\x82R\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81\x83\x01\x90\x81R\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED``\x83\x01R\x81R\x81Q\x80\x83\x01\x90\x92R\x7F']\xC4\xA2\x88\xD1\xAF\xB3\xCB\xB1\xAC\t\x18u$\xC7\xDB69]\xF7\xBE;\x99\xE6s\xB1:\x07Ze\xEC\x82R\x7F\x1D\x9B\xEF\xCD\x05\xA52>m\xA4\xD45\xF3\xB6\x17\xCD\xB3\xAF\x83(\\-\xF7\x11\xEF9\xC0\x15q\x82\x7F\x9D` \x83\x81\x01\x91\x90\x91R\x81\x01\x91\x90\x91R\x90V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R`\0\x80\x80a5P`\0\x80Q` aSZ\x839\x81Q\x91R\x86aL\xB1V[\x90P[a5\\\x81a>\tV[\x90\x93P\x91P`\0\x80Q` aSZ\x839\x81Q\x91R\x82\x83\t\x83\x14\x15a5\x96W`@\x80Q\x80\x82\x01\x90\x91R\x90\x81R` \x81\x01\x91\x90\x91R\x93\x92PPPV[`\0\x80Q` aSZ\x839\x81Q\x91R`\x01\x82\x08\x90Pa5SV[`@\x80Q\x80\x82\x01\x82R\x86\x81R` \x80\x82\x01\x86\x90R\x82Q\x80\x84\x01\x90\x93R\x86\x83R\x82\x01\x84\x90R`\0\x91\x82\x91\x90a5\xE2aA!V[`\0[`\x02\x81\x10\x15a7\xA7W`\0a5\xFB\x82`\x06aO\xE4V[\x90P\x84\x82`\x02\x81\x10a6\x0FWa6\x0FaL\x9BV[` \x02\x01QQ\x83a6!\x83`\0aMHV[`\x0C\x81\x10a61Wa61aL\x9BV[` \x02\x01R\x84\x82`\x02\x81\x10a6HWa6HaL\x9BV[` \x02\x01Q` \x01Q\x83\x82`\x01a6_\x91\x90aMHV[`\x0C\x81\x10a6oWa6oaL\x9BV[` \x02\x01R\x83\x82`\x02\x81\x10a6\x86Wa6\x86aL\x9BV[` \x02\x01QQQ\x83a6\x99\x83`\x02aMHV[`\x0C\x81\x10a6\xA9Wa6\xA9aL\x9BV[` \x02\x01R\x83\x82`\x02\x81\x10a6\xC0Wa6\xC0aL\x9BV[` \x02\x01QQ`\x01` \x02\x01Q\x83a6\xD9\x83`\x03aMHV[`\x0C\x81\x10a6\xE9Wa6\xE9aL\x9BV[` \x02\x01R\x83\x82`\x02\x81\x10a7\0Wa7\0aL\x9BV[` \x02\x01Q` \x01Q`\0`\x02\x81\x10a7\x1BWa7\x1BaL\x9BV[` \x02\x01Q\x83a7,\x83`\x04aMHV[`\x0C\x81\x10a7F\x17\x17\xE3\x91\x01`@Q\x80\x91\x03\x90\xA1`\x97\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[`\xC9\x80T`\xFF\x19\x16\x82\x15\x15\x90\x81\x17\x90\x91U`@Q\x90\x81R\x7F@\xE4\xED\x88\n)\xE0\xF6\xDD\xCE0tW\xFBu\xCD\xDFO\xEE\xF7\xD3\xEC\xB00\x1B\xFD\xF4\x97j\x0E-\xFC\x90` \x01[`@Q\x80\x91\x03\x90\xA1PV[`\0\x80a9\xAD\x84a>\x8BV[\x90P\x80\x83`\xFF\x16`\x01\x90\x1B\x11a:+W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`?`$\x82\x01R\x7FBitmapUtils.orderedBytesArrayToB`D\x82\x01R\x7Fitmap: bitmap exceeds max value\0`d\x82\x01R`\x84\x01a\x06\xF1V[\x93\x92PPPV[`\0\x80[\x82\x15a,\x9DWa:G`\x01\x84aM\xD1V[\x90\x92\x16\x91\x80a:U\x81aRNV[\x91PPa:6V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01Ra\x02\0\x82a\xFF\xFF\x16\x10a:\xB9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x10`$\x82\x01Roscalar-too-large`\x80\x1B`D\x82\x01R`d\x01a\x06\xF1V[\x81a\xFF\xFF\x16`\x01\x14\x15a:\xCDWP\x81a,\x9DV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01\x81\x90R\x84\x90`\x01\x90[\x81a\xFF\xFF\x16\x86a\xFF\xFF\x16\x10a;6W`\x01a\xFF\xFF\x87\x16`\xFF\x83\x16\x1C\x81\x16\x14\x15a;\x19Wa;\x16\x84\x84a3\xCCV[\x93P[a;#\x83\x84a3\xCCV[\x92Pb\x01\xFF\xFE`\x01\x92\x83\x1B\x16\x91\x01a:\xE9V[P\x91\x95\x94PPPPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81Q\x15\x80\x15a;fWP` \x82\x01Q\x15[\x15a;\x84WPP`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01`\0\x80Q` aSZ\x839\x81Q\x91R\x84` \x01Qa;\xB7\x91\x90aL\xB1V[a;\xCF\x90`\0\x80Q` aSZ\x839\x81Q\x91RaM\xD1V[\x90R\x92\x91PPV[\x91\x90PV[`e\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x90\x93U`@Q\x91\x16\x91\x90\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPV[`\xFBT`\x01`\x01`\xA0\x1B\x03\x16\x15\x80\x15aV[PPV[`\x01`\x01`\xA0\x1B\x03\x81\x16`\0\x81\x81R`\x02` \x90\x81R`@\x91\x82\x90 \x80T`\xFF\x80\x82\x16\x15`\xFF\x19\x90\x92\x16\x82\x17\x90\x92U\x83Q\x94\x85R\x16\x15\x15\x90\x83\x01R\x7F\\2e\xF5\xFBF.\xF4\x93\x0F\xE4{\xEA\xA1\x83d|\x97\xF1\x9B\xA5E\xB7a\xF4\x1B\xC8\xCDF!\xD4\x14\x91\x01a9\x96V[`\0a=\xB8\x82`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RP`@\x80Q\x80\x82\x01\x90\x91R\x81Q\x81R``\x90\x91\x01Qc\xFF\xFF\xFF\xFF\x16` \x82\x01R\x90V[`@\x80Q\x82Q` \x80\x83\x01\x91\x90\x91R\x90\x92\x01Qc\xFF\xFF\xFF\xFF\x16\x90\x82\x01R``\x01[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[`\0\x81`@Q` \x01a=\xD9\x91\x90aR\xDEV[`\0\x80\x80`\0\x80Q` aSZ\x839\x81Q\x91R`\x03`\0\x80Q` aSZ\x839\x81Q\x91R\x86`\0\x80Q` aSZ\x839\x81Q\x91R\x88\x89\t\t\x08\x90P`\0a>\x7F\x82\x7F\x0C\x19\x13\x9C\xB8Lh\nn\x14\x11m\xA0`V\x17e\xE0Z\xA4Z\x1Cr\xA3O\x08#\x05\xB6\x1F?R`\0\x80Q` aSZ\x839\x81Q\x91Ra@\x18V[\x91\x95\x91\x94P\x90\x92PPPV[`\0a\x01\0\x82Q\x11\x15a?\x14W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`D`$\x82\x01\x81\x90R\x7FBitmapUtils.orderedBytesArrayToB\x90\x82\x01R\x7Fitmap: orderedBytesArray is too `d\x82\x01Rclong`\xE0\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[\x81Qa?\"WP`\0\x91\x90PV[`\0\x80\x83`\0\x81Q\x81\x10a?8Wa?8aL\x9BV[\x01` \x01Q`\x01`\xF8\x91\x90\x91\x1C\x81\x90\x1B\x92P[\x84Q\x81\x10\x15a@\x0FW\x84\x81\x81Q\x81\x10a?fWa?faL\x9BV[\x01` \x01Q`\x01`\xF8\x91\x90\x91\x1C\x1B\x91P\x82\x82\x11a?\xFBW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`G`$\x82\x01R\x7FBitmapUtils.orderedBytesArrayToB`D\x82\x01R\x7Fitmap: orderedBytesArray is not `d\x82\x01Rf\x1B\xDC\x99\x19\\\x99Y`\xCA\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[\x91\x81\x17\x91a@\x08\x81aM`V[\x90Pa?KV[P\x90\x93\x92PPPV[`\0\x80a@#aA@V[a@+aA^V[` \x80\x82R\x81\x81\x01\x81\x90R`@\x82\x01\x81\x90R``\x82\x01\x88\x90R`\x80\x82\x01\x87\x90R`\xA0\x82\x01\x86\x90R\x82`\xC0\x83`\x05a\x07\xD0Z\x03\xFA\x92P\x82\x80\x15a3\x84WP\x82a@\xB5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1A`$\x82\x01R\x7FBN254.expMod: call failure\0\0\0\0\0\0`D\x82\x01R`d\x01a\x06\xF1V[PQ\x95\x94PPPPPV[`@Q\x80``\x01`@R\x80`\x03\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`\x80\x01`@R\x80`\x04\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`@\x01`@R\x80aA\x0FaA|V[\x81R` \x01aA\x1CaA|V[\x90R\x90V[`@Q\x80a\x01\x80\x01`@R\x80`\x0C\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80` \x01`@R\x80`\x01\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`\xC0\x01`@R\x80`\x06\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`@\x01`@R\x80`\x02\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x07\x03W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aA\xC1W`\0\x80\xFD[\x815a:+\x81aA\x9AV[`\0` \x82\x84\x03\x12\x15aA\xDEW`\0\x80\xFD[P5\x91\x90PV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aB\x1DWaB\x1DaA\xE5V[`@R\x90V[`@Qa\x01\0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aB\x1DWaB\x1DaA\xE5V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aBnWaBnaA\xE5V[`@R\x91\x90PV[`\0`@\x82\x84\x03\x12\x15aB\x88W`\0\x80\xFD[aB\x90aA\xFBV[\x90P\x815\x81R` \x82\x015` \x82\x01R\x92\x91PPV[`\0\x82`\x1F\x83\x01\x12aB\xB7W`\0\x80\xFD[aB\xBFaA\xFBV[\x80`@\x84\x01\x85\x81\x11\x15aB\xD1W`\0\x80\xFD[\x84[\x81\x81\x10\x15aB\xEBW\x805\x84R` \x93\x84\x01\x93\x01aB\xD3V[P\x90\x95\x94PPPPPV[`\0`\x80\x82\x84\x03\x12\x15aC\x08W`\0\x80\xFD[aC\x10aA\xFBV[\x90PaC\x1C\x83\x83aB\xA6V[\x81RaC+\x83`@\x84\x01aB\xA6V[` \x82\x01R\x92\x91PPV[`\0\x80`\0\x80a\x01 \x85\x87\x03\x12\x15aCMW`\0\x80\xFD[\x845\x93PaC^\x86` \x87\x01aBvV[\x92PaCm\x86``\x87\x01aB\xF6V[\x91PaC|\x86`\xE0\x87\x01aBvV[\x90P\x92\x95\x91\x94P\x92PV[\x805a;\xD7\x81aA\x9AV[` \x80\x82R\x82Q\x82\x82\x01\x81\x90R`\0\x91\x90\x84\x82\x01\x90`@\x85\x01\x90\x84[\x81\x81\x10\x15aC\xD3W\x83Q`\x01`\x01`\xA0\x1B\x03\x16\x83R\x92\x84\x01\x92\x91\x84\x01\x91`\x01\x01aC\xAEV[P\x90\x96\x95PPPPPPV[\x80\x15\x15\x81\x14a\x07\x03W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aC\xFFW`\0\x80\xFD[\x815a:+\x81aC\xDFV[`\xFF\x81\x16\x81\x14a\x07\x03W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aD+W`\0\x80\xFD[\x815a:+\x81aD\nV[\x805c\xFF\xFF\xFF\xFF\x81\x16\x81\x14a;\xD7W`\0\x80\xFD[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15aDcWaDcaA\xE5V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12aD~W`\0\x80\xFD[\x815` aD\x93aD\x8E\x83aDJV[aBFV[\x82\x81R`\x05\x92\x90\x92\x1B\x84\x01\x81\x01\x91\x81\x81\x01\x90\x86\x84\x11\x15aD\xB2W`\0\x80\xFD[\x82\x86\x01[\x84\x81\x10\x15aD\xD4WaD\xC7\x81aD6V[\x83R\x91\x83\x01\x91\x83\x01aD\xB6V[P\x96\x95PPPPPPV[`\0\x82`\x1F\x83\x01\x12aD\xF0W`\0\x80\xFD[\x815` aE\0aD\x8E\x83aDJV[\x82\x81R`\x06\x92\x90\x92\x1B\x84\x01\x81\x01\x91\x81\x81\x01\x90\x86\x84\x11\x15aE\x1FW`\0\x80\xFD[\x82\x86\x01[\x84\x81\x10\x15aD\xD4WaE5\x88\x82aBvV[\x83R\x91\x83\x01\x91`@\x01aE#V[`\0\x82`\x1F\x83\x01\x12aETW`\0\x80\xFD[\x815` aEdaD\x8E\x83aDJV[\x82\x81R`\x05\x92\x90\x92\x1B\x84\x01\x81\x01\x91\x81\x81\x01\x90\x86\x84\x11\x15aE\x83W`\0\x80\xFD[\x82\x86\x01[\x84\x81\x10\x15aD\xD4W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15aE\xA6W`\0\x80\x81\xFD[aE\xB4\x89\x86\x83\x8B\x01\x01aDmV[\x84RP\x91\x83\x01\x91\x83\x01aE\x87V[`\0a\x01\x80\x82\x84\x03\x12\x15aE\xD5W`\0\x80\xFD[aE\xDDaB#V[\x90P\x815`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aE\xF6W`\0\x80\xFD[aF\x02\x85\x83\x86\x01aDmV[\x83R` \x84\x015\x91P\x80\x82\x11\x15aF\x18W`\0\x80\xFD[aF$\x85\x83\x86\x01aD\xDFV[` \x84\x01R`@\x84\x015\x91P\x80\x82\x11\x15aF=W`\0\x80\xFD[aFI\x85\x83\x86\x01aD\xDFV[`@\x84\x01RaF[\x85``\x86\x01aB\xF6V[``\x84\x01RaFm\x85`\xE0\x86\x01aBvV[`\x80\x84\x01Ra\x01 \x84\x015\x91P\x80\x82\x11\x15aF\x87W`\0\x80\xFD[aF\x93\x85\x83\x86\x01aDmV[`\xA0\x84\x01Ra\x01@\x84\x015\x91P\x80\x82\x11\x15aF\xADW`\0\x80\xFD[aF\xB9\x85\x83\x86\x01aDmV[`\xC0\x84\x01Ra\x01`\x84\x015\x91P\x80\x82\x11\x15aF\xD3W`\0\x80\xFD[PaF\xE0\x84\x82\x85\x01aECV[`\xE0\x83\x01RP\x92\x91PPV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15aG\x04W`\0\x80\xFD[\x855\x94P` \x86\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aG\"W`\0\x80\xFD[\x81\x88\x01\x91P\x88`\x1F\x83\x01\x12aG6W`\0\x80\xFD[\x815\x81\x81\x11\x15aGEW`\0\x80\xFD[\x89` \x82\x85\x01\x01\x11\x15aGWW`\0\x80\xFD[` \x83\x01\x96P\x94PaGk`@\x89\x01aD6V[\x93P``\x88\x015\x91P\x80\x82\x11\x15aG\x81W`\0\x80\xFD[PaG\x8E\x88\x82\x89\x01aE\xC2V[\x91PP\x92\x95P\x92\x95\x90\x93PV[`\0\x81Q\x80\x84R` \x80\x85\x01\x94P\x80\x84\x01`\0[\x83\x81\x10\x15aG\xD4W\x81Q`\x01`\x01``\x1B\x03\x16\x87R\x95\x82\x01\x95\x90\x82\x01\x90`\x01\x01aG\xAFV[P\x94\x95\x94PPPPPV[`@\x81R`\0\x83Q`@\x80\x84\x01RaG\xFA`\x80\x84\x01\x82aG\x9BV[\x90P` \x85\x01Q`?\x19\x84\x83\x03\x01``\x85\x01RaH\x17\x82\x82aG\x9BV[\x92PPP\x82` \x83\x01R\x93\x92PPPV[`\0\x80`\0\x80`\0`\xA0\x86\x88\x03\x12\x15aH@W`\0\x80\xFD[\x855aHK\x81aA\x9AV[\x94P` \x86\x81\x015\x94P`@\x87\x015aHc\x81aA\x9AV[\x93P``\x87\x015`\x01`\x01`@\x1B\x03\x81\x11\x15aH~W`\0\x80\xFD[\x87\x01`\x1F\x81\x01\x89\x13aH\x8FW`\0\x80\xFD[\x805aH\x9DaD\x8E\x82aDJV[\x81\x81R`\x05\x91\x90\x91\x1B\x82\x01\x83\x01\x90\x83\x81\x01\x90\x8B\x83\x11\x15aH\xBCW`\0\x80\xFD[\x92\x84\x01\x92[\x82\x84\x10\x15aH\xE3W\x835aH\xD4\x81aA\x9AV[\x82R\x92\x84\x01\x92\x90\x84\x01\x90aH\xC1V[\x80\x96PPPPPPaH\xF7`\x80\x87\x01aC\x87V[\x90P\x92\x95P\x92\x95\x90\x93PV[`\0\x80`@\x83\x85\x03\x12\x15aI\x16W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aI-W`\0\x80\xFD[\x90\x84\x01\x90`\x80\x82\x87\x03\x12\x15aIAW`\0\x80\xFD[\x90\x92P` \x84\x015\x90\x80\x82\x11\x15aIWW`\0\x80\xFD[PaId\x85\x82\x86\x01aE\xC2V[\x91PP\x92P\x92\x90PV[`\0\x81Q\x80\x84R`\0[\x81\x81\x10\x15aI\x94W` \x81\x85\x01\x81\x01Q\x86\x83\x01\x82\x01R\x01aIxV[\x81\x81\x11\x15aI\xA6W`\0` \x83\x87\x01\x01R[P`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[` \x81R`\0a:+` \x83\x01\x84aInV[`\0`\x01`\x01`@\x1B\x03\x83\x11\x15aI\xE7WaI\xE7aA\xE5V[aI\xFA`\x1F\x84\x01`\x1F\x19\x16` \x01aBFV[\x90P\x82\x81R\x83\x83\x83\x01\x11\x15aJ\x0EW`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12aJ6W`\0\x80\xFD[a:+\x83\x835` \x85\x01aI\xCEV[`\0\x80`@\x83\x85\x03\x12\x15aJXW`\0\x80\xFD[\x825aJc\x81aA\x9AV[\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aJ\x7FW`\0\x80\xFD[\x90\x84\x01\x90``\x82\x87\x03\x12\x15aJ\x93W`\0\x80\xFD[`@Q``\x81\x01\x81\x81\x10\x83\x82\x11\x17\x15aJ\xAEWaJ\xAEaA\xE5V[`@R\x825\x82\x81\x11\x15aJ\xC0W`\0\x80\xFD[aJ\xCC\x88\x82\x86\x01aJ%V[\x82RP` \x83\x015` \x82\x01R`@\x83\x015`@\x82\x01R\x80\x93PPPP\x92P\x92\x90PV[`\0` \x82\x84\x03\x12\x15aK\x02W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15aK\x18W`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13aK)W`\0\x80\xFD[aK8\x84\x825` \x84\x01aI\xCEV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15aKRW`\0\x80\xFD[a:+\x82aD6V[`\0\x80` \x83\x85\x03\x12\x15aKnW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aK\x85W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12aK\x99W`\0\x80\xFD[\x815\x81\x81\x11\x15aK\xA8W`\0\x80\xFD[\x86` \x82`\x05\x1B\x85\x01\x01\x11\x15aK\xBDW`\0\x80\xFD[` \x92\x90\x92\x01\x96\x91\x95P\x90\x93PPPPV[`\0` \x82\x84\x03\x12\x15aK\xE1W`\0\x80\xFD[\x81Qa:+\x81aA\x9AV[` \x80\x82R`*\x90\x82\x01R\x7Fmsg.sender is not permissioned a`@\x82\x01Ri9\x90:\xB780\xBA\xB9\xB2\xB9`\xB1\x1B``\x82\x01R`\x80\x01\x90V[`\0` \x82\x84\x03\x12\x15aLHW`\0\x80\xFD[\x81Qa:+\x81aC\xDFV[` \x80\x82R`(\x90\x82\x01R\x7Fmsg.sender is not permissioned a`@\x82\x01Rg9\x9080\xBA\xB9\xB2\xB9`\xC1\x1B``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x82aL\xCEWcNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[P\x06\x90V[`\0` \x82\x84\x03\x12\x15aL\xE5W`\0\x80\xFD[PQ\x91\x90PV[`\0` \x82\x84\x03\x12\x15aL\xFEW`\0\x80\xFD[\x81Q`\x01`\x01`\xC0\x1B\x03\x81\x16\x81\x14a:+W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aM'W`\0\x80\xFD[\x81Qa:+\x81aD\nV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0\x82\x19\x82\x11\x15aM[WaM[aM2V[P\x01\x90V[`\0`\0\x19\x82\x14\x15aMtWaMtaM2V[P`\x01\x01\x90V[`\x01`\x01``\x1B\x03\x81\x16\x81\x14a\x07\x03W`\0\x80\xFD[`\0`@\x82\x84\x03\x12\x15aM\xA2W`\0\x80\xFD[aM\xAAaA\xFBV[\x82QaM\xB5\x81aA\x9AV[\x81R` \x83\x01QaM\xC5\x81aM{V[` \x82\x01R\x93\x92PPPV[`\0\x82\x82\x10\x15aM\xE3WaM\xE3aM2V[P\x03\x90V[`\0` \x82\x84\x03\x12\x15aM\xFAW`\0\x80\xFD[\x81Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x19\x81\x16\x81\x14a:+W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aN%W`\0\x80\xFD[\x81Qa:+\x81aM{V[`\0`\x01`\x01``\x1B\x03\x83\x81\x16\x90\x83\x16\x81\x81\x10\x15aNPWaNPaM2V[\x03\x93\x92PPPV[c\xFF\xFF\xFF\xFF`\xE0\x1B\x83`\xE0\x1B\x16\x81R`\0`\x04\x82\x01\x83Q` \x80\x86\x01`\0[\x83\x81\x10\x15aN\x93W\x81Q\x85R\x93\x82\x01\x93\x90\x82\x01\x90`\x01\x01aNwV[P\x92\x97\x96PPPPPPPV[`\0c\xFF\xFF\xFF\xFF\x80\x83\x16\x81\x85\x16\x80\x83\x03\x82\x11\x15aN\xBFWaN\xBFaM2V[\x01\x94\x93PPPPV[`\0\x80\x835`\x1E\x19\x846\x03\x01\x81\x12aN\xDFW`\0\x80\xFD[\x83\x01\x805\x91P`\x01`\x01`@\x1B\x03\x82\x11\x15aN\xF9W`\0\x80\xFD[` \x01\x91P6\x81\x90\x03\x82\x13\x15aO\x0EW`\0\x80\xFD[\x92P\x92\x90PV[`\0`\x80\x826\x03\x12\x15aO'W`\0\x80\xFD[`@Q`\x80\x81\x01`\x01`\x01`@\x1B\x03\x82\x82\x10\x81\x83\x11\x17\x15aOJWaOJaA\xE5V[\x81`@R\x845\x83R` \x85\x015\x91P\x80\x82\x11\x15aOfW`\0\x80\xFD[aOr6\x83\x87\x01aJ%V[` \x84\x01R`@\x85\x015\x91P\x80\x82\x11\x15aO\x8BW`\0\x80\xFD[PaO\x986\x82\x86\x01aJ%V[`@\x83\x01RPaO\xAA``\x84\x01aD6V[``\x82\x01R\x92\x91PPV[`\0`\x01`\x01``\x1B\x03\x80\x83\x16\x81\x85\x16\x81\x83\x04\x81\x11\x82\x15\x15\x16\x15aO\xDBWaO\xDBaM2V[\x02\x94\x93PPPPV[`\0\x81`\0\x19\x04\x83\x11\x82\x15\x15\x16\x15aO\xFEWaO\xFEaM2V[P\x02\x90V[` \x80\x82R`R\x90\x82\x01R\x7FServiceManagerBase.onlyRegistryC`@\x82\x01R\x7Foordinator: caller is not the re``\x82\x01Rq3\xB4\xB9\xBA9<\x901\xB7\xB7\xB924\xB70\xBA7\xB9`q\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x80`\xA0\x1B\x03\x83\x16\x81R`@` \x82\x01R`\0\x82Q```@\x84\x01RaP\xA5`\xA0\x84\x01\x82aInV[\x90P` \x84\x01Q``\x84\x01R`@\x84\x01Q`\x80\x84\x01R\x80\x91PP\x93\x92PPPV[`\0\x825`\x9E\x19\x836\x03\x01\x81\x12aP\xDCW`\0\x80\xFD[\x91\x90\x91\x01\x92\x91PPV[\x81\x83R`\0` \x80\x85\x01\x94P\x82`\0[\x85\x81\x10\x15aG\xD4W\x815aQ\t\x81aA\x9AV[`\x01`\x01`\xA0\x1B\x03\x16\x87R\x81\x83\x015aQ!\x81aM{V[`\x01`\x01``\x1B\x03\x16\x87\x84\x01R`@\x96\x87\x01\x96\x91\x90\x91\x01\x90`\x01\x01aP\xF6V[` \x80\x82R\x81\x81\x01\x83\x90R`\0\x90`@\x80\x84\x01`\x05\x86\x90\x1B\x85\x01\x82\x01\x87\x85[\x88\x81\x10\x15aR@W\x87\x83\x03`?\x19\x01\x84R\x8156\x8B\x90\x03`\x9E\x19\x01\x81\x12aQ\x86W`\0\x80\xFD[\x8A\x01`\xA0\x8156\x83\x90\x03`\x1E\x19\x01\x81\x12aQ\x9FW`\0\x80\xFD[\x82\x01\x805`\x01`\x01`@\x1B\x03\x81\x11\x15aQ\xB7W`\0\x80\xFD[\x80`\x06\x1B6\x03\x84\x13\x15aQ\xC9W`\0\x80\xFD[\x82\x87RaQ\xDB\x83\x88\x01\x82\x8C\x85\x01aP\xE6V[\x92PPPaQ\xEA\x88\x83\x01aC\x87V[`\x01`\x01`\xA0\x1B\x03\x16\x88\x86\x01R\x81\x87\x015\x87\x86\x01R``aR\x0C\x81\x84\x01aD6V[c\xFF\xFF\xFF\xFF\x16\x90\x86\x01R`\x80aR#\x83\x82\x01aD6V[c\xFF\xFF\xFF\xFF\x16\x95\x01\x94\x90\x94RP\x92\x85\x01\x92\x90\x85\x01\x90`\x01\x01aQ`V[P\x90\x98\x97PPPPPPPPV[`\0a\xFF\xFF\x80\x83\x16\x81\x81\x14\x15aRfWaRfaM2V[`\x01\x01\x93\x92PPPV[`\0\x80\x835`\x1E\x19\x846\x03\x01\x81\x12aR\x87W`\0\x80\xFD[\x83\x01` \x81\x01\x92P5\x90P`\x01`\x01`@\x1B\x03\x81\x11\x15aR\xA6W`\0\x80\xFD[\x806\x03\x83\x13\x15aO\x0EW`\0\x80\xFD[\x81\x83R\x81\x81` \x85\x017P`\0\x82\x82\x01` \x90\x81\x01\x91\x90\x91R`\x1F\x90\x91\x01`\x1F\x19\x16\x90\x91\x01\x01\x90V[` \x81R\x815` \x82\x01R`\0aR\xF8` \x84\x01\x84aRpV[`\x80`@\x85\x01RaS\r`\xA0\x85\x01\x82\x84aR\xB5V[\x91PPaS\x1D`@\x85\x01\x85aRpV[\x84\x83\x03`\x1F\x19\x01``\x86\x01RaS4\x83\x82\x84aR\xB5V[\x92PPPc\xFF\xFF\xFF\xFFaSI``\x86\x01aD6V[\x16`\x80\x84\x01R\x80\x91PP\x92\x91PPV\xFE0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDGEigenDAServiceManager.confirmBatBLSSignatureChecker.checkSignatu\xA2dipfsX\"\x12 \x08'RI\xEC\">8\x0E\xD1?K\x05\x96\xED\xED\x1A\xE59X\xC6\xCFh\x852k\xD2[I:\x84ZdsolcC\0\x08\x0C\x003", + ); + /// The runtime bytecode of the contract, as deployed on the network. + /// + /// ```text + ///0x608060405234801561001057600080fd5b50600436106102535760003560e01c80637794965a11610146578063df5cf723116100c3578063ef02445811610087578063ef024458146105e3578063f1220983146105eb578063f2fde38b146105fe578063fabc1cbc14610611578063fc299dee14610624578063fce36c7d1461063757600080fd5b8063df5cf72314610564578063e15234ff1461058b578063e481af9d146105a8578063eaefd27d146105b0578063eccbbfc9146105c357600080fd5b8063a364f4da1161010a578063a364f4da146104ee578063a5b7890a14610501578063a98fb35514610524578063b98d090814610537578063bafa91071461054457600080fd5b80637794965a146104775780638687feae1461048a578063886f1195146104b75780638da5cb5b146104ca5780639926ee7d146104db57600080fd5b80635df45946116101d45780636d14a987116101985780636d14a987146104065780636efb46361461042d578063715018a61461044e57806372d18e8d14610456578063775bbcb51461046457600080fd5b80635df45946146103675780635e033476146103a65780635e8b3f2d146103b057806368304835146103b95780636b3aa72e146103e057600080fd5b8063416c7e5e1161021b578063416c7e5e146102e25780634972134a146102f5578063595c6a671461031a5780635ac86ab7146103225780635c975abb1461035557600080fd5b806310d67a2f14610258578063136439dd1461026d578063171f1d5b1461028057806333cfb7b7146102af5780633bc28c8c146102cf575b600080fd5b61026b6102663660046141af565b61064a565b005b61026b61027b3660046141cc565b610706565b61029361028e366004614336565b610845565b6040805192151583529015156020830152015b60405180910390f35b6102c26102bd3660046141af565b6109cf565b6040516102a69190614392565b61026b6102dd3660046141af565b610e9e565b61026b6102f03660046143ed565b610eaf565b6000546103059063ffffffff1681565b60405163ffffffff90911681526020016102a6565b61026b610fe6565b610345610330366004614419565b60fc54600160ff9092169190911b9081161490565b60405190151581526020016102a6565b60fc545b6040519081526020016102a6565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102a6565b610305620189c081565b61030561012c81565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000061038e565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b61044061043b3660046146ec565b6110ad565b6040516102a69291906147df565b61026b611fc4565b60005463ffffffff16610305565b61026b610472366004614828565b611fd8565b61026b610485366004614903565b612141565b6104aa604051806040016040528060018152602001602160f81b81525081565b6040516102a691906149bb565b60fb5461038e906001600160a01b031681565b6065546001600160a01b031661038e565b61026b6104e9366004614a45565b6126d9565b61026b6104fc3660046141af565b61279d565b61034561050f3660046141af565b60026020526000908152604090205460ff1681565b61026b610532366004614af0565b612864565b60c9546103459060ff1681565b6104aa604051806040016040528060018152602001603760f81b81525081565b61038e7f000000000000000000000000000000000000000000000000000000000000000081565b6104aa604051806040016040528060018152602001600081525081565b6102c26128b8565b6103056105be366004614b40565b612c81565b6103596105d1366004614b40565b60016020526000908152604090205481565b610359606481565b61026b6105f93660046141af565b612ca3565b61026b61060c3660046141af565b612cb4565b61026b61061f3660046141cc565b612d2a565b60975461038e906001600160a01b031681565b61026b610645366004614b5b565b612e86565b60fb60009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561069d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c19190614bcf565b6001600160a01b0316336001600160a01b0316146106fa5760405162461bcd60e51b81526004016106f190614bec565b60405180910390fd5b6107038161323e565b50565b60fb5460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa15801561074e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107729190614c36565b61078e5760405162461bcd60e51b81526004016106f190614c53565b60fc54818116146108075760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e70617573653a20696e76616c696420617474656d70742060448201527f746f20756e70617573652066756e6374696f6e616c697479000000000000000060648201526084016106f1565b60fc81905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d906020015b60405180910390a250565b60008060007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018787600001518860200151886000015160006002811061088d5761088d614c9b565b60200201518951600160200201518a602001516000600281106108b2576108b2614c9b565b60200201518b602001516001600281106108ce576108ce614c9b565b602090810291909101518c518d83015160405161092b9a99989796959401988952602089019790975260408801959095526060870193909352608086019190915260a085015260c084015260e08301526101008201526101200190565b6040516020818303038152906040528051906020012060001c61094e9190614cb1565b90506109c16109676109608884613335565b86906133cc565b61096f613460565b6109b76109a8856109a2604080518082018252600080825260209182015281518083019092526001825260029082015290565b90613335565b6109b18c613520565b906133cc565b886201d4c06135b0565b909890975095505050505050565b6040516309aa152760e11b81526001600160a01b0382811660048301526060916000917f000000000000000000000000000000000000000000000000000000000000000016906313542a4e90602401602060405180830381865afa158015610a3b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5f9190614cd3565b60405163871ef04960e01b8152600481018290529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063871ef04990602401602060405180830381865afa158015610aca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aee9190614cec565b90506001600160c01b0381161580610b8857507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639aa1653d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610b5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b839190614d15565b60ff16155b15610ba457505060408051600081526020810190915292915050565b6000610bb8826001600160c01b03166137d4565b90506000805b8251811015610c8e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633ca5a5f5848381518110610c0857610c08614c9b565b01602001516040516001600160e01b031960e084901b16815260f89190911c6004820152602401602060405180830381865afa158015610c4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c709190614cd3565b610c7a9083614d48565b915080610c8681614d60565b915050610bbe565b506000816001600160401b03811115610ca957610ca96141e5565b604051908082528060200260200182016040528015610cd2578160200160208202803683370190505b5090506000805b8451811015610e91576000858281518110610cf657610cf6614c9b565b0160200151604051633ca5a5f560e01b815260f89190911c6004820181905291506000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690633ca5a5f590602401602060405180830381865afa158015610d6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8f9190614cd3565b905060005b81811015610e7b576040516356e4026d60e11b815260ff84166004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063adc804da906044016040805180830381865afa158015610e09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2d9190614d90565b60000151868681518110610e4357610e43614c9b565b6001600160a01b039092166020928302919091019091015284610e6581614d60565b9550508080610e7390614d60565b915050610d94565b5050508080610e8990614d60565b915050610cd9565b5090979650505050505050565b610ea6613896565b610703816138f0565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f319190614bcf565b6001600160a01b0316336001600160a01b031614610fdd5760405162461bcd60e51b815260206004820152605c60248201527f424c535369676e6174757265436865636b65722e6f6e6c79436f6f7264696e6160448201527f746f724f776e65723a2063616c6c6572206973206e6f7420746865206f776e6560648201527f72206f6620746865207265676973747279436f6f7264696e61746f7200000000608482015260a4016106f1565b61070381613959565b60fb5460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110529190614c36565b61106e5760405162461bcd60e51b81526004016106f190614c53565b60001960fc81905560405190815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2565b60408051808201909152606080825260208201526000846111245760405162461bcd60e51b8152602060048201526037602482015260008051602061539a83398151915260448201527f7265733a20656d7074792071756f72756d20696e70757400000000000000000060648201526084016106f1565b6040830151518514801561113c575060a08301515185145b801561114c575060c08301515185145b801561115c575060e08301515185145b6111c65760405162461bcd60e51b8152602060048201526041602482015260008051602061539a83398151915260448201527f7265733a20696e7075742071756f72756d206c656e677468206d69736d6174636064820152600d60fb1b608482015260a4016106f1565b8251516020840151511461123e5760405162461bcd60e51b81526020600482015260446024820181905260008051602061539a833981519152908201527f7265733a20696e707574206e6f6e7369676e6572206c656e677468206d69736d6064820152630c2e8c6d60e31b608482015260a4016106f1565b4363ffffffff168463ffffffff16106112ad5760405162461bcd60e51b815260206004820152603c602482015260008051602061539a83398151915260448201527f7265733a20696e76616c6964207265666572656e636520626c6f636b0000000060648201526084016106f1565b6040805180820182526000808252602080830191909152825180840190935260608084529083015290866001600160401b038111156112ee576112ee6141e5565b604051908082528060200260200182016040528015611317578160200160208202803683370190505b506020820152866001600160401b03811115611335576113356141e5565b60405190808252806020026020018201604052801561135e578160200160208202803683370190505b50815260408051808201909152606080825260208201528560200151516001600160401b03811115611392576113926141e5565b6040519080825280602002602001820160405280156113bb578160200160208202803683370190505b5081526020860151516001600160401b038111156113db576113db6141e5565b604051908082528060200260200182016040528015611404578160200160208202803683370190505b50816020018190525060006114d68a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505060408051639aa1653d60e01b815290516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169350639aa1653d925060048083019260209291908290030181865afa1580156114ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114d19190614d15565b6139a1565b905060005b876020015151811015611771576115208860200151828151811061150157611501614c9b565b6020026020010151805160009081526020918201519091526040902090565b8360200151828151811061153657611536614c9b565b602090810291909101015280156115f6576020830151611557600183614dd1565b8151811061156757611567614c9b565b602002602001015160001c8360200151828151811061158857611588614c9b565b602002602001015160001c116115f6576040805162461bcd60e51b815260206004820152602481019190915260008051602061539a83398151915260448201527f7265733a206e6f6e5369676e65725075626b657973206e6f7420736f7274656460648201526084016106f1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166304ec63518460200151838151811061163b5761163b614c9b565b60200260200101518b8b60000151858151811061165a5761165a614c9b565b60200260200101516040518463ffffffff1660e01b81526004016116979392919092835263ffffffff918216602084015216604082015260600190565b602060405180830381865afa1580156116b4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d89190614cec565b6001600160c01b0316836000015182815181106116f7576116f7614c9b565b60200260200101818152505061175d610960611731848660000151858151811061172357611723614c9b565b602002602001015116613a32565b8a60200151848151811061174757611747614c9b565b6020026020010151613a5d90919063ffffffff16565b94508061176981614d60565b9150506114db565b505061177c83613b41565b60c95490935060ff16600081611793576000611815565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c448feb86040518163ffffffff1660e01b8152600401602060405180830381865afa1580156117f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118159190614cd3565b905060005b8a811015611e93578215611975578963ffffffff16827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663249a0c428f8f8681811061187157611871614c9b565b60405160e085901b6001600160e01b031916815292013560f81c600483015250602401602060405180830381865afa1580156118b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d59190614cd3565b6118df9190614d48565b116119755760405162461bcd60e51b8152602060048201526066602482015260008051602061539a83398151915260448201527f7265733a205374616b6552656769737472792075706461746573206d7573742060648201527f62652077697468696e207769746864726177616c44656c6179426c6f636b732060848201526577696e646f7760d01b60a482015260c4016106f1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166368bccaac8d8d848181106119b6576119b6614c9b565b9050013560f81c60f81b60f81c8c8c60a0015185815181106119da576119da614c9b565b60209081029190910101516040516001600160e01b031960e086901b16815260ff909316600484015263ffffffff9182166024840152166044820152606401602060405180830381865afa158015611a36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5a9190614de8565b6001600160401b031916611a7d8a60400151838151811061150157611501614c9b565b67ffffffffffffffff191614611b195760405162461bcd60e51b8152602060048201526061602482015260008051602061539a83398151915260448201527f7265733a2071756f72756d41706b206861736820696e2073746f72616765206460648201527f6f6573206e6f74206d617463682070726f76696465642071756f72756d2061706084820152606b60f81b60a482015260c4016106f1565b611b4989604001518281518110611b3257611b32614c9b565b6020026020010151876133cc90919063ffffffff16565b95507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c8294c568d8d84818110611b8c57611b8c614c9b565b9050013560f81c60f81b60f81c8c8c60c001518581518110611bb057611bb0614c9b565b60209081029190910101516040516001600160e01b031960e086901b16815260ff909316600484015263ffffffff9182166024840152166044820152606401602060405180830381865afa158015611c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c309190614e13565b85602001518281518110611c4657611c46614c9b565b6001600160601b03909216602092830291909101820152850151805182908110611c7257611c72614c9b565b602002602001015185600001518281518110611c9057611c90614c9b565b60200260200101906001600160601b031690816001600160601b0316815250506000805b8a6020015151811015611e7e57611d0886600001518281518110611cda57611cda614c9b565b60200260200101518f8f86818110611cf457611cf4614c9b565b600192013560f81c9290921c811614919050565b15611e6c577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663f2be94ae8f8f86818110611d4e57611d4e614c9b565b9050013560f81c60f81b60f81c8e89602001518581518110611d7257611d72614c9b565b60200260200101518f60e001518881518110611d9057611d90614c9b565b60200260200101518781518110611da957611da9614c9b565b60209081029190910101516040516001600160e01b031960e087901b16815260ff909416600485015263ffffffff92831660248501526044840191909152166064820152608401602060405180830381865afa158015611e0d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e319190614e13565b8751805185908110611e4557611e45614c9b565b60200260200101818151611e599190614e30565b6001600160601b03169052506001909101905b80611e7681614d60565b915050611cb4565b50508080611e8b90614d60565b91505061181a565b505050600080611ead8c868a606001518b60800151610845565b9150915081611f1e5760405162461bcd60e51b8152602060048201526043602482015260008051602061539a83398151915260448201527f7265733a2070616972696e6720707265636f6d70696c652063616c6c206661696064820152621b195960ea1b608482015260a4016106f1565b80611f7f5760405162461bcd60e51b8152602060048201526039602482015260008051602061539a83398151915260448201527f7265733a207369676e617475726520697320696e76616c69640000000000000060648201526084016106f1565b50506000878260200151604051602001611f9a929190614e58565b60408051808303601f190181529190528051602090910120929b929a509198505050505050505050565b611fcc613896565b611fd66000613bdc565b565b603254610100900460ff1615808015611ff85750603254600160ff909116105b806120125750303b158015612012575060325460ff166001145b6120755760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016106f1565b6032805460ff191660011790558015612098576032805461ff0019166101001790555b6120a28686613c2e565b6120ab84613bdc565b6120b4826138f0565b60005b83518110156120f2576120e28482815181106120d5576120d5614c9b565b6020026020010151613d18565b6120eb81614d60565b90506120b7565b508015612139576032805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b60fc546000906001908116141561219a5760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e646578206973207061757365640000000000000060448201526064016106f1565b3360009081526002602052604090205460ff1661220e5760405162461bcd60e51b815260206004820152602c60248201527f6f6e6c794261746368436f6e6669726d65723a206e6f742066726f6d2062617460448201526b31b41031b7b73334b936b2b960a11b60648201526084016106f1565b32331461228b5760405162461bcd60e51b8152602060048201526051602482015260008051602061537a83398151915260448201527f63683a2068656164657220616e64206e6f6e7369676e65722064617461206d75606482015270737420626520696e2063616c6c6461746160781b608482015260a4016106f1565b4361229c6080850160608601614b40565b63ffffffff161061231b5760405162461bcd60e51b815260206004820152604f602482015260008051602061537a83398151915260448201527f63683a20737065636966696564207265666572656e6365426c6f636b4e756d6260648201526e657220697320696e2066757475726560881b608482015260a4016106f1565b63ffffffff431661012c6123356080860160608701614b40565b61233f9190614ea0565b63ffffffff1610156123c55760405162461bcd60e51b8152602060048201526055602482015260008051602061537a83398151915260448201527f63683a20737065636966696564207265666572656e6365426c6f636b4e756d62606482015274195c881a5cc81d1bdbc819985c881a5b881c185cdd605a1b608482015260a4016106f1565b6123d26040840184614ec8565b90506123e16020850185614ec8565b9050146124795760405162461bcd60e51b8152602060048201526066602482015260008051602061537a83398151915260448201527f63683a2071756f72756d4e756d6265727320616e64207369676e65645374616b60648201527f65466f7251756f72756d73206d757374206265206f66207468652073616d65206084820152650d8cadccee8d60d31b60a482015260c4016106f1565b600061248c61248785614f15565b613d7b565b90506000806124b8836124a26020890189614ec8565b6124b260808b0160608c01614b40565b896110ad565b9150915060005b6124cc6040880188614ec8565b905081101561260e576124e26040880188614ec8565b828181106124f2576124f2614c9b565b9050013560f81c60f81b60f81c60ff168360200151828151811061251857612518614c9b565b602002602001015161252a9190614fb5565b6001600160601b031660648460000151838151811061254b5761254b614c9b565b60200260200101516001600160601b03166125669190614fe4565b10156125fc5760405162461bcd60e51b81526020600482015260646024820181905260008051602061537a83398151915260448301527f63683a207369676e61746f7269657320646f206e6f74206f776e206174206c65908201527f617374207468726573686f6c642070657263656e74616765206f6620612071756084820152636f72756d60e01b60a482015260c4016106f1565b8061260681614d60565b9150506124bf565b506000805463ffffffff169061262388613df6565b6040805160208082018490528183018790524360e01b6001600160e01b0319166060830152825160448184030181526064830180855281519183019190912063ffffffff881660008181526001909452928590205552905191925086917fc75557c4ad49697e231449688be13ef11cb6be8ed0d18819d8dde074a5a16f8a9181900360840190a26126b5826001614ea0565b6000805463ffffffff191663ffffffff929092169190911790555050505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146127215760405162461bcd60e51b81526004016106f190615003565b604051639926ee7d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639926ee7d9061276f908590859060040161507b565b600060405180830381600087803b15801561278957600080fd5b505af1158015612139573d6000803e3d6000fd5b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146127e55760405162461bcd60e51b81526004016106f190615003565b6040516351b27a6d60e11b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063a364f4da906024015b600060405180830381600087803b15801561284957600080fd5b505af115801561285d573d6000803e3d6000fd5b5050505050565b61286c613896565b60405163a98fb35560e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a98fb3559061282f9084906004016149bb565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639aa1653d6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561291a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293e9190614d15565b60ff1690508061295c57505060408051600081526020810190915290565b6000805b82811015612a1157604051633ca5a5f560e01b815260ff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633ca5a5f590602401602060405180830381865afa1580156129cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f39190614cd3565b6129fd9083614d48565b915080612a0981614d60565b915050612960565b506000816001600160401b03811115612a2c57612a2c6141e5565b604051908082528060200260200182016040528015612a55578160200160208202803683370190505b5090506000805b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639aa1653d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612aba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ade9190614d15565b60ff16811015612c7757604051633ca5a5f560e01b815260ff821660048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633ca5a5f590602401602060405180830381865afa158015612b52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b769190614cd3565b905060005b81811015612c62576040516356e4026d60e11b815260ff84166004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063adc804da906044016040805180830381865afa158015612bf0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c149190614d90565b60000151858581518110612c2a57612c2a614c9b565b6001600160a01b039092166020928302919091019091015283612c4c81614d60565b9450508080612c5a90614d60565b915050612b7b565b50508080612c6f90614d60565b915050612a5c565b5090949350505050565b600061012c612c93620189c084614ea0565b612c9d9190614ea0565b92915050565b612cab613896565b61070381613d18565b612cbc613896565b6001600160a01b038116612d215760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106f1565b61070381613bdc565b60fb60009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612da19190614bcf565b6001600160a01b0316336001600160a01b031614612dd15760405162461bcd60e51b81526004016106f190614bec565b60fc5419811960fc54191614612e4f5760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e756e70617573653a20696e76616c696420617474656d7060448201527f7420746f2070617573652066756e6374696f6e616c697479000000000000000060648201526084016106f1565b60fc81905560405181815233907f3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c9060200161083a565b6097546001600160a01b03163314612f1b5760405162461bcd60e51b815260206004820152604c60248201527f536572766963654d616e61676572426173652e6f6e6c7952657761726473496e60448201527f69746961746f723a2063616c6c6572206973206e6f742074686520726577617260648201526b32399034b734ba34b0ba37b960a11b608482015260a4016106f1565b60005b818110156131ef57828282818110612f3857612f38614c9b565b9050602002810190612f4a91906150c6565b612f5b9060408101906020016141af565b6001600160a01b03166323b872dd3330868686818110612f7d57612f7d614c9b565b9050602002810190612f8f91906150c6565b604080516001600160e01b031960e087901b1681526001600160a01b039485166004820152939092166024840152013560448201526064016020604051808303816000875af1158015612fe6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061300a9190614c36565b50600083838381811061301f5761301f614c9b565b905060200281019061303191906150c6565b6130429060408101906020016141af565b604051636eb1769f60e11b81523060048201526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166024830152919091169063dd62ed3e90604401602060405180830381865afa1580156130b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130d49190614cd3565b90508383838181106130e8576130e8614c9b565b90506020028101906130fa91906150c6565b61310b9060408101906020016141af565b6001600160a01b031663095ea7b37f00000000000000000000000000000000000000000000000000000000000000008387878781811061314d5761314d614c9b565b905060200281019061315f91906150c6565b6040013561316d9190614d48565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af11580156131b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131dc9190614c36565b5050806131e890614d60565b9050612f1e565b5060405163fce36c7d60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063fce36c7d9061276f9085908590600401615141565b6001600160a01b0381166132cc5760405162461bcd60e51b815260206004820152604960248201527f5061757361626c652e5f73657450617573657252656769737472793a206e657760448201527f50617573657252656769737472792063616e6e6f7420626520746865207a65726064820152686f206164647265737360b81b608482015260a4016106f1565b60fb54604080516001600160a01b03928316815291831660208301527f6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6910160405180910390a160fb80546001600160a01b0319166001600160a01b0392909216919091179055565b60408051808201909152600080825260208201526133516140c0565b835181526020808501519082015260408082018490526000908360608460076107d05a03fa905080801561338457613386565bfe5b50806133c45760405162461bcd60e51b815260206004820152600d60248201526c1958cb5b5d5b0b59985a5b1959609a1b60448201526064016106f1565b505092915050565b60408051808201909152600080825260208201526133e86140de565b835181526020808501518183015283516040808401919091529084015160608301526000908360808460066107d05a03fa90508080156133845750806133c45760405162461bcd60e51b815260206004820152600d60248201526c1958cb5859190b59985a5b1959609a1b60448201526064016106f1565b6134686140fc565b50604080516080810182527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c28183019081527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6060830152815281518083019092527f275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec82527f1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d60208381019190915281019190915290565b60408051808201909152600080825260208201526000808061355060008051602061535a83398151915286614cb1565b90505b61355c81613e09565b909350915060008051602061535a833981519152828309831415613596576040805180820190915290815260208101919091529392505050565b60008051602061535a833981519152600182089050613553565b6040805180820182528681526020808201869052825180840190935286835282018490526000918291906135e2614121565b60005b60028110156137a75760006135fb826006614fe4565b905084826002811061360f5761360f614c9b565b60200201515183613621836000614d48565b600c811061363157613631614c9b565b602002015284826002811061364857613648614c9b565b6020020151602001518382600161365f9190614d48565b600c811061366f5761366f614c9b565b602002015283826002811061368657613686614c9b565b6020020151515183613699836002614d48565b600c81106136a9576136a9614c9b565b60200201528382600281106136c0576136c0614c9b565b60200201515160016020020151836136d9836003614d48565b600c81106136e9576136e9614c9b565b602002015283826002811061370057613700614c9b565b60200201516020015160006002811061371b5761371b614c9b565b60200201518361372c836004614d48565b600c811061373c5761373c614c9b565b602002015283826002811061375357613753614c9b565b60200201516020015160016002811061376e5761376e614c9b565b60200201518361377f836005614d48565b600c811061378f5761378f614c9b565b6020020152508061379f81614d60565b9150506135e5565b506137b0614140565b60006020826101808560088cfa9151919c9115159b50909950505050505050505050565b60606000806137e284613a32565b61ffff166001600160401b038111156137fd576137fd6141e5565b6040519080825280601f01601f191660200182016040528015613827576020820181803683370190505b5090506000805b82518210801561383f575061010081105b15612c77576001811b935085841615613886578060f81b83838151811061386857613868614c9b565b60200101906001600160f81b031916908160001a9053508160010191505b61388f81614d60565b905061382e565b6065546001600160a01b03163314611fd65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106f1565b609754604080516001600160a01b03928316815291831660208301527fe11cddf1816a43318ca175bbc52cd0185436e9cbead7c83acc54a73e461717e3910160405180910390a1609780546001600160a01b0319166001600160a01b0392909216919091179055565b60c9805460ff19168215159081179091556040519081527f40e4ed880a29e0f6ddce307457fb75cddf4feef7d3ecb0301bfdf4976a0e2dfc906020015b60405180910390a150565b6000806139ad84613e8b565b9050808360ff166001901b11613a2b5760405162461bcd60e51b815260206004820152603f60248201527f4269746d61705574696c732e6f72646572656442797465734172726179546f4260448201527f69746d61703a206269746d61702065786365656473206d61782076616c75650060648201526084016106f1565b9392505050565b6000805b8215612c9d57613a47600184614dd1565b9092169180613a558161524e565b915050613a36565b60408051808201909152600080825260208201526102008261ffff1610613ab95760405162461bcd60e51b815260206004820152601060248201526f7363616c61722d746f6f2d6c6172676560801b60448201526064016106f1565b8161ffff1660011415613acd575081612c9d565b6040805180820190915260008082526020820181905284906001905b8161ffff168661ffff1610613b3657600161ffff871660ff83161c81161415613b1957613b1684846133cc565b93505b613b2383846133cc565b92506201fffe600192831b169101613ae9565b509195945050505050565b60408051808201909152600080825260208201528151158015613b6657506020820151155b15613b84575050604080518082019091526000808252602082015290565b60405180604001604052808360000151815260200160008051602061535a8339815191528460200151613bb79190614cb1565b613bcf9060008051602061535a833981519152614dd1565b905292915050565b919050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60fb546001600160a01b0316158015613c4f57506001600160a01b03821615155b613cd15760405162461bcd60e51b815260206004820152604760248201527f5061757361626c652e5f696e697469616c697a655061757365723a205f696e6960448201527f7469616c697a6550617573657228292063616e206f6e6c792062652063616c6c6064820152666564206f6e636560c81b608482015260a4016106f1565b60fc81905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2613d148261323e565b5050565b6001600160a01b038116600081815260026020908152604091829020805460ff8082161560ff1990921682179092558351948552161515908301527f5c3265f5fb462ef4930fe47beaa183647c97f19ba545b761f41bc8cd4621d4149101613996565b6000613db882604080518082019091526000808252602082015250604080518082019091528151815260609091015163ffffffff16602082015290565b6040805182516020808301919091529092015163ffffffff16908201526060015b604051602081830303815290604052805190602001209050919050565b600081604051602001613dd991906152de565b6000808060008051602061535a833981519152600360008051602061535a8339815191528660008051602061535a833981519152888909090890506000613e7f827f0c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5260008051602061535a833981519152614018565b91959194509092505050565b600061010082511115613f145760405162461bcd60e51b8152602060048201526044602482018190527f4269746d61705574696c732e6f72646572656442797465734172726179546f42908201527f69746d61703a206f7264657265644279746573417272617920697320746f6f206064820152636c6f6e6760e01b608482015260a4016106f1565b8151613f2257506000919050565b60008083600081518110613f3857613f38614c9b565b0160200151600160f89190911c81901b92505b845181101561400f57848181518110613f6657613f66614c9b565b0160200151600160f89190911c1b9150828211613ffb5760405162461bcd60e51b815260206004820152604760248201527f4269746d61705574696c732e6f72646572656442797465734172726179546f4260448201527f69746d61703a206f72646572656442797465734172726179206973206e6f74206064820152661bdc99195c995960ca1b608482015260a4016106f1565b9181179161400881614d60565b9050613f4b565b50909392505050565b600080614023614140565b61402b61415e565b602080825281810181905260408201819052606082018890526080820187905260a082018690528260c08360056107d05a03fa92508280156133845750826140b55760405162461bcd60e51b815260206004820152601a60248201527f424e3235342e6578704d6f643a2063616c6c206661696c75726500000000000060448201526064016106f1565b505195945050505050565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806040016040528061410f61417c565b815260200161411c61417c565b905290565b604051806101800160405280600c906020820280368337509192915050565b60405180602001604052806001906020820280368337509192915050565b6040518060c001604052806006906020820280368337509192915050565b60405180604001604052806002906020820280368337509192915050565b6001600160a01b038116811461070357600080fd5b6000602082840312156141c157600080fd5b8135613a2b8161419a565b6000602082840312156141de57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561421d5761421d6141e5565b60405290565b60405161010081016001600160401b038111828210171561421d5761421d6141e5565b604051601f8201601f191681016001600160401b038111828210171561426e5761426e6141e5565b604052919050565b60006040828403121561428857600080fd5b6142906141fb565b9050813581526020820135602082015292915050565b600082601f8301126142b757600080fd5b6142bf6141fb565b8060408401858111156142d157600080fd5b845b818110156142eb5780358452602093840193016142d3565b509095945050505050565b60006080828403121561430857600080fd5b6143106141fb565b905061431c83836142a6565b815261432b83604084016142a6565b602082015292915050565b600080600080610120858703121561434d57600080fd5b8435935061435e8660208701614276565b925061436d86606087016142f6565b915061437c8660e08701614276565b905092959194509250565b8035613bd78161419a565b6020808252825182820181905260009190848201906040850190845b818110156143d35783516001600160a01b0316835292840192918401916001016143ae565b50909695505050505050565b801515811461070357600080fd5b6000602082840312156143ff57600080fd5b8135613a2b816143df565b60ff8116811461070357600080fd5b60006020828403121561442b57600080fd5b8135613a2b8161440a565b803563ffffffff81168114613bd757600080fd5b60006001600160401b03821115614463576144636141e5565b5060051b60200190565b600082601f83011261447e57600080fd5b8135602061449361448e8361444a565b614246565b82815260059290921b840181019181810190868411156144b257600080fd5b8286015b848110156144d4576144c781614436565b83529183019183016144b6565b509695505050505050565b600082601f8301126144f057600080fd5b8135602061450061448e8361444a565b82815260069290921b8401810191818101908684111561451f57600080fd5b8286015b848110156144d4576145358882614276565b835291830191604001614523565b600082601f83011261455457600080fd5b8135602061456461448e8361444a565b82815260059290921b8401810191818101908684111561458357600080fd5b8286015b848110156144d45780356001600160401b038111156145a65760008081fd5b6145b48986838b010161446d565b845250918301918301614587565b600061018082840312156145d557600080fd5b6145dd614223565b905081356001600160401b03808211156145f657600080fd5b6146028583860161446d565b8352602084013591508082111561461857600080fd5b614624858386016144df565b6020840152604084013591508082111561463d57600080fd5b614649858386016144df565b604084015261465b85606086016142f6565b606084015261466d8560e08601614276565b608084015261012084013591508082111561468757600080fd5b6146938583860161446d565b60a08401526101408401359150808211156146ad57600080fd5b6146b98583860161446d565b60c08401526101608401359150808211156146d357600080fd5b506146e084828501614543565b60e08301525092915050565b60008060008060006080868803121561470457600080fd5b8535945060208601356001600160401b038082111561472257600080fd5b818801915088601f83011261473657600080fd5b81358181111561474557600080fd5b89602082850101111561475757600080fd5b602083019650945061476b60408901614436565b9350606088013591508082111561478157600080fd5b5061478e888289016145c2565b9150509295509295909350565b600081518084526020808501945080840160005b838110156147d45781516001600160601b0316875295820195908201906001016147af565b509495945050505050565b60408152600083516040808401526147fa608084018261479b565b90506020850151603f19848303016060850152614817828261479b565b925050508260208301529392505050565b600080600080600060a0868803121561484057600080fd5b853561484b8161419a565b9450602086810135945060408701356148638161419a565b935060608701356001600160401b0381111561487e57600080fd5b8701601f8101891361488f57600080fd5b803561489d61448e8261444a565b81815260059190911b8201830190838101908b8311156148bc57600080fd5b928401925b828410156148e35783356148d48161419a565b825292840192908401906148c1565b80965050505050506148f760808701614387565b90509295509295909350565b6000806040838503121561491657600080fd5b82356001600160401b038082111561492d57600080fd5b908401906080828703121561494157600080fd5b9092506020840135908082111561495757600080fd5b50614964858286016145c2565b9150509250929050565b6000815180845260005b8181101561499457602081850181015186830182015201614978565b818111156149a6576000602083870101525b50601f01601f19169290920160200192915050565b602081526000613a2b602083018461496e565b60006001600160401b038311156149e7576149e76141e5565b6149fa601f8401601f1916602001614246565b9050828152838383011115614a0e57600080fd5b828260208301376000602084830101529392505050565b600082601f830112614a3657600080fd5b613a2b838335602085016149ce565b60008060408385031215614a5857600080fd5b8235614a638161419a565b915060208301356001600160401b0380821115614a7f57600080fd5b9084019060608287031215614a9357600080fd5b604051606081018181108382111715614aae57614aae6141e5565b604052823582811115614ac057600080fd5b614acc88828601614a25565b82525060208301356020820152604083013560408201528093505050509250929050565b600060208284031215614b0257600080fd5b81356001600160401b03811115614b1857600080fd5b8201601f81018413614b2957600080fd5b614b38848235602084016149ce565b949350505050565b600060208284031215614b5257600080fd5b613a2b82614436565b60008060208385031215614b6e57600080fd5b82356001600160401b0380821115614b8557600080fd5b818501915085601f830112614b9957600080fd5b813581811115614ba857600080fd5b8660208260051b8501011115614bbd57600080fd5b60209290920196919550909350505050565b600060208284031215614be157600080fd5b8151613a2b8161419a565b6020808252602a908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526939903ab73830bab9b2b960b11b606082015260800190565b600060208284031215614c4857600080fd5b8151613a2b816143df565b60208082526028908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526739903830bab9b2b960c11b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600082614cce57634e487b7160e01b600052601260045260246000fd5b500690565b600060208284031215614ce557600080fd5b5051919050565b600060208284031215614cfe57600080fd5b81516001600160c01b0381168114613a2b57600080fd5b600060208284031215614d2757600080fd5b8151613a2b8161440a565b634e487b7160e01b600052601160045260246000fd5b60008219821115614d5b57614d5b614d32565b500190565b6000600019821415614d7457614d74614d32565b5060010190565b6001600160601b038116811461070357600080fd5b600060408284031215614da257600080fd5b614daa6141fb565b8251614db58161419a565b81526020830151614dc581614d7b565b60208201529392505050565b600082821015614de357614de3614d32565b500390565b600060208284031215614dfa57600080fd5b815167ffffffffffffffff1981168114613a2b57600080fd5b600060208284031215614e2557600080fd5b8151613a2b81614d7b565b60006001600160601b0383811690831681811015614e5057614e50614d32565b039392505050565b63ffffffff60e01b8360e01b1681526000600482018351602080860160005b83811015614e9357815185529382019390820190600101614e77565b5092979650505050505050565b600063ffffffff808316818516808303821115614ebf57614ebf614d32565b01949350505050565b6000808335601e19843603018112614edf57600080fd5b8301803591506001600160401b03821115614ef957600080fd5b602001915036819003821315614f0e57600080fd5b9250929050565b600060808236031215614f2757600080fd5b604051608081016001600160401b038282108183111715614f4a57614f4a6141e5565b81604052843583526020850135915080821115614f6657600080fd5b614f7236838701614a25565b60208401526040850135915080821115614f8b57600080fd5b50614f9836828601614a25565b604083015250614faa60608401614436565b606082015292915050565b60006001600160601b0380831681851681830481118215151615614fdb57614fdb614d32565b02949350505050565b6000816000190483118215151615614ffe57614ffe614d32565b500290565b60208082526052908201527f536572766963654d616e61676572426173652e6f6e6c7952656769737472794360408201527f6f6f7264696e61746f723a2063616c6c6572206973206e6f742074686520726560608201527133b4b9ba393c9031b7b7b93234b730ba37b960711b608082015260a00190565b60018060a01b03831681526040602082015260008251606060408401526150a560a084018261496e565b90506020840151606084015260408401516080840152809150509392505050565b60008235609e198336030181126150dc57600080fd5b9190910192915050565b8183526000602080850194508260005b858110156147d45781356151098161419a565b6001600160a01b031687528183013561512181614d7b565b6001600160601b03168784015260409687019691909101906001016150f6565b60208082528181018390526000906040808401600586901b8501820187855b8881101561524057878303603f190184528135368b9003609e1901811261518657600080fd5b8a0160a0813536839003601e1901811261519f57600080fd5b820180356001600160401b038111156151b757600080fd5b8060061b36038413156151c957600080fd5b8287526151db838801828c85016150e6565b925050506151ea888301614387565b6001600160a01b0316888601528187013587860152606061520c818401614436565b63ffffffff16908601526080615223838201614436565b63ffffffff16950194909452509285019290850190600101615160565b509098975050505050505050565b600061ffff8083168181141561526657615266614d32565b6001019392505050565b6000808335601e1984360301811261528757600080fd5b83016020810192503590506001600160401b038111156152a657600080fd5b803603831315614f0e57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081528135602082015260006152f86020840184615270565b6080604085015261530d60a0850182846152b5565b91505061531d6040850185615270565b848303601f190160608601526153348382846152b5565b9250505063ffffffff61534960608601614436565b166080840152809150509291505056fe30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47456967656e4441536572766963654d616e616765722e636f6e6669726d426174424c535369676e6174757265436865636b65722e636865636b5369676e617475a264697066735822122008275249ec223e380ed13f4b0596eded1ae53958c6cf6885326bd25b493a845a64736f6c634300080c0033 + /// ``` + #[rustfmt::skip] + #[allow(clippy::all)] + pub static DEPLOYED_BYTECODE: alloy_sol_types::private::Bytes = alloy_sol_types::private::Bytes::from_static( + b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x02SW`\x005`\xE0\x1C\x80cw\x94\x96Z\x11a\x01FW\x80c\xDF\\\xF7#\x11a\0\xC3W\x80c\xEF\x02DX\x11a\0\x87W\x80c\xEF\x02DX\x14a\x05\xE3W\x80c\xF1\"\t\x83\x14a\x05\xEBW\x80c\xF2\xFD\xE3\x8B\x14a\x05\xFEW\x80c\xFA\xBC\x1C\xBC\x14a\x06\x11W\x80c\xFC)\x9D\xEE\x14a\x06$W\x80c\xFC\xE3l}\x14a\x067W`\0\x80\xFD[\x80c\xDF\\\xF7#\x14a\x05dW\x80c\xE1R4\xFF\x14a\x05\x8BW\x80c\xE4\x81\xAF\x9D\x14a\x05\xA8W\x80c\xEA\xEF\xD2}\x14a\x05\xB0W\x80c\xEC\xCB\xBF\xC9\x14a\x05\xC3W`\0\x80\xFD[\x80c\xA3d\xF4\xDA\x11a\x01\nW\x80c\xA3d\xF4\xDA\x14a\x04\xEEW\x80c\xA5\xB7\x89\n\x14a\x05\x01W\x80c\xA9\x8F\xB3U\x14a\x05$W\x80c\xB9\x8D\t\x08\x14a\x057W\x80c\xBA\xFA\x91\x07\x14a\x05DW`\0\x80\xFD[\x80cw\x94\x96Z\x14a\x04wW\x80c\x86\x87\xFE\xAE\x14a\x04\x8AW\x80c\x88o\x11\x95\x14a\x04\xB7W\x80c\x8D\xA5\xCB[\x14a\x04\xCAW\x80c\x99&\xEE}\x14a\x04\xDBW`\0\x80\xFD[\x80c]\xF4YF\x11a\x01\xD4W\x80cm\x14\xA9\x87\x11a\x01\x98W\x80cm\x14\xA9\x87\x14a\x04\x06W\x80cn\xFBF6\x14a\x04-W\x80cqP\x18\xA6\x14a\x04NW\x80cr\xD1\x8E\x8D\x14a\x04VW\x80cw[\xBC\xB5\x14a\x04dW`\0\x80\xFD[\x80c]\xF4YF\x14a\x03gW\x80c^\x034v\x14a\x03\xA6W\x80c^\x8B?-\x14a\x03\xB0W\x80ch0H5\x14a\x03\xB9W\x80ck:\xA7.\x14a\x03\xE0W`\0\x80\xFD[\x80cAl~^\x11a\x02\x1BW\x80cAl~^\x14a\x02\xE2W\x80cIr\x13J\x14a\x02\xF5W\x80cY\\jg\x14a\x03\x1AW\x80cZ\xC8j\xB7\x14a\x03\"W\x80c\\\x97Z\xBB\x14a\x03UW`\0\x80\xFD[\x80c\x10\xD6z/\x14a\x02XW\x80c\x13d9\xDD\x14a\x02mW\x80c\x17\x1F\x1D[\x14a\x02\x80W\x80c3\xCF\xB7\xB7\x14a\x02\xAFW\x80c;\xC2\x8C\x8C\x14a\x02\xCFW[`\0\x80\xFD[a\x02ka\x02f6`\x04aA\xAFV[a\x06JV[\0[a\x02ka\x02{6`\x04aA\xCCV[a\x07\x06V[a\x02\x93a\x02\x8E6`\x04aC6V[a\x08EV[`@\x80Q\x92\x15\x15\x83R\x90\x15\x15` \x83\x01R\x01[`@Q\x80\x91\x03\x90\xF3[a\x02\xC2a\x02\xBD6`\x04aA\xAFV[a\t\xCFV[`@Qa\x02\xA6\x91\x90aC\x92V[a\x02ka\x02\xDD6`\x04aA\xAFV[a\x0E\x9EV[a\x02ka\x02\xF06`\x04aC\xEDV[a\x0E\xAFV[`\0Ta\x03\x05\x90c\xFF\xFF\xFF\xFF\x16\x81V[`@Qc\xFF\xFF\xFF\xFF\x90\x91\x16\x81R` \x01a\x02\xA6V[a\x02ka\x0F\xE6V[a\x03Ea\x0306`\x04aD\x19V[`\xFCT`\x01`\xFF\x90\x92\x16\x91\x90\x91\x1B\x90\x81\x16\x14\x90V[`@Q\x90\x15\x15\x81R` \x01a\x02\xA6V[`\xFCT[`@Q\x90\x81R` \x01a\x02\xA6V[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x02\xA6V[a\x03\x05b\x01\x89\xC0\x81V[a\x03\x05a\x01,\x81V[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0a\x03\x8EV[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[a\x04@a\x04;6`\x04aF\xECV[a\x10\xADV[`@Qa\x02\xA6\x92\x91\x90aG\xDFV[a\x02ka\x1F\xC4V[`\0Tc\xFF\xFF\xFF\xFF\x16a\x03\x05V[a\x02ka\x04r6`\x04aH(V[a\x1F\xD8V[a\x02ka\x04\x856`\x04aI\x03V[a!AV[a\x04\xAA`@Q\x80`@\x01`@R\x80`\x01\x81R` \x01`!`\xF8\x1B\x81RP\x81V[`@Qa\x02\xA6\x91\x90aI\xBBV[`\xFBTa\x03\x8E\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`eT`\x01`\x01`\xA0\x1B\x03\x16a\x03\x8EV[a\x02ka\x04\xE96`\x04aJEV[a&\xD9V[a\x02ka\x04\xFC6`\x04aA\xAFV[a'\x9DV[a\x03Ea\x05\x0F6`\x04aA\xAFV[`\x02` R`\0\x90\x81R`@\x90 T`\xFF\x16\x81V[a\x02ka\x0526`\x04aJ\xF0V[a(dV[`\xC9Ta\x03E\x90`\xFF\x16\x81V[a\x04\xAA`@Q\x80`@\x01`@R\x80`\x01\x81R` \x01`7`\xF8\x1B\x81RP\x81V[a\x03\x8E\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81V[a\x04\xAA`@Q\x80`@\x01`@R\x80`\x01\x81R` \x01`\0\x81RP\x81V[a\x02\xC2a(\xB8V[a\x03\x05a\x05\xBE6`\x04aK@V[a,\x81V[a\x03Ya\x05\xD16`\x04aK@V[`\x01` R`\0\x90\x81R`@\x90 T\x81V[a\x03Y`d\x81V[a\x02ka\x05\xF96`\x04aA\xAFV[a,\xA3V[a\x02ka\x06\x0C6`\x04aA\xAFV[a,\xB4V[a\x02ka\x06\x1F6`\x04aA\xCCV[a-*V[`\x97Ta\x03\x8E\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[a\x02ka\x06E6`\x04aK[V[a.\x86V[`\xFB`\0\x90T\x90a\x01\0\n\x90\x04`\x01`\x01`\xA0\x1B\x03\x16`\x01`\x01`\xA0\x1B\x03\x16c\xEA\xB6mz`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06\x9DW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06\xC1\x91\x90aK\xCFV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\xFAW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aK\xECV[`@Q\x80\x91\x03\x90\xFD[a\x07\x03\x81a2>V[PV[`\xFBT`@Qc#}\xFBG`\xE1\x1B\x81R3`\x04\x82\x01R`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x90cF\xFB\xF6\x8E\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07NW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07r\x91\x90aL6V[a\x07\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aLSV[`\xFCT\x81\x81\x16\x14a\x08\x07W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7FPausable.pause: invalid attempt `D\x82\x01R\x7Fto unpause functionality\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`\xFC\x81\x90U`@Q\x81\x81R3\x90\x7F\xAB@\xA3t\xBCQ\xDE7\"\0\xA8\xBC\x98\x1A\xF8\xC9\xEC\xDC\x08\xDF\xDA\xEF\x0B\xB6\xE0\x9F\x88\xF3\xC6\x16\xEF=\x90` \x01[`@Q\x80\x91\x03\x90\xA2PV[`\0\x80`\0\x7F0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X](3\xE8Hy\xB9p\x91C\xE1\xF5\x93\xF0\0\0\x01\x87\x87`\0\x01Q\x88` \x01Q\x88`\0\x01Q`\0`\x02\x81\x10a\x08\x8DWa\x08\x8DaL\x9BV[` \x02\x01Q\x89Q`\x01` \x02\x01Q\x8A` \x01Q`\0`\x02\x81\x10a\x08\xB2Wa\x08\xB2aL\x9BV[` \x02\x01Q\x8B` \x01Q`\x01`\x02\x81\x10a\x08\xCEWa\x08\xCEaL\x9BV[` \x90\x81\x02\x91\x90\x91\x01Q\x8CQ\x8D\x83\x01Q`@Qa\t+\x9A\x99\x98\x97\x96\x95\x94\x01\x98\x89R` \x89\x01\x97\x90\x97R`@\x88\x01\x95\x90\x95R``\x87\x01\x93\x90\x93R`\x80\x86\x01\x91\x90\x91R`\xA0\x85\x01R`\xC0\x84\x01R`\xE0\x83\x01Ra\x01\0\x82\x01Ra\x01 \x01\x90V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 `\0\x1Ca\tN\x91\x90aL\xB1V[\x90Pa\t\xC1a\tga\t`\x88\x84a35V[\x86\x90a3\xCCV[a\toa4`V[a\t\xB7a\t\xA8\x85a\t\xA2`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x91\x82\x01R\x81Q\x80\x83\x01\x90\x92R`\x01\x82R`\x02\x90\x82\x01R\x90V[\x90a35V[a\t\xB1\x8Ca5 V[\x90a3\xCCV[\x88b\x01\xD4\xC0a5\xB0V[\x90\x98\x90\x97P\x95PPPPPPV[`@Qc\t\xAA\x15'`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x82\x81\x16`\x04\x83\x01R``\x91`\0\x91\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\x13T*N\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\n;W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n_\x91\x90aL\xD3V[`@Qc\x87\x1E\xF0I`\xE0\x1B\x81R`\x04\x81\x01\x82\x90R\x90\x91P`\0\x90`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\x87\x1E\xF0I\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\n\xCAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xEE\x91\x90aL\xECV[\x90P`\x01`\x01`\xC0\x1B\x03\x81\x16\x15\x80a\x0B\x88WP\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x9A\xA1e=`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0B_W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0B\x83\x91\x90aM\x15V[`\xFF\x16\x15[\x15a\x0B\xA4WPP`@\x80Q`\0\x81R` \x81\x01\x90\x91R\x92\x91PPV[`\0a\x0B\xB8\x82`\x01`\x01`\xC0\x1B\x03\x16a7\xD4V[\x90P`\0\x80[\x82Q\x81\x10\x15a\x0C\x8EW\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c<\xA5\xA5\xF5\x84\x83\x81Q\x81\x10a\x0C\x08Wa\x0C\x08aL\x9BV[\x01` \x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x84\x90\x1B\x16\x81R`\xF8\x91\x90\x91\x1C`\x04\x82\x01R`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0CLW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0Cp\x91\x90aL\xD3V[a\x0Cz\x90\x83aMHV[\x91P\x80a\x0C\x86\x81aM`V[\x91PPa\x0B\xBEV[P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xA9Wa\x0C\xA9aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0C\xD2W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P`\0\x80[\x84Q\x81\x10\x15a\x0E\x91W`\0\x85\x82\x81Q\x81\x10a\x0C\xF6Wa\x0C\xF6aL\x9BV[\x01` \x01Q`@Qc<\xA5\xA5\xF5`\xE0\x1B\x81R`\xF8\x91\x90\x91\x1C`\x04\x82\x01\x81\x90R\x91P`\0\x90`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c<\xA5\xA5\xF5\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\rkW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\r\x8F\x91\x90aL\xD3V[\x90P`\0[\x81\x81\x10\x15a\x0E{W`@QcV\xE4\x02m`\xE1\x1B\x81R`\xFF\x84\x16`\x04\x82\x01R`$\x81\x01\x82\x90R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c\xAD\xC8\x04\xDA\x90`D\x01`@\x80Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\tW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E-\x91\x90aM\x90V[`\0\x01Q\x86\x86\x81Q\x81\x10a\x0ECWa\x0ECaL\x9BV[`\x01`\x01`\xA0\x1B\x03\x90\x92\x16` \x92\x83\x02\x91\x90\x91\x01\x90\x91\x01R\x84a\x0Ee\x81aM`V[\x95PP\x80\x80a\x0Es\x90aM`V[\x91PPa\r\x94V[PPP\x80\x80a\x0E\x89\x90aM`V[\x91PPa\x0C\xD9V[P\x90\x97\x96PPPPPPPV[a\x0E\xA6a8\x96V[a\x07\x03\x81a8\xF0V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x8D\xA5\xCB[`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0F\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0F1\x91\x90aK\xCFV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x0F\xDDW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\\`$\x82\x01R\x7FBLSSignatureChecker.onlyCoordina`D\x82\x01R\x7FtorOwner: caller is not the owne`d\x82\x01R\x7Fr of the registryCoordinator\0\0\0\0`\x84\x82\x01R`\xA4\x01a\x06\xF1V[a\x07\x03\x81a9YV[`\xFBT`@Qc#}\xFBG`\xE1\x1B\x81R3`\x04\x82\x01R`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x90cF\xFB\xF6\x8E\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x10.W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10R\x91\x90aL6V[a\x10nW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aLSV[`\0\x19`\xFC\x81\x90U`@Q\x90\x81R3\x90\x7F\xAB@\xA3t\xBCQ\xDE7\"\0\xA8\xBC\x98\x1A\xF8\xC9\xEC\xDC\x08\xDF\xDA\xEF\x0B\xB6\xE0\x9F\x88\xF3\xC6\x16\xEF=\x90` \x01`@Q\x80\x91\x03\x90\xA2V[`@\x80Q\x80\x82\x01\x90\x91R``\x80\x82R` \x82\x01R`\0\x84a\x11$W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: empty quorum input\0\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`@\x83\x01QQ\x85\x14\x80\x15a\x11W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`D`$\x82\x01\x81\x90R`\0\x80Q` aS\x9A\x839\x81Q\x91R\x90\x82\x01R\x7Fres: input nonsigner length mism`d\x82\x01Rc\x0C.\x8Cm`\xE3\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[Cc\xFF\xFF\xFF\xFF\x16\x84c\xFF\xFF\xFF\xFF\x16\x10a\x12\xADW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`<`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: invalid reference block\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`@\x80Q\x80\x82\x01\x82R`\0\x80\x82R` \x80\x83\x01\x91\x90\x91R\x82Q\x80\x84\x01\x90\x93R``\x80\x84R\x90\x83\x01R\x90\x86`\x01`\x01`@\x1B\x03\x81\x11\x15a\x12\xEEWa\x12\xEEaA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x13\x17W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P` \x82\x01R\x86`\x01`\x01`@\x1B\x03\x81\x11\x15a\x135Wa\x135aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x13^W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R`@\x80Q\x80\x82\x01\x90\x91R``\x80\x82R` \x82\x01R\x85` \x01QQ`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x92Wa\x13\x92aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x13\xBBW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x86\x01QQ`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\xDBWa\x13\xDBaA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x14\x04W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81` \x01\x81\x90RP`\0a\x14\xD6\x8A\x8A\x80\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x93\x92\x91\x90\x81\x81R` \x01\x83\x83\x80\x82\x847`\0\x92\x01\x91\x90\x91RPP`@\x80Qc\x9A\xA1e=`\xE0\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x93Pc\x9A\xA1e=\x92P`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x14\xADW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x14\xD1\x91\x90aM\x15V[a9\xA1V[\x90P`\0[\x87` \x01QQ\x81\x10\x15a\x17qWa\x15 \x88` \x01Q\x82\x81Q\x81\x10a\x15\x01Wa\x15\x01aL\x9BV[` \x02` \x01\x01Q\x80Q`\0\x90\x81R` \x91\x82\x01Q\x90\x91R`@\x90 \x90V[\x83` \x01Q\x82\x81Q\x81\x10a\x156Wa\x156aL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01R\x80\x15a\x15\xF6W` \x83\x01Qa\x15W`\x01\x83aM\xD1V[\x81Q\x81\x10a\x15gWa\x15gaL\x9BV[` \x02` \x01\x01Q`\0\x1C\x83` \x01Q\x82\x81Q\x81\x10a\x15\x88Wa\x15\x88aL\x9BV[` \x02` \x01\x01Q`\0\x1C\x11a\x15\xF6W`@\x80QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`$\x81\x01\x91\x90\x91R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: nonSignerPubkeys not sorted`d\x82\x01R`\x84\x01a\x06\xF1V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x04\xECcQ\x84` \x01Q\x83\x81Q\x81\x10a\x16;Wa\x16;aL\x9BV[` \x02` \x01\x01Q\x8B\x8B`\0\x01Q\x85\x81Q\x81\x10a\x16ZWa\x16ZaL\x9BV[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x16\x97\x93\x92\x91\x90\x92\x83Rc\xFF\xFF\xFF\xFF\x91\x82\x16` \x84\x01R\x16`@\x82\x01R``\x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x16\xB4W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x16\xD8\x91\x90aL\xECV[`\x01`\x01`\xC0\x1B\x03\x16\x83`\0\x01Q\x82\x81Q\x81\x10a\x16\xF7Wa\x16\xF7aL\x9BV[` \x02` \x01\x01\x81\x81RPPa\x17]a\t`a\x171\x84\x86`\0\x01Q\x85\x81Q\x81\x10a\x17#Wa\x17#aL\x9BV[` \x02` \x01\x01Q\x16a:2V[\x8A` \x01Q\x84\x81Q\x81\x10a\x17GWa\x17GaL\x9BV[` \x02` \x01\x01Qa:]\x90\x91\x90c\xFF\xFF\xFF\xFF\x16V[\x94P\x80a\x17i\x81aM`V[\x91PPa\x14\xDBV[PPa\x17|\x83a;AV[`\xC9T\x90\x93P`\xFF\x16`\0\x81a\x17\x93W`\0a\x18\x15V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\xC4H\xFE\xB8`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xF1W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x18\x15\x91\x90aL\xD3V[\x90P`\0[\x8A\x81\x10\x15a\x1E\x93W\x82\x15a\x19uW\x89c\xFF\xFF\xFF\xFF\x16\x82\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c$\x9A\x0CB\x8F\x8F\x86\x81\x81\x10a\x18qWa\x18qaL\x9BV[`@Q`\xE0\x85\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81R\x92\x015`\xF8\x1C`\x04\x83\x01RP`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x18\xB1W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x18\xD5\x91\x90aL\xD3V[a\x18\xDF\x91\x90aMHV[\x11a\x19uW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`f`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: StakeRegistry updates must `d\x82\x01R\x7Fbe within withdrawalDelayBlocks `\x84\x82\x01Rewindow`\xD0\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16ch\xBC\xCA\xAC\x8D\x8D\x84\x81\x81\x10a\x19\xB6Wa\x19\xB6aL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C\x8C\x8C`\xA0\x01Q\x85\x81Q\x81\x10a\x19\xDAWa\x19\xDAaL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x86\x90\x1B\x16\x81R`\xFF\x90\x93\x16`\x04\x84\x01Rc\xFF\xFF\xFF\xFF\x91\x82\x16`$\x84\x01R\x16`D\x82\x01R`d\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1AZ\x91\x90aM\xE8V[`\x01`\x01`@\x1B\x03\x19\x16a\x1A}\x8A`@\x01Q\x83\x81Q\x81\x10a\x15\x01Wa\x15\x01aL\x9BV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x19\x16\x14a\x1B\x19W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`a`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: quorumApk hash in storage d`d\x82\x01R\x7Foes not match provided quorum ap`\x84\x82\x01R`k`\xF8\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[a\x1BI\x89`@\x01Q\x82\x81Q\x81\x10a\x1B2Wa\x1B2aL\x9BV[` \x02` \x01\x01Q\x87a3\xCC\x90\x91\x90c\xFF\xFF\xFF\xFF\x16V[\x95P\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\xC8)LV\x8D\x8D\x84\x81\x81\x10a\x1B\x8CWa\x1B\x8CaL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C\x8C\x8C`\xC0\x01Q\x85\x81Q\x81\x10a\x1B\xB0Wa\x1B\xB0aL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x86\x90\x1B\x16\x81R`\xFF\x90\x93\x16`\x04\x84\x01Rc\xFF\xFF\xFF\xFF\x91\x82\x16`$\x84\x01R\x16`D\x82\x01R`d\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1C\x0CW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1C0\x91\x90aN\x13V[\x85` \x01Q\x82\x81Q\x81\x10a\x1CFWa\x1CFaL\x9BV[`\x01`\x01``\x1B\x03\x90\x92\x16` \x92\x83\x02\x91\x90\x91\x01\x82\x01R\x85\x01Q\x80Q\x82\x90\x81\x10a\x1CrWa\x1CraL\x9BV[` \x02` \x01\x01Q\x85`\0\x01Q\x82\x81Q\x81\x10a\x1C\x90Wa\x1C\x90aL\x9BV[` \x02` \x01\x01\x90`\x01`\x01``\x1B\x03\x16\x90\x81`\x01`\x01``\x1B\x03\x16\x81RPP`\0\x80[\x8A` \x01QQ\x81\x10\x15a\x1E~Wa\x1D\x08\x86`\0\x01Q\x82\x81Q\x81\x10a\x1C\xDAWa\x1C\xDAaL\x9BV[` \x02` \x01\x01Q\x8F\x8F\x86\x81\x81\x10a\x1C\xF4Wa\x1C\xF4aL\x9BV[`\x01\x92\x015`\xF8\x1C\x92\x90\x92\x1C\x81\x16\x14\x91\x90PV[\x15a\x1ElW\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\xF2\xBE\x94\xAE\x8F\x8F\x86\x81\x81\x10a\x1DNWa\x1DNaL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C\x8E\x89` \x01Q\x85\x81Q\x81\x10a\x1DrWa\x1DraL\x9BV[` \x02` \x01\x01Q\x8F`\xE0\x01Q\x88\x81Q\x81\x10a\x1D\x90Wa\x1D\x90aL\x9BV[` \x02` \x01\x01Q\x87\x81Q\x81\x10a\x1D\xA9Wa\x1D\xA9aL\x9BV[` \x90\x81\x02\x91\x90\x91\x01\x01Q`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x87\x90\x1B\x16\x81R`\xFF\x90\x94\x16`\x04\x85\x01Rc\xFF\xFF\xFF\xFF\x92\x83\x16`$\x85\x01R`D\x84\x01\x91\x90\x91R\x16`d\x82\x01R`\x84\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1E\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1E1\x91\x90aN\x13V[\x87Q\x80Q\x85\x90\x81\x10a\x1EEWa\x1EEaL\x9BV[` \x02` \x01\x01\x81\x81Qa\x1EY\x91\x90aN0V[`\x01`\x01``\x1B\x03\x16\x90RP`\x01\x90\x91\x01\x90[\x80a\x1Ev\x81aM`V[\x91PPa\x1C\xB4V[PP\x80\x80a\x1E\x8B\x90aM`V[\x91PPa\x18\x1AV[PPP`\0\x80a\x1E\xAD\x8C\x86\x8A``\x01Q\x8B`\x80\x01Qa\x08EV[\x91P\x91P\x81a\x1F\x1EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`C`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: pairing precompile call fai`d\x82\x01Rb\x1B\x19Y`\xEA\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[\x80a\x1F\x7FW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`9`$\x82\x01R`\0\x80Q` aS\x9A\x839\x81Q\x91R`D\x82\x01R\x7Fres: signature is invalid\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[PP`\0\x87\x82` \x01Q`@Q` \x01a\x1F\x9A\x92\x91\x90aNXV[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x80Q` \x90\x91\x01 \x92\x9B\x92\x9AP\x91\x98PPPPPPPPPV[a\x1F\xCCa8\x96V[a\x1F\xD6`\0a;\xDCV[V[`2Ta\x01\0\x90\x04`\xFF\x16\x15\x80\x80\x15a\x1F\xF8WP`2T`\x01`\xFF\x90\x91\x16\x10[\x80a \x12WP0;\x15\x80\x15a \x12WP`2T`\xFF\x16`\x01\x14[a uW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`.`$\x82\x01R\x7FInitializable: contract is alrea`D\x82\x01Rm\x19\x1EH\x1A[\x9A]\x1AX[\x1A^\x99Y`\x92\x1B`d\x82\x01R`\x84\x01a\x06\xF1V[`2\x80T`\xFF\x19\x16`\x01\x17\x90U\x80\x15a \x98W`2\x80Ta\xFF\0\x19\x16a\x01\0\x17\x90U[a \xA2\x86\x86a<.V[a \xAB\x84a;\xDCV[a \xB4\x82a8\xF0V[`\0[\x83Q\x81\x10\x15a \xF2Wa \xE2\x84\x82\x81Q\x81\x10a \xD5Wa \xD5aL\x9BV[` \x02` \x01\x01Qa=\x18V[a \xEB\x81aM`V[\x90Pa \xB7V[P\x80\x15a!9W`2\x80Ta\xFF\0\x19\x16\x90U`@Q`\x01\x81R\x7F\x7F&\xB8?\xF9n\x1F+jh/\x138R\xF6y\x8A\t\xC4e\xDA\x95\x92\x14`\xCE\xFB8G@$\x98\x90` \x01`@Q\x80\x91\x03\x90\xA1[PPPPPPV[`\xFCT`\0\x90`\x01\x90\x81\x16\x14\x15a!\x9AW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x19`$\x82\x01R\x7FPausable: index is paused\0\0\0\0\0\0\0`D\x82\x01R`d\x01a\x06\xF1V[3`\0\x90\x81R`\x02` R`@\x90 T`\xFF\x16a\"\x0EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`,`$\x82\x01R\x7FonlyBatchConfirmer: not from bat`D\x82\x01Rk1\xB4\x101\xB7\xB734\xB96\xB2\xB9`\xA1\x1B`d\x82\x01R`\x84\x01a\x06\xF1V[23\x14a\"\x8BW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`Q`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: header and nonsigner data mu`d\x82\x01Rpst be in calldata`x\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[Ca\"\x9C`\x80\x85\x01``\x86\x01aK@V[c\xFF\xFF\xFF\xFF\x16\x10a#\x1BW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`O`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: specified referenceBlockNumb`d\x82\x01Rner is in future`\x88\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[c\xFF\xFF\xFF\xFFC\x16a\x01,a#5`\x80\x86\x01``\x87\x01aK@V[a#?\x91\x90aN\xA0V[c\xFF\xFF\xFF\xFF\x16\x10\x15a#\xC5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`U`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: specified referenceBlockNumb`d\x82\x01Rt\x19\\\x88\x1A\\\xC8\x1D\x1B\xDB\xC8\x19\x98\\\x88\x1A[\x88\x1C\x18\\\xDD`Z\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[a#\xD2`@\x84\x01\x84aN\xC8V[\x90Pa#\xE1` \x85\x01\x85aN\xC8V[\x90P\x14a$yW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`f`$\x82\x01R`\0\x80Q` aSz\x839\x81Q\x91R`D\x82\x01R\x7Fch: quorumNumbers and signedStak`d\x82\x01R\x7FeForQuorums must be of the same `\x84\x82\x01Re\r\x8C\xAD\xCC\xEE\x8D`\xD3\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[`\0a$\x8Ca$\x87\x85aO\x15V[a={V[\x90P`\0\x80a$\xB8\x83a$\xA2` \x89\x01\x89aN\xC8V[a$\xB2`\x80\x8B\x01``\x8C\x01aK@V[\x89a\x10\xADV[\x91P\x91P`\0[a$\xCC`@\x88\x01\x88aN\xC8V[\x90P\x81\x10\x15a&\x0EWa$\xE2`@\x88\x01\x88aN\xC8V[\x82\x81\x81\x10a$\xF2Wa$\xF2aL\x9BV[\x90P\x015`\xF8\x1C`\xF8\x1B`\xF8\x1C`\xFF\x16\x83` \x01Q\x82\x81Q\x81\x10a%\x18Wa%\x18aL\x9BV[` \x02` \x01\x01Qa%*\x91\x90aO\xB5V[`\x01`\x01``\x1B\x03\x16`d\x84`\0\x01Q\x83\x81Q\x81\x10a%KWa%KaL\x9BV[` \x02` \x01\x01Q`\x01`\x01``\x1B\x03\x16a%f\x91\x90aO\xE4V[\x10\x15a%\xFCW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`d`$\x82\x01\x81\x90R`\0\x80Q` aSz\x839\x81Q\x91R`D\x83\x01R\x7Fch: signatories do not own at le\x90\x82\x01R\x7Fast threshold percentage of a qu`\x84\x82\x01Rcorum`\xE0\x1B`\xA4\x82\x01R`\xC4\x01a\x06\xF1V[\x80a&\x06\x81aM`V[\x91PPa$\xBFV[P`\0\x80Tc\xFF\xFF\xFF\xFF\x16\x90a&#\x88a=\xF6V[`@\x80Q` \x80\x82\x01\x84\x90R\x81\x83\x01\x87\x90RC`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16``\x83\x01R\x82Q`D\x81\x84\x03\x01\x81R`d\x83\x01\x80\x85R\x81Q\x91\x83\x01\x91\x90\x91 c\xFF\xFF\xFF\xFF\x88\x16`\0\x81\x81R`\x01\x90\x94R\x92\x85\x90 UR\x90Q\x91\x92P\x86\x91\x7F\xC7UW\xC4\xADIi~#\x14Ih\x8B\xE1>\xF1\x1C\xB6\xBE\x8E\xD0\xD1\x88\x19\xD8\xDD\xE0t\xA5\xA1o\x8A\x91\x81\x90\x03`\x84\x01\x90\xA2a&\xB5\x82`\x01aN\xA0V[`\0\x80Tc\xFF\xFF\xFF\xFF\x19\x16c\xFF\xFF\xFF\xFF\x92\x90\x92\x16\x91\x90\x91\x17\x90UPPPPPPPPV[3`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a'!W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aP\x03V[`@Qc\x99&\xEE}`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\x99&\xEE}\x90a'o\x90\x85\x90\x85\x90`\x04\x01aP{V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a'\x89W`\0\x80\xFD[PZ\xF1\x15\x80\x15a!9W=`\0\x80>=`\0\xFD[3`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x14a'\xE5W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aP\x03V[`@QcQ\xB2zm`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x82\x81\x16`\x04\x83\x01R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\xA3d\xF4\xDA\x90`$\x01[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a(IW`\0\x80\xFD[PZ\xF1\x15\x80\x15a(]W=`\0\x80>=`\0\xFD[PPPPPV[a(la8\x96V[`@Qc\xA9\x8F\xB3U`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\xA9\x8F\xB3U\x90a(/\x90\x84\x90`\x04\x01aI\xBBV[```\0\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x9A\xA1e=`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a)\x1AW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a)>\x91\x90aM\x15V[`\xFF\x16\x90P\x80a)\\WPP`@\x80Q`\0\x81R` \x81\x01\x90\x91R\x90V[`\0\x80[\x82\x81\x10\x15a*\x11W`@Qc<\xA5\xA5\xF5`\xE0\x1B\x81R`\xFF\x82\x16`\x04\x82\x01R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c<\xA5\xA5\xF5\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a)\xCFW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a)\xF3\x91\x90aL\xD3V[a)\xFD\x90\x83aMHV[\x91P\x80a*\t\x81aM`V[\x91PPa)`V[P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a*,Wa*,aA\xE5V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a*UW\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x90P`\0\x80[\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16c\x9A\xA1e=`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a*\xBAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a*\xDE\x91\x90aM\x15V[`\xFF\x16\x81\x10\x15a,wW`@Qc<\xA5\xA5\xF5`\xE0\x1B\x81R`\xFF\x82\x16`\x04\x82\x01R`\0\x90\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c<\xA5\xA5\xF5\x90`$\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a+RW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a+v\x91\x90aL\xD3V[\x90P`\0[\x81\x81\x10\x15a,bW`@QcV\xE4\x02m`\xE1\x1B\x81R`\xFF\x84\x16`\x04\x82\x01R`$\x81\x01\x82\x90R\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0`\x01`\x01`\xA0\x1B\x03\x16\x90c\xAD\xC8\x04\xDA\x90`D\x01`@\x80Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a+\xF0W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a,\x14\x91\x90aM\x90V[`\0\x01Q\x85\x85\x81Q\x81\x10a,*Wa,*aL\x9BV[`\x01`\x01`\xA0\x1B\x03\x90\x92\x16` \x92\x83\x02\x91\x90\x91\x01\x90\x91\x01R\x83a,L\x81aM`V[\x94PP\x80\x80a,Z\x90aM`V[\x91PPa+{V[PP\x80\x80a,o\x90aM`V[\x91PPa*\\V[P\x90\x94\x93PPPPV[`\0a\x01,a,\x93b\x01\x89\xC0\x84aN\xA0V[a,\x9D\x91\x90aN\xA0V[\x92\x91PPV[a,\xABa8\x96V[a\x07\x03\x81a=\x18V[a,\xBCa8\x96V[`\x01`\x01`\xA0\x1B\x03\x81\x16a-!W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\xF1V[a\x07\x03\x81a;\xDCV[`\xFB`\0\x90T\x90a\x01\0\n\x90\x04`\x01`\x01`\xA0\x1B\x03\x16`\x01`\x01`\xA0\x1B\x03\x16c\xEA\xB6mz`@Q\x81c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a-}W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a-\xA1\x91\x90aK\xCFV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a-\xD1W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xF1\x90aK\xECV[`\xFCT\x19\x81\x19`\xFCT\x19\x16\x14a.OW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`8`$\x82\x01R\x7FPausable.unpause: invalid attemp`D\x82\x01R\x7Ft to pause functionality\0\0\0\0\0\0\0\0`d\x82\x01R`\x84\x01a\x06\xF1V[`\xFC\x81\x90U`@Q\x81\x81R3\x90\x7F5\x82\xD1\x82\x8E&\xBFV\xBD\x80\x15\x02\xBC\x02\x1A\xC0\xBC\x8A\xFBW\xC8&\xE4\x98kEY<\x8F\xAD8\x9C\x90` \x01a\x08:V[`\x97T`\x01`\x01`\xA0\x1B\x03\x163\x14a/\x1BW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`L`$\x82\x01R\x7FServiceManagerBase.onlyRewardsIn`D\x82\x01R\x7Fitiator: caller is not the rewar`d\x82\x01Rk29\x904\xB74\xBA4\xB0\xBA7\xB9`\xA1\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[`\0[\x81\x81\x10\x15a1\xEFW\x82\x82\x82\x81\x81\x10a/8Wa/8aL\x9BV[\x90P` \x02\x81\x01\x90a/J\x91\x90aP\xC6V[a/[\x90`@\x81\x01\x90` \x01aA\xAFV[`\x01`\x01`\xA0\x1B\x03\x16c#\xB8r\xDD30\x86\x86\x86\x81\x81\x10a/}Wa/}aL\x9BV[\x90P` \x02\x81\x01\x90a/\x8F\x91\x90aP\xC6V[`@\x80Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x87\x90\x1B\x16\x81R`\x01`\x01`\xA0\x1B\x03\x94\x85\x16`\x04\x82\x01R\x93\x90\x92\x16`$\x84\x01R\x015`D\x82\x01R`d\x01` `@Q\x80\x83\x03\x81`\0\x87Z\xF1\x15\x80\x15a/\xE6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a0\n\x91\x90aL6V[P`\0\x83\x83\x83\x81\x81\x10a0\x1FWa0\x1FaL\x9BV[\x90P` \x02\x81\x01\x90a01\x91\x90aP\xC6V[a0B\x90`@\x81\x01\x90` \x01aA\xAFV[`@Qcn\xB1v\x9F`\xE1\x1B\x81R0`\x04\x82\x01R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x81\x16`$\x83\x01R\x91\x90\x91\x16\x90c\xDDb\xED>\x90`D\x01` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a0\xB0W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a0\xD4\x91\x90aL\xD3V[\x90P\x83\x83\x83\x81\x81\x10a0\xE8Wa0\xE8aL\x9BV[\x90P` \x02\x81\x01\x90a0\xFA\x91\x90aP\xC6V[a1\x0B\x90`@\x81\x01\x90` \x01aA\xAFV[`\x01`\x01`\xA0\x1B\x03\x16c\t^\xA7\xB3\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x83\x87\x87\x87\x81\x81\x10a1MWa1MaL\x9BV[\x90P` \x02\x81\x01\x90a1_\x91\x90aP\xC6V[`@\x015a1m\x91\x90aMHV[`@Q`\x01`\x01`\xE0\x1B\x03\x19`\xE0\x85\x90\x1B\x16\x81R`\x01`\x01`\xA0\x1B\x03\x90\x92\x16`\x04\x83\x01R`$\x82\x01R`D\x01` `@Q\x80\x83\x03\x81`\0\x87Z\xF1\x15\x80\x15a1\xB8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a1\xDC\x91\x90aL6V[PP\x80a1\xE8\x90aM`V[\x90Pa/\x1EV[P`@Qc\xFC\xE3l}`\xE0\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x7F\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x16\x90c\xFC\xE3l}\x90a'o\x90\x85\x90\x85\x90`\x04\x01aQAV[`\x01`\x01`\xA0\x1B\x03\x81\x16a2\xCCW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`I`$\x82\x01R\x7FPausable._setPauserRegistry: new`D\x82\x01R\x7FPauserRegistry cannot be the zer`d\x82\x01Rho address`\xB8\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[`\xFBT`@\x80Q`\x01`\x01`\xA0\x1B\x03\x92\x83\x16\x81R\x91\x83\x16` \x83\x01R\x7Fn\x9F\xCDS\x98\x96\xFC\xA6\x0E\x8B\x0F\x01\xDDX\x023\xE4\x8Ak\x0F}\xF0\x13\xB8\x9B\xA7\xF5e\x86\x9A\xCD\xB6\x91\x01`@Q\x80\x91\x03\x90\xA1`\xFB\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01Ra3Qa@\xC0V[\x83Q\x81R` \x80\x85\x01Q\x90\x82\x01R`@\x80\x82\x01\x84\x90R`\0\x90\x83``\x84`\x07a\x07\xD0Z\x03\xFA\x90P\x80\x80\x15a3\x84Wa3\x86V[\xFE[P\x80a3\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\r`$\x82\x01Rl\x19X\xCB[][\x0BY\x98Z[\x19Y`\x9A\x1B`D\x82\x01R`d\x01a\x06\xF1V[PP\x92\x91PPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01Ra3\xE8a@\xDEV[\x83Q\x81R` \x80\x85\x01Q\x81\x83\x01R\x83Q`@\x80\x84\x01\x91\x90\x91R\x90\x84\x01Q``\x83\x01R`\0\x90\x83`\x80\x84`\x06a\x07\xD0Z\x03\xFA\x90P\x80\x80\x15a3\x84WP\x80a3\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\r`$\x82\x01Rl\x19X\xCBXY\x19\x0BY\x98Z[\x19Y`\x9A\x1B`D\x82\x01R`d\x01a\x06\xF1V[a4ha@\xFCV[P`@\x80Q`\x80\x81\x01\x82R\x7F\x19\x8E\x93\x93\x92\rH:r`\xBF\xB71\xFB]%\xF1\xAAI35\xA9\xE7\x12\x97\xE4\x85\xB7\xAE\xF3\x12\xC2\x81\x83\x01\x90\x81R\x7F\x18\0\xDE\xEF\x12\x1F\x1EvBj\0f^\\DygC\"\xD4\xF7^\xDA\xDDF\xDE\xBD\\\xD9\x92\xF6\xED``\x83\x01R\x81R\x81Q\x80\x83\x01\x90\x92R\x7F']\xC4\xA2\x88\xD1\xAF\xB3\xCB\xB1\xAC\t\x18u$\xC7\xDB69]\xF7\xBE;\x99\xE6s\xB1:\x07Ze\xEC\x82R\x7F\x1D\x9B\xEF\xCD\x05\xA52>m\xA4\xD45\xF3\xB6\x17\xCD\xB3\xAF\x83(\\-\xF7\x11\xEF9\xC0\x15q\x82\x7F\x9D` \x83\x81\x01\x91\x90\x91R\x81\x01\x91\x90\x91R\x90V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R`\0\x80\x80a5P`\0\x80Q` aSZ\x839\x81Q\x91R\x86aL\xB1V[\x90P[a5\\\x81a>\tV[\x90\x93P\x91P`\0\x80Q` aSZ\x839\x81Q\x91R\x82\x83\t\x83\x14\x15a5\x96W`@\x80Q\x80\x82\x01\x90\x91R\x90\x81R` \x81\x01\x91\x90\x91R\x93\x92PPPV[`\0\x80Q` aSZ\x839\x81Q\x91R`\x01\x82\x08\x90Pa5SV[`@\x80Q\x80\x82\x01\x82R\x86\x81R` \x80\x82\x01\x86\x90R\x82Q\x80\x84\x01\x90\x93R\x86\x83R\x82\x01\x84\x90R`\0\x91\x82\x91\x90a5\xE2aA!V[`\0[`\x02\x81\x10\x15a7\xA7W`\0a5\xFB\x82`\x06aO\xE4V[\x90P\x84\x82`\x02\x81\x10a6\x0FWa6\x0FaL\x9BV[` \x02\x01QQ\x83a6!\x83`\0aMHV[`\x0C\x81\x10a61Wa61aL\x9BV[` \x02\x01R\x84\x82`\x02\x81\x10a6HWa6HaL\x9BV[` \x02\x01Q` \x01Q\x83\x82`\x01a6_\x91\x90aMHV[`\x0C\x81\x10a6oWa6oaL\x9BV[` \x02\x01R\x83\x82`\x02\x81\x10a6\x86Wa6\x86aL\x9BV[` \x02\x01QQQ\x83a6\x99\x83`\x02aMHV[`\x0C\x81\x10a6\xA9Wa6\xA9aL\x9BV[` \x02\x01R\x83\x82`\x02\x81\x10a6\xC0Wa6\xC0aL\x9BV[` \x02\x01QQ`\x01` \x02\x01Q\x83a6\xD9\x83`\x03aMHV[`\x0C\x81\x10a6\xE9Wa6\xE9aL\x9BV[` \x02\x01R\x83\x82`\x02\x81\x10a7\0Wa7\0aL\x9BV[` \x02\x01Q` \x01Q`\0`\x02\x81\x10a7\x1BWa7\x1BaL\x9BV[` \x02\x01Q\x83a7,\x83`\x04aMHV[`\x0C\x81\x10a7F\x17\x17\xE3\x91\x01`@Q\x80\x91\x03\x90\xA1`\x97\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[`\xC9\x80T`\xFF\x19\x16\x82\x15\x15\x90\x81\x17\x90\x91U`@Q\x90\x81R\x7F@\xE4\xED\x88\n)\xE0\xF6\xDD\xCE0tW\xFBu\xCD\xDFO\xEE\xF7\xD3\xEC\xB00\x1B\xFD\xF4\x97j\x0E-\xFC\x90` \x01[`@Q\x80\x91\x03\x90\xA1PV[`\0\x80a9\xAD\x84a>\x8BV[\x90P\x80\x83`\xFF\x16`\x01\x90\x1B\x11a:+W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`?`$\x82\x01R\x7FBitmapUtils.orderedBytesArrayToB`D\x82\x01R\x7Fitmap: bitmap exceeds max value\0`d\x82\x01R`\x84\x01a\x06\xF1V[\x93\x92PPPV[`\0\x80[\x82\x15a,\x9DWa:G`\x01\x84aM\xD1V[\x90\x92\x16\x91\x80a:U\x81aRNV[\x91PPa:6V[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01Ra\x02\0\x82a\xFF\xFF\x16\x10a:\xB9W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x10`$\x82\x01Roscalar-too-large`\x80\x1B`D\x82\x01R`d\x01a\x06\xF1V[\x81a\xFF\xFF\x16`\x01\x14\x15a:\xCDWP\x81a,\x9DV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01\x81\x90R\x84\x90`\x01\x90[\x81a\xFF\xFF\x16\x86a\xFF\xFF\x16\x10a;6W`\x01a\xFF\xFF\x87\x16`\xFF\x83\x16\x1C\x81\x16\x14\x15a;\x19Wa;\x16\x84\x84a3\xCCV[\x93P[a;#\x83\x84a3\xCCV[\x92Pb\x01\xFF\xFE`\x01\x92\x83\x1B\x16\x91\x01a:\xE9V[P\x91\x95\x94PPPPPV[`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x81Q\x15\x80\x15a;fWP` \x82\x01Q\x15[\x15a;\x84WPP`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01R\x90V[`@Q\x80`@\x01`@R\x80\x83`\0\x01Q\x81R` \x01`\0\x80Q` aSZ\x839\x81Q\x91R\x84` \x01Qa;\xB7\x91\x90aL\xB1V[a;\xCF\x90`\0\x80Q` aSZ\x839\x81Q\x91RaM\xD1V[\x90R\x92\x91PPV[\x91\x90PV[`e\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x90\x93U`@Q\x91\x16\x91\x90\x82\x90\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x90`\0\x90\xA3PPV[`\xFBT`\x01`\x01`\xA0\x1B\x03\x16\x15\x80\x15aV[PPV[`\x01`\x01`\xA0\x1B\x03\x81\x16`\0\x81\x81R`\x02` \x90\x81R`@\x91\x82\x90 \x80T`\xFF\x80\x82\x16\x15`\xFF\x19\x90\x92\x16\x82\x17\x90\x92U\x83Q\x94\x85R\x16\x15\x15\x90\x83\x01R\x7F\\2e\xF5\xFBF.\xF4\x93\x0F\xE4{\xEA\xA1\x83d|\x97\xF1\x9B\xA5E\xB7a\xF4\x1B\xC8\xCDF!\xD4\x14\x91\x01a9\x96V[`\0a=\xB8\x82`@\x80Q\x80\x82\x01\x90\x91R`\0\x80\x82R` \x82\x01RP`@\x80Q\x80\x82\x01\x90\x91R\x81Q\x81R``\x90\x91\x01Qc\xFF\xFF\xFF\xFF\x16` \x82\x01R\x90V[`@\x80Q\x82Q` \x80\x83\x01\x91\x90\x91R\x90\x92\x01Qc\xFF\xFF\xFF\xFF\x16\x90\x82\x01R``\x01[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x80Q\x90` \x01 \x90P\x91\x90PV[`\0\x81`@Q` \x01a=\xD9\x91\x90aR\xDEV[`\0\x80\x80`\0\x80Q` aSZ\x839\x81Q\x91R`\x03`\0\x80Q` aSZ\x839\x81Q\x91R\x86`\0\x80Q` aSZ\x839\x81Q\x91R\x88\x89\t\t\x08\x90P`\0a>\x7F\x82\x7F\x0C\x19\x13\x9C\xB8Lh\nn\x14\x11m\xA0`V\x17e\xE0Z\xA4Z\x1Cr\xA3O\x08#\x05\xB6\x1F?R`\0\x80Q` aSZ\x839\x81Q\x91Ra@\x18V[\x91\x95\x91\x94P\x90\x92PPPV[`\0a\x01\0\x82Q\x11\x15a?\x14W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`D`$\x82\x01\x81\x90R\x7FBitmapUtils.orderedBytesArrayToB\x90\x82\x01R\x7Fitmap: orderedBytesArray is too `d\x82\x01Rclong`\xE0\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[\x81Qa?\"WP`\0\x91\x90PV[`\0\x80\x83`\0\x81Q\x81\x10a?8Wa?8aL\x9BV[\x01` \x01Q`\x01`\xF8\x91\x90\x91\x1C\x81\x90\x1B\x92P[\x84Q\x81\x10\x15a@\x0FW\x84\x81\x81Q\x81\x10a?fWa?faL\x9BV[\x01` \x01Q`\x01`\xF8\x91\x90\x91\x1C\x1B\x91P\x82\x82\x11a?\xFBW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`G`$\x82\x01R\x7FBitmapUtils.orderedBytesArrayToB`D\x82\x01R\x7Fitmap: orderedBytesArray is not `d\x82\x01Rf\x1B\xDC\x99\x19\\\x99Y`\xCA\x1B`\x84\x82\x01R`\xA4\x01a\x06\xF1V[\x91\x81\x17\x91a@\x08\x81aM`V[\x90Pa?KV[P\x90\x93\x92PPPV[`\0\x80a@#aA@V[a@+aA^V[` \x80\x82R\x81\x81\x01\x81\x90R`@\x82\x01\x81\x90R``\x82\x01\x88\x90R`\x80\x82\x01\x87\x90R`\xA0\x82\x01\x86\x90R\x82`\xC0\x83`\x05a\x07\xD0Z\x03\xFA\x92P\x82\x80\x15a3\x84WP\x82a@\xB5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`\x1A`$\x82\x01R\x7FBN254.expMod: call failure\0\0\0\0\0\0`D\x82\x01R`d\x01a\x06\xF1V[PQ\x95\x94PPPPPV[`@Q\x80``\x01`@R\x80`\x03\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`\x80\x01`@R\x80`\x04\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`@\x01`@R\x80aA\x0FaA|V[\x81R` \x01aA\x1CaA|V[\x90R\x90V[`@Q\x80a\x01\x80\x01`@R\x80`\x0C\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80` \x01`@R\x80`\x01\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`\xC0\x01`@R\x80`\x06\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`@Q\x80`@\x01`@R\x80`\x02\x90` \x82\x02\x806\x837P\x91\x92\x91PPV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x07\x03W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aA\xC1W`\0\x80\xFD[\x815a:+\x81aA\x9AV[`\0` \x82\x84\x03\x12\x15aA\xDEW`\0\x80\xFD[P5\x91\x90PV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aB\x1DWaB\x1DaA\xE5V[`@R\x90V[`@Qa\x01\0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aB\x1DWaB\x1DaA\xE5V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15aBnWaBnaA\xE5V[`@R\x91\x90PV[`\0`@\x82\x84\x03\x12\x15aB\x88W`\0\x80\xFD[aB\x90aA\xFBV[\x90P\x815\x81R` \x82\x015` \x82\x01R\x92\x91PPV[`\0\x82`\x1F\x83\x01\x12aB\xB7W`\0\x80\xFD[aB\xBFaA\xFBV[\x80`@\x84\x01\x85\x81\x11\x15aB\xD1W`\0\x80\xFD[\x84[\x81\x81\x10\x15aB\xEBW\x805\x84R` \x93\x84\x01\x93\x01aB\xD3V[P\x90\x95\x94PPPPPV[`\0`\x80\x82\x84\x03\x12\x15aC\x08W`\0\x80\xFD[aC\x10aA\xFBV[\x90PaC\x1C\x83\x83aB\xA6V[\x81RaC+\x83`@\x84\x01aB\xA6V[` \x82\x01R\x92\x91PPV[`\0\x80`\0\x80a\x01 \x85\x87\x03\x12\x15aCMW`\0\x80\xFD[\x845\x93PaC^\x86` \x87\x01aBvV[\x92PaCm\x86``\x87\x01aB\xF6V[\x91PaC|\x86`\xE0\x87\x01aBvV[\x90P\x92\x95\x91\x94P\x92PV[\x805a;\xD7\x81aA\x9AV[` \x80\x82R\x82Q\x82\x82\x01\x81\x90R`\0\x91\x90\x84\x82\x01\x90`@\x85\x01\x90\x84[\x81\x81\x10\x15aC\xD3W\x83Q`\x01`\x01`\xA0\x1B\x03\x16\x83R\x92\x84\x01\x92\x91\x84\x01\x91`\x01\x01aC\xAEV[P\x90\x96\x95PPPPPPV[\x80\x15\x15\x81\x14a\x07\x03W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aC\xFFW`\0\x80\xFD[\x815a:+\x81aC\xDFV[`\xFF\x81\x16\x81\x14a\x07\x03W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aD+W`\0\x80\xFD[\x815a:+\x81aD\nV[\x805c\xFF\xFF\xFF\xFF\x81\x16\x81\x14a;\xD7W`\0\x80\xFD[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15aDcWaDcaA\xE5V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12aD~W`\0\x80\xFD[\x815` aD\x93aD\x8E\x83aDJV[aBFV[\x82\x81R`\x05\x92\x90\x92\x1B\x84\x01\x81\x01\x91\x81\x81\x01\x90\x86\x84\x11\x15aD\xB2W`\0\x80\xFD[\x82\x86\x01[\x84\x81\x10\x15aD\xD4WaD\xC7\x81aD6V[\x83R\x91\x83\x01\x91\x83\x01aD\xB6V[P\x96\x95PPPPPPV[`\0\x82`\x1F\x83\x01\x12aD\xF0W`\0\x80\xFD[\x815` aE\0aD\x8E\x83aDJV[\x82\x81R`\x06\x92\x90\x92\x1B\x84\x01\x81\x01\x91\x81\x81\x01\x90\x86\x84\x11\x15aE\x1FW`\0\x80\xFD[\x82\x86\x01[\x84\x81\x10\x15aD\xD4WaE5\x88\x82aBvV[\x83R\x91\x83\x01\x91`@\x01aE#V[`\0\x82`\x1F\x83\x01\x12aETW`\0\x80\xFD[\x815` aEdaD\x8E\x83aDJV[\x82\x81R`\x05\x92\x90\x92\x1B\x84\x01\x81\x01\x91\x81\x81\x01\x90\x86\x84\x11\x15aE\x83W`\0\x80\xFD[\x82\x86\x01[\x84\x81\x10\x15aD\xD4W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15aE\xA6W`\0\x80\x81\xFD[aE\xB4\x89\x86\x83\x8B\x01\x01aDmV[\x84RP\x91\x83\x01\x91\x83\x01aE\x87V[`\0a\x01\x80\x82\x84\x03\x12\x15aE\xD5W`\0\x80\xFD[aE\xDDaB#V[\x90P\x815`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aE\xF6W`\0\x80\xFD[aF\x02\x85\x83\x86\x01aDmV[\x83R` \x84\x015\x91P\x80\x82\x11\x15aF\x18W`\0\x80\xFD[aF$\x85\x83\x86\x01aD\xDFV[` \x84\x01R`@\x84\x015\x91P\x80\x82\x11\x15aF=W`\0\x80\xFD[aFI\x85\x83\x86\x01aD\xDFV[`@\x84\x01RaF[\x85``\x86\x01aB\xF6V[``\x84\x01RaFm\x85`\xE0\x86\x01aBvV[`\x80\x84\x01Ra\x01 \x84\x015\x91P\x80\x82\x11\x15aF\x87W`\0\x80\xFD[aF\x93\x85\x83\x86\x01aDmV[`\xA0\x84\x01Ra\x01@\x84\x015\x91P\x80\x82\x11\x15aF\xADW`\0\x80\xFD[aF\xB9\x85\x83\x86\x01aDmV[`\xC0\x84\x01Ra\x01`\x84\x015\x91P\x80\x82\x11\x15aF\xD3W`\0\x80\xFD[PaF\xE0\x84\x82\x85\x01aECV[`\xE0\x83\x01RP\x92\x91PPV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15aG\x04W`\0\x80\xFD[\x855\x94P` \x86\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aG\"W`\0\x80\xFD[\x81\x88\x01\x91P\x88`\x1F\x83\x01\x12aG6W`\0\x80\xFD[\x815\x81\x81\x11\x15aGEW`\0\x80\xFD[\x89` \x82\x85\x01\x01\x11\x15aGWW`\0\x80\xFD[` \x83\x01\x96P\x94PaGk`@\x89\x01aD6V[\x93P``\x88\x015\x91P\x80\x82\x11\x15aG\x81W`\0\x80\xFD[PaG\x8E\x88\x82\x89\x01aE\xC2V[\x91PP\x92\x95P\x92\x95\x90\x93PV[`\0\x81Q\x80\x84R` \x80\x85\x01\x94P\x80\x84\x01`\0[\x83\x81\x10\x15aG\xD4W\x81Q`\x01`\x01``\x1B\x03\x16\x87R\x95\x82\x01\x95\x90\x82\x01\x90`\x01\x01aG\xAFV[P\x94\x95\x94PPPPPV[`@\x81R`\0\x83Q`@\x80\x84\x01RaG\xFA`\x80\x84\x01\x82aG\x9BV[\x90P` \x85\x01Q`?\x19\x84\x83\x03\x01``\x85\x01RaH\x17\x82\x82aG\x9BV[\x92PPP\x82` \x83\x01R\x93\x92PPPV[`\0\x80`\0\x80`\0`\xA0\x86\x88\x03\x12\x15aH@W`\0\x80\xFD[\x855aHK\x81aA\x9AV[\x94P` \x86\x81\x015\x94P`@\x87\x015aHc\x81aA\x9AV[\x93P``\x87\x015`\x01`\x01`@\x1B\x03\x81\x11\x15aH~W`\0\x80\xFD[\x87\x01`\x1F\x81\x01\x89\x13aH\x8FW`\0\x80\xFD[\x805aH\x9DaD\x8E\x82aDJV[\x81\x81R`\x05\x91\x90\x91\x1B\x82\x01\x83\x01\x90\x83\x81\x01\x90\x8B\x83\x11\x15aH\xBCW`\0\x80\xFD[\x92\x84\x01\x92[\x82\x84\x10\x15aH\xE3W\x835aH\xD4\x81aA\x9AV[\x82R\x92\x84\x01\x92\x90\x84\x01\x90aH\xC1V[\x80\x96PPPPPPaH\xF7`\x80\x87\x01aC\x87V[\x90P\x92\x95P\x92\x95\x90\x93PV[`\0\x80`@\x83\x85\x03\x12\x15aI\x16W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aI-W`\0\x80\xFD[\x90\x84\x01\x90`\x80\x82\x87\x03\x12\x15aIAW`\0\x80\xFD[\x90\x92P` \x84\x015\x90\x80\x82\x11\x15aIWW`\0\x80\xFD[PaId\x85\x82\x86\x01aE\xC2V[\x91PP\x92P\x92\x90PV[`\0\x81Q\x80\x84R`\0[\x81\x81\x10\x15aI\x94W` \x81\x85\x01\x81\x01Q\x86\x83\x01\x82\x01R\x01aIxV[\x81\x81\x11\x15aI\xA6W`\0` \x83\x87\x01\x01R[P`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[` \x81R`\0a:+` \x83\x01\x84aInV[`\0`\x01`\x01`@\x1B\x03\x83\x11\x15aI\xE7WaI\xE7aA\xE5V[aI\xFA`\x1F\x84\x01`\x1F\x19\x16` \x01aBFV[\x90P\x82\x81R\x83\x83\x83\x01\x11\x15aJ\x0EW`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12aJ6W`\0\x80\xFD[a:+\x83\x835` \x85\x01aI\xCEV[`\0\x80`@\x83\x85\x03\x12\x15aJXW`\0\x80\xFD[\x825aJc\x81aA\x9AV[\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aJ\x7FW`\0\x80\xFD[\x90\x84\x01\x90``\x82\x87\x03\x12\x15aJ\x93W`\0\x80\xFD[`@Q``\x81\x01\x81\x81\x10\x83\x82\x11\x17\x15aJ\xAEWaJ\xAEaA\xE5V[`@R\x825\x82\x81\x11\x15aJ\xC0W`\0\x80\xFD[aJ\xCC\x88\x82\x86\x01aJ%V[\x82RP` \x83\x015` \x82\x01R`@\x83\x015`@\x82\x01R\x80\x93PPPP\x92P\x92\x90PV[`\0` \x82\x84\x03\x12\x15aK\x02W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15aK\x18W`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13aK)W`\0\x80\xFD[aK8\x84\x825` \x84\x01aI\xCEV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15aKRW`\0\x80\xFD[a:+\x82aD6V[`\0\x80` \x83\x85\x03\x12\x15aKnW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x80\x82\x11\x15aK\x85W`\0\x80\xFD[\x81\x85\x01\x91P\x85`\x1F\x83\x01\x12aK\x99W`\0\x80\xFD[\x815\x81\x81\x11\x15aK\xA8W`\0\x80\xFD[\x86` \x82`\x05\x1B\x85\x01\x01\x11\x15aK\xBDW`\0\x80\xFD[` \x92\x90\x92\x01\x96\x91\x95P\x90\x93PPPPV[`\0` \x82\x84\x03\x12\x15aK\xE1W`\0\x80\xFD[\x81Qa:+\x81aA\x9AV[` \x80\x82R`*\x90\x82\x01R\x7Fmsg.sender is not permissioned a`@\x82\x01Ri9\x90:\xB780\xBA\xB9\xB2\xB9`\xB1\x1B``\x82\x01R`\x80\x01\x90V[`\0` \x82\x84\x03\x12\x15aLHW`\0\x80\xFD[\x81Qa:+\x81aC\xDFV[` \x80\x82R`(\x90\x82\x01R\x7Fmsg.sender is not permissioned a`@\x82\x01Rg9\x9080\xBA\xB9\xB2\xB9`\xC1\x1B``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x82aL\xCEWcNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[P\x06\x90V[`\0` \x82\x84\x03\x12\x15aL\xE5W`\0\x80\xFD[PQ\x91\x90PV[`\0` \x82\x84\x03\x12\x15aL\xFEW`\0\x80\xFD[\x81Q`\x01`\x01`\xC0\x1B\x03\x81\x16\x81\x14a:+W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aM'W`\0\x80\xFD[\x81Qa:+\x81aD\nV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0\x82\x19\x82\x11\x15aM[WaM[aM2V[P\x01\x90V[`\0`\0\x19\x82\x14\x15aMtWaMtaM2V[P`\x01\x01\x90V[`\x01`\x01``\x1B\x03\x81\x16\x81\x14a\x07\x03W`\0\x80\xFD[`\0`@\x82\x84\x03\x12\x15aM\xA2W`\0\x80\xFD[aM\xAAaA\xFBV[\x82QaM\xB5\x81aA\x9AV[\x81R` \x83\x01QaM\xC5\x81aM{V[` \x82\x01R\x93\x92PPPV[`\0\x82\x82\x10\x15aM\xE3WaM\xE3aM2V[P\x03\x90V[`\0` \x82\x84\x03\x12\x15aM\xFAW`\0\x80\xFD[\x81Qg\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x19\x81\x16\x81\x14a:+W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15aN%W`\0\x80\xFD[\x81Qa:+\x81aM{V[`\0`\x01`\x01``\x1B\x03\x83\x81\x16\x90\x83\x16\x81\x81\x10\x15aNPWaNPaM2V[\x03\x93\x92PPPV[c\xFF\xFF\xFF\xFF`\xE0\x1B\x83`\xE0\x1B\x16\x81R`\0`\x04\x82\x01\x83Q` \x80\x86\x01`\0[\x83\x81\x10\x15aN\x93W\x81Q\x85R\x93\x82\x01\x93\x90\x82\x01\x90`\x01\x01aNwV[P\x92\x97\x96PPPPPPPV[`\0c\xFF\xFF\xFF\xFF\x80\x83\x16\x81\x85\x16\x80\x83\x03\x82\x11\x15aN\xBFWaN\xBFaM2V[\x01\x94\x93PPPPV[`\0\x80\x835`\x1E\x19\x846\x03\x01\x81\x12aN\xDFW`\0\x80\xFD[\x83\x01\x805\x91P`\x01`\x01`@\x1B\x03\x82\x11\x15aN\xF9W`\0\x80\xFD[` \x01\x91P6\x81\x90\x03\x82\x13\x15aO\x0EW`\0\x80\xFD[\x92P\x92\x90PV[`\0`\x80\x826\x03\x12\x15aO'W`\0\x80\xFD[`@Q`\x80\x81\x01`\x01`\x01`@\x1B\x03\x82\x82\x10\x81\x83\x11\x17\x15aOJWaOJaA\xE5V[\x81`@R\x845\x83R` \x85\x015\x91P\x80\x82\x11\x15aOfW`\0\x80\xFD[aOr6\x83\x87\x01aJ%V[` \x84\x01R`@\x85\x015\x91P\x80\x82\x11\x15aO\x8BW`\0\x80\xFD[PaO\x986\x82\x86\x01aJ%V[`@\x83\x01RPaO\xAA``\x84\x01aD6V[``\x82\x01R\x92\x91PPV[`\0`\x01`\x01``\x1B\x03\x80\x83\x16\x81\x85\x16\x81\x83\x04\x81\x11\x82\x15\x15\x16\x15aO\xDBWaO\xDBaM2V[\x02\x94\x93PPPPV[`\0\x81`\0\x19\x04\x83\x11\x82\x15\x15\x16\x15aO\xFEWaO\xFEaM2V[P\x02\x90V[` \x80\x82R`R\x90\x82\x01R\x7FServiceManagerBase.onlyRegistryC`@\x82\x01R\x7Foordinator: caller is not the re``\x82\x01Rq3\xB4\xB9\xBA9<\x901\xB7\xB7\xB924\xB70\xBA7\xB9`q\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x80`\xA0\x1B\x03\x83\x16\x81R`@` \x82\x01R`\0\x82Q```@\x84\x01RaP\xA5`\xA0\x84\x01\x82aInV[\x90P` \x84\x01Q``\x84\x01R`@\x84\x01Q`\x80\x84\x01R\x80\x91PP\x93\x92PPPV[`\0\x825`\x9E\x19\x836\x03\x01\x81\x12aP\xDCW`\0\x80\xFD[\x91\x90\x91\x01\x92\x91PPV[\x81\x83R`\0` \x80\x85\x01\x94P\x82`\0[\x85\x81\x10\x15aG\xD4W\x815aQ\t\x81aA\x9AV[`\x01`\x01`\xA0\x1B\x03\x16\x87R\x81\x83\x015aQ!\x81aM{V[`\x01`\x01``\x1B\x03\x16\x87\x84\x01R`@\x96\x87\x01\x96\x91\x90\x91\x01\x90`\x01\x01aP\xF6V[` \x80\x82R\x81\x81\x01\x83\x90R`\0\x90`@\x80\x84\x01`\x05\x86\x90\x1B\x85\x01\x82\x01\x87\x85[\x88\x81\x10\x15aR@W\x87\x83\x03`?\x19\x01\x84R\x8156\x8B\x90\x03`\x9E\x19\x01\x81\x12aQ\x86W`\0\x80\xFD[\x8A\x01`\xA0\x8156\x83\x90\x03`\x1E\x19\x01\x81\x12aQ\x9FW`\0\x80\xFD[\x82\x01\x805`\x01`\x01`@\x1B\x03\x81\x11\x15aQ\xB7W`\0\x80\xFD[\x80`\x06\x1B6\x03\x84\x13\x15aQ\xC9W`\0\x80\xFD[\x82\x87RaQ\xDB\x83\x88\x01\x82\x8C\x85\x01aP\xE6V[\x92PPPaQ\xEA\x88\x83\x01aC\x87V[`\x01`\x01`\xA0\x1B\x03\x16\x88\x86\x01R\x81\x87\x015\x87\x86\x01R``aR\x0C\x81\x84\x01aD6V[c\xFF\xFF\xFF\xFF\x16\x90\x86\x01R`\x80aR#\x83\x82\x01aD6V[c\xFF\xFF\xFF\xFF\x16\x95\x01\x94\x90\x94RP\x92\x85\x01\x92\x90\x85\x01\x90`\x01\x01aQ`V[P\x90\x98\x97PPPPPPPPV[`\0a\xFF\xFF\x80\x83\x16\x81\x81\x14\x15aRfWaRfaM2V[`\x01\x01\x93\x92PPPV[`\0\x80\x835`\x1E\x19\x846\x03\x01\x81\x12aR\x87W`\0\x80\xFD[\x83\x01` \x81\x01\x92P5\x90P`\x01`\x01`@\x1B\x03\x81\x11\x15aR\xA6W`\0\x80\xFD[\x806\x03\x83\x13\x15aO\x0EW`\0\x80\xFD[\x81\x83R\x81\x81` \x85\x017P`\0\x82\x82\x01` \x90\x81\x01\x91\x90\x91R`\x1F\x90\x91\x01`\x1F\x19\x16\x90\x91\x01\x01\x90V[` \x81R\x815` \x82\x01R`\0aR\xF8` \x84\x01\x84aRpV[`\x80`@\x85\x01RaS\r`\xA0\x85\x01\x82\x84aR\xB5V[\x91PPaS\x1D`@\x85\x01\x85aRpV[\x84\x83\x03`\x1F\x19\x01``\x86\x01RaS4\x83\x82\x84aR\xB5V[\x92PPPc\xFF\xFF\xFF\xFFaSI``\x86\x01aD6V[\x16`\x80\x84\x01R\x80\x91PP\x92\x91PPV\xFE0dNr\xE11\xA0)\xB8PE\xB6\x81\x81X]\x97\x81j\x91hq\xCA\x8D< \x8C\x16\xD8|\xFDGEigenDAServiceManager.confirmBatBLSSignatureChecker.checkSignatu\xA2dipfsX\"\x12 \x08'RI\xEC\">8\x0E\xD1?K\x05\x96\xED\xED\x1A\xE59X\xC6\xCFh\x852k\xD2[I:\x84ZdsolcC\0\x08\x0C\x003", + ); + /**```solidity + struct BatchHeader { bytes32 blobHeadersRoot; bytes quorumNumbers; bytes signedStakeForQuorums; uint32 referenceBlockNumber; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct BatchHeader { + pub blobHeadersRoot: alloy::sol_types::private::FixedBytes<32>, + pub quorumNumbers: alloy::sol_types::private::Bytes, + pub signedStakeForQuorums: alloy::sol_types::private::Bytes, + pub referenceBlockNumber: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::Bytes, + alloy::sol_types::sol_data::Bytes, + alloy::sol_types::sol_data::Uint<32>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::FixedBytes<32>, + alloy::sol_types::private::Bytes, + alloy::sol_types::private::Bytes, + u32, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: BatchHeader) -> Self { + ( + value.blobHeadersRoot, + value.quorumNumbers, + value.signedStakeForQuorums, + value.referenceBlockNumber, + ) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for BatchHeader { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + blobHeadersRoot: tuple.0, + quorumNumbers: tuple.1, + signedStakeForQuorums: tuple.2, + referenceBlockNumber: tuple.3, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for BatchHeader { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for BatchHeader { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize(&self.blobHeadersRoot), + ::tokenize( + &self.quorumNumbers, + ), + ::tokenize( + &self.signedStakeForQuorums, + ), + as alloy_sol_types::SolType>::tokenize(&self.referenceBlockNumber), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for BatchHeader { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for BatchHeader { + const NAME: &'static str = "BatchHeader"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed( + "BatchHeader(bytes32 blobHeadersRoot,bytes quorumNumbers,bytes signedStakeForQuorums,uint32 referenceBlockNumber)", + ) + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + alloy_sol_types::private::Vec::new() + } + #[inline] + fn eip712_encode_type() -> alloy_sol_types::private::Cow<'static, str> { + ::eip712_root_type() + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + as alloy_sol_types::SolType>::eip712_data_word( + &self.blobHeadersRoot, + ) + .0, + ::eip712_data_word( + &self.quorumNumbers, + ) + .0, + ::eip712_data_word( + &self.signedStakeForQuorums, + ) + .0, + as alloy_sol_types::SolType>::eip712_data_word( + &self.referenceBlockNumber, + ) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for BatchHeader { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.blobHeadersRoot, + ) + + ::topic_preimage_length( + &rust.quorumNumbers, + ) + + ::topic_preimage_length( + &rust.signedStakeForQuorums, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.referenceBlockNumber, + ) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.blobHeadersRoot, + out, + ); + ::encode_topic_preimage( + &rust.quorumNumbers, + out, + ); + ::encode_topic_preimage( + &rust.signedStakeForQuorums, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.referenceBlockNumber, + out, + ); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**```solidity + struct G1Point { uint256 X; uint256 Y; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct G1Point { + pub X: alloy::sol_types::private::U256, + pub Y: alloy::sol_types::private::U256, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Uint<256>, + alloy::sol_types::sol_data::Uint<256>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::U256, + alloy::sol_types::private::U256, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: G1Point) -> Self { + (value.X, value.Y) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for G1Point { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + X: tuple.0, + Y: tuple.1, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for G1Point { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for G1Point { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.X, + ), + as alloy_sol_types::SolType>::tokenize( + &self.Y, + ), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for G1Point { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for G1Point { + const NAME: &'static str = "G1Point"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed("G1Point(uint256 X,uint256 Y)") + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + alloy_sol_types::private::Vec::new() + } + #[inline] + fn eip712_encode_type() -> alloy_sol_types::private::Cow<'static, str> { + ::eip712_root_type() + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + as alloy_sol_types::SolType>::eip712_data_word(&self.X) + .0, + as alloy_sol_types::SolType>::eip712_data_word(&self.Y) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for G1Point { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + as alloy_sol_types::EventTopic>::topic_preimage_length(&rust.X) + + as alloy_sol_types::EventTopic>::topic_preimage_length(&rust.Y) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + as alloy_sol_types::EventTopic>::encode_topic_preimage(&rust.X, out); + as alloy_sol_types::EventTopic>::encode_topic_preimage(&rust.Y, out); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**```solidity + struct G2Point { uint256[2] X; uint256[2] Y; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct G2Point { + pub X: [alloy::sol_types::private::U256; 2usize], + pub Y: [alloy::sol_types::private::U256; 2usize], + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::FixedArray, 2usize>, + alloy::sol_types::sol_data::FixedArray, 2usize>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + [alloy::sol_types::private::U256; 2usize], + [alloy::sol_types::private::U256; 2usize], + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: G2Point) -> Self { + (value.X, value.Y) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for G2Point { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + X: tuple.0, + Y: tuple.1, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for G2Point { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for G2Point { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + , + 2usize, + > as alloy_sol_types::SolType>::tokenize(&self.X), + , + 2usize, + > as alloy_sol_types::SolType>::tokenize(&self.Y), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for G2Point { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for G2Point { + const NAME: &'static str = "G2Point"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed("G2Point(uint256[2] X,uint256[2] Y)") + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + alloy_sol_types::private::Vec::new() + } + #[inline] + fn eip712_encode_type() -> alloy_sol_types::private::Cow<'static, str> { + ::eip712_root_type() + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + , + 2usize, + > as alloy_sol_types::SolType>::eip712_data_word(&self.X) + .0, + , + 2usize, + > as alloy_sol_types::SolType>::eip712_data_word(&self.Y) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for G2Point { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + , + 2usize, + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.X + ) + + , + 2usize, + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.Y + ) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + , + 2usize, + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.X, out + ); + , + 2usize, + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.Y, out + ); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**```solidity + struct NonSignerStakesAndSignature { uint32[] nonSignerQuorumBitmapIndices; G1Point[] nonSignerPubkeys; G1Point[] quorumApks; G2Point apkG2; G1Point sigma; uint32[] quorumApkIndices; uint32[] totalStakeIndices; uint32[][] nonSignerStakeIndices; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct NonSignerStakesAndSignature { + pub nonSignerQuorumBitmapIndices: alloy::sol_types::private::Vec, + pub nonSignerPubkeys: + alloy::sol_types::private::Vec<::RustType>, + pub quorumApks: + alloy::sol_types::private::Vec<::RustType>, + pub apkG2: ::RustType, + pub sigma: ::RustType, + pub quorumApkIndices: alloy::sol_types::private::Vec, + pub totalStakeIndices: alloy::sol_types::private::Vec, + pub nonSignerStakeIndices: + alloy::sol_types::private::Vec>, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Array>, + alloy::sol_types::sol_data::Array, + alloy::sol_types::sol_data::Array, + G2Point, + G1Point, + alloy::sol_types::sol_data::Array>, + alloy::sol_types::sol_data::Array>, + alloy::sol_types::sol_data::Array< + alloy::sol_types::sol_data::Array>, + >, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Vec, + alloy::sol_types::private::Vec<::RustType>, + alloy::sol_types::private::Vec<::RustType>, + ::RustType, + ::RustType, + alloy::sol_types::private::Vec, + alloy::sol_types::private::Vec, + alloy::sol_types::private::Vec>, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: NonSignerStakesAndSignature) -> Self { + ( + value.nonSignerQuorumBitmapIndices, + value.nonSignerPubkeys, + value.quorumApks, + value.apkG2, + value.sigma, + value.quorumApkIndices, + value.totalStakeIndices, + value.nonSignerStakeIndices, + ) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for NonSignerStakesAndSignature { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + nonSignerQuorumBitmapIndices: tuple.0, + nonSignerPubkeys: tuple.1, + quorumApks: tuple.2, + apkG2: tuple.3, + sigma: tuple.4, + quorumApkIndices: tuple.5, + totalStakeIndices: tuple.6, + nonSignerStakeIndices: tuple.7, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for NonSignerStakesAndSignature { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for NonSignerStakesAndSignature { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + , + > as alloy_sol_types::SolType>::tokenize( + &self.nonSignerQuorumBitmapIndices, + ), + as alloy_sol_types::SolType>::tokenize(&self.nonSignerPubkeys), + as alloy_sol_types::SolType>::tokenize(&self.quorumApks), + ::tokenize(&self.apkG2), + ::tokenize(&self.sigma), + , + > as alloy_sol_types::SolType>::tokenize(&self.quorumApkIndices), + , + > as alloy_sol_types::SolType>::tokenize(&self.totalStakeIndices), + , + >, + > as alloy_sol_types::SolType>::tokenize(&self.nonSignerStakeIndices), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for NonSignerStakesAndSignature { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for NonSignerStakesAndSignature { + const NAME: &'static str = "NonSignerStakesAndSignature"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed( + "NonSignerStakesAndSignature(uint32[] nonSignerQuorumBitmapIndices,G1Point[] nonSignerPubkeys,G1Point[] quorumApks,G2Point apkG2,G1Point sigma,uint32[] quorumApkIndices,uint32[] totalStakeIndices,uint32[][] nonSignerStakeIndices)", + ) + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + let mut components = alloy_sol_types::private::Vec::with_capacity(4); + components.push(::eip712_root_type()); + components.extend(::eip712_components()); + components.push(::eip712_root_type()); + components.extend(::eip712_components()); + components.push(::eip712_root_type()); + components.extend(::eip712_components()); + components.push(::eip712_root_type()); + components.extend(::eip712_components()); + components + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + , + > as alloy_sol_types::SolType>::eip712_data_word( + &self.nonSignerQuorumBitmapIndices, + ) + .0, + as alloy_sol_types::SolType>::eip712_data_word( + &self.nonSignerPubkeys, + ) + .0, + as alloy_sol_types::SolType>::eip712_data_word(&self.quorumApks) + .0, + ::eip712_data_word(&self.apkG2) + .0, + ::eip712_data_word(&self.sigma) + .0, + , + > as alloy_sol_types::SolType>::eip712_data_word( + &self.quorumApkIndices, + ) + .0, + , + > as alloy_sol_types::SolType>::eip712_data_word( + &self.totalStakeIndices, + ) + .0, + , + >, + > as alloy_sol_types::SolType>::eip712_data_word( + &self.nonSignerStakeIndices, + ) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for NonSignerStakesAndSignature { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + , + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.nonSignerQuorumBitmapIndices, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.nonSignerPubkeys, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.quorumApks, + ) + + ::topic_preimage_length( + &rust.apkG2, + ) + + ::topic_preimage_length( + &rust.sigma, + ) + + , + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.quorumApkIndices, + ) + + , + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.totalStakeIndices, + ) + + , + >, + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.nonSignerStakeIndices, + ) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + , + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.nonSignerQuorumBitmapIndices, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.nonSignerPubkeys, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.quorumApks, + out, + ); + ::encode_topic_preimage(&rust.apkG2, out); + ::encode_topic_preimage(&rust.sigma, out); + , + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.quorumApkIndices, + out, + ); + , + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.totalStakeIndices, + out, + ); + >, + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.nonSignerStakeIndices, + out, + ); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**```solidity + struct QuorumStakeTotals { uint96[] signedStakeForQuorum; uint96[] totalStakeForQuorum; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct QuorumStakeTotals { + pub signedStakeForQuorum: alloy::sol_types::private::Vec< + as alloy::sol_types::SolType>::RustType, + >, + pub totalStakeForQuorum: alloy::sol_types::private::Vec< + as alloy::sol_types::SolType>::RustType, + >, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Array>, + alloy::sol_types::sol_data::Array>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Vec< + as alloy::sol_types::SolType>::RustType, + >, + alloy::sol_types::private::Vec< + as alloy::sol_types::SolType>::RustType, + >, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: QuorumStakeTotals) -> Self { + (value.signedStakeForQuorum, value.totalStakeForQuorum) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for QuorumStakeTotals { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + signedStakeForQuorum: tuple.0, + totalStakeForQuorum: tuple.1, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for QuorumStakeTotals { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for QuorumStakeTotals { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + , + > as alloy_sol_types::SolType>::tokenize(&self.signedStakeForQuorum), + , + > as alloy_sol_types::SolType>::tokenize(&self.totalStakeForQuorum), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for QuorumStakeTotals { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for QuorumStakeTotals { + const NAME: &'static str = "QuorumStakeTotals"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed( + "QuorumStakeTotals(uint96[] signedStakeForQuorum,uint96[] totalStakeForQuorum)", + ) + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + alloy_sol_types::private::Vec::new() + } + #[inline] + fn eip712_encode_type() -> alloy_sol_types::private::Cow<'static, str> { + ::eip712_root_type() + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + , + > as alloy_sol_types::SolType>::eip712_data_word( + &self.signedStakeForQuorum, + ) + .0, + , + > as alloy_sol_types::SolType>::eip712_data_word( + &self.totalStakeForQuorum, + ) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for QuorumStakeTotals { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + , + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.signedStakeForQuorum, + ) + + , + > as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.totalStakeForQuorum, + ) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + , + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.signedStakeForQuorum, + out, + ); + , + > as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.totalStakeForQuorum, + out, + ); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**```solidity + struct RewardsSubmission { StrategyAndMultiplier[] strategiesAndMultipliers; address token; uint256 amount; uint32 startTimestamp; uint32 duration; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct RewardsSubmission { + pub strategiesAndMultipliers: alloy::sol_types::private::Vec< + ::RustType, + >, + pub token: alloy::sol_types::private::Address, + pub amount: alloy::sol_types::private::U256, + pub startTimestamp: u32, + pub duration: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Array, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Uint<256>, + alloy::sol_types::sol_data::Uint<32>, + alloy::sol_types::sol_data::Uint<32>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Vec< + ::RustType, + >, + alloy::sol_types::private::Address, + alloy::sol_types::private::U256, + u32, + u32, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: RewardsSubmission) -> Self { + ( + value.strategiesAndMultipliers, + value.token, + value.amount, + value.startTimestamp, + value.duration, + ) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for RewardsSubmission { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + strategiesAndMultipliers: tuple.0, + token: tuple.1, + amount: tuple.2, + startTimestamp: tuple.3, + duration: tuple.4, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for RewardsSubmission { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for RewardsSubmission { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.strategiesAndMultipliers, + ), + ::tokenize( + &self.token, + ), + as alloy_sol_types::SolType>::tokenize(&self.amount), + as alloy_sol_types::SolType>::tokenize(&self.startTimestamp), + as alloy_sol_types::SolType>::tokenize(&self.duration), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for RewardsSubmission { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for RewardsSubmission { + const NAME: &'static str = "RewardsSubmission"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed( + "RewardsSubmission(StrategyAndMultiplier[] strategiesAndMultipliers,address token,uint256 amount,uint32 startTimestamp,uint32 duration)", + ) + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + let mut components = alloy_sol_types::private::Vec::with_capacity(1); + components.push( + ::eip712_root_type(), + ); + components.extend( + ::eip712_components(), + ); + components + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + as alloy_sol_types::SolType>::eip712_data_word( + &self.strategiesAndMultipliers, + ) + .0, + ::eip712_data_word( + &self.token, + ) + .0, + as alloy_sol_types::SolType>::eip712_data_word(&self.amount) + .0, + as alloy_sol_types::SolType>::eip712_data_word( + &self.startTimestamp, + ) + .0, + as alloy_sol_types::SolType>::eip712_data_word(&self.duration) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for RewardsSubmission { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.strategiesAndMultipliers, + ) + + ::topic_preimage_length( + &rust.token, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.amount, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.startTimestamp, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.duration, + ) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.strategiesAndMultipliers, + out, + ); + ::encode_topic_preimage( + &rust.token, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.amount, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.startTimestamp, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.duration, + out, + ); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**```solidity + struct SignatureWithSaltAndExpiry { bytes signature; bytes32 salt; uint256 expiry; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct SignatureWithSaltAndExpiry { + pub signature: alloy::sol_types::private::Bytes, + pub salt: alloy::sol_types::private::FixedBytes<32>, + pub expiry: alloy::sol_types::private::U256, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Bytes, + alloy::sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::Uint<256>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Bytes, + alloy::sol_types::private::FixedBytes<32>, + alloy::sol_types::private::U256, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: SignatureWithSaltAndExpiry) -> Self { + (value.signature, value.salt, value.expiry) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for SignatureWithSaltAndExpiry { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + signature: tuple.0, + salt: tuple.1, + expiry: tuple.2, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for SignatureWithSaltAndExpiry { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for SignatureWithSaltAndExpiry { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + ::tokenize( + &self.signature, + ), + as alloy_sol_types::SolType>::tokenize(&self.salt), + as alloy_sol_types::SolType>::tokenize(&self.expiry), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for SignatureWithSaltAndExpiry { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for SignatureWithSaltAndExpiry { + const NAME: &'static str = "SignatureWithSaltAndExpiry"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed( + "SignatureWithSaltAndExpiry(bytes signature,bytes32 salt,uint256 expiry)", + ) + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + alloy_sol_types::private::Vec::new() + } + #[inline] + fn eip712_encode_type() -> alloy_sol_types::private::Cow<'static, str> { + ::eip712_root_type() + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + ::eip712_data_word( + &self.signature, + ) + .0, + as alloy_sol_types::SolType>::eip712_data_word(&self.salt) + .0, + as alloy_sol_types::SolType>::eip712_data_word(&self.expiry) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for SignatureWithSaltAndExpiry { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + ::topic_preimage_length( + &rust.signature, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length(&rust.salt) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.expiry, + ) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + ::encode_topic_preimage( + &rust.signature, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.salt, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.expiry, + out, + ); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**```solidity + struct StrategyAndMultiplier { address strategy; uint96 multiplier; } + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct StrategyAndMultiplier { + pub strategy: alloy::sol_types::private::Address, + pub multiplier: + as alloy::sol_types::SolType>::RustType, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Uint<96>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Address, + as alloy::sol_types::SolType>::RustType, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: StrategyAndMultiplier) -> Self { + (value.strategy, value.multiplier) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for StrategyAndMultiplier { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + strategy: tuple.0, + multiplier: tuple.1, + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolValue for StrategyAndMultiplier { + type SolType = Self; + } + #[automatically_derived] + impl alloy_sol_types::private::SolTypeValue for StrategyAndMultiplier { + #[inline] + fn stv_to_tokens(&self) -> ::Token<'_> { + ( + ::tokenize( + &self.strategy, + ), + as alloy_sol_types::SolType>::tokenize( + &self.multiplier, + ), + ) + } + #[inline] + fn stv_abi_encoded_size(&self) -> usize { + if let Some(size) = ::ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encoded_size(&tuple) + } + #[inline] + fn stv_eip712_data_word(&self) -> alloy_sol_types::Word { + ::eip712_hash_struct(self) + } + #[inline] + fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec) { + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_encode_packed_to( + &tuple, out, + ) + } + #[inline] + fn stv_abi_packed_encoded_size(&self) -> usize { + if let Some(size) = ::PACKED_ENCODED_SIZE { + return size; + } + let tuple = + as ::core::convert::From>::from(self.clone()); + as alloy_sol_types::SolType>::abi_packed_encoded_size( + &tuple, + ) + } + } + #[automatically_derived] + impl alloy_sol_types::SolType for StrategyAndMultiplier { + type RustType = Self; + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SOL_NAME: &'static str = ::NAME; + const ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::ENCODED_SIZE; + const PACKED_ENCODED_SIZE: Option = + as alloy_sol_types::SolType>::PACKED_ENCODED_SIZE; + #[inline] + fn valid_token(token: &Self::Token<'_>) -> bool { + as alloy_sol_types::SolType>::valid_token(token) + } + #[inline] + fn detokenize(token: Self::Token<'_>) -> Self::RustType { + let tuple = as alloy_sol_types::SolType>::detokenize(token); + >>::from(tuple) + } + } + #[automatically_derived] + impl alloy_sol_types::SolStruct for StrategyAndMultiplier { + const NAME: &'static str = "StrategyAndMultiplier"; + #[inline] + fn eip712_root_type() -> alloy_sol_types::private::Cow<'static, str> { + alloy_sol_types::private::Cow::Borrowed( + "StrategyAndMultiplier(address strategy,uint96 multiplier)", + ) + } + #[inline] + fn eip712_components( + ) -> alloy_sol_types::private::Vec> + { + alloy_sol_types::private::Vec::new() + } + #[inline] + fn eip712_encode_type() -> alloy_sol_types::private::Cow<'static, str> { + ::eip712_root_type() + } + #[inline] + fn eip712_encode_data(&self) -> alloy_sol_types::private::Vec { + [ + ::eip712_data_word( + &self.strategy, + ) + .0, + as alloy_sol_types::SolType>::eip712_data_word(&self.multiplier) + .0, + ] + .concat() + } + } + #[automatically_derived] + impl alloy_sol_types::EventTopic for StrategyAndMultiplier { + #[inline] + fn topic_preimage_length(rust: &Self::RustType) -> usize { + 0usize + + ::topic_preimage_length( + &rust.strategy, + ) + + as alloy_sol_types::EventTopic>::topic_preimage_length( + &rust.multiplier, + ) + } + #[inline] + fn encode_topic_preimage( + rust: &Self::RustType, + out: &mut alloy_sol_types::private::Vec, + ) { + out.reserve(::topic_preimage_length(rust)); + ::encode_topic_preimage( + &rust.strategy, + out, + ); + as alloy_sol_types::EventTopic>::encode_topic_preimage( + &rust.multiplier, + out, + ); + } + #[inline] + fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken { + let mut out = alloy_sol_types::private::Vec::new(); + ::encode_topic_preimage(rust, &mut out); + alloy_sol_types::abi::token::WordToken(alloy_sol_types::private::keccak256(out)) + } + } + }; + /**Event with signature `BatchConfirmed(bytes32,uint32)` and selector `0xc75557c4ad49697e231449688be13ef11cb6be8ed0d18819d8dde074a5a16f8a`. + ```solidity + event BatchConfirmed(bytes32 indexed batchHeaderHash, uint32 batchId); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct BatchConfirmed { + #[allow(missing_docs)] + pub batchHeaderHash: alloy::sol_types::private::FixedBytes<32>, + #[allow(missing_docs)] + pub batchId: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for BatchConfirmed { + type DataTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = ( + alloy_sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::FixedBytes<32>, + ); + const SIGNATURE: &'static str = "BatchConfirmed(bytes32,uint32)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 199u8, 85u8, 87u8, 196u8, 173u8, 73u8, 105u8, 126u8, 35u8, 20u8, 73u8, 104u8, + 139u8, 225u8, 62u8, 241u8, 28u8, 182u8, 190u8, 142u8, 208u8, 209u8, 136u8, + 25u8, 216u8, 221u8, 224u8, 116u8, 165u8, 161u8, 111u8, 138u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { + batchHeaderHash: topics.1, + batchId: data.0, + } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.batchId, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(), self.batchHeaderHash.clone()) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + out[1usize] = as alloy_sol_types::EventTopic>::encode_topic(&self.batchHeaderHash); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for BatchConfirmed { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&BatchConfirmed> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &BatchConfirmed) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `BatchConfirmerStatusChanged(address,bool)` and selector `0x5c3265f5fb462ef4930fe47beaa183647c97f19ba545b761f41bc8cd4621d414`. + ```solidity + event BatchConfirmerStatusChanged(address batchConfirmer, bool status); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct BatchConfirmerStatusChanged { + #[allow(missing_docs)] + pub batchConfirmer: alloy::sol_types::private::Address, + #[allow(missing_docs)] + pub status: bool, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for BatchConfirmerStatusChanged { + type DataTuple<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Bool, + ); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = (alloy_sol_types::sol_data::FixedBytes<32>,); + const SIGNATURE: &'static str = "BatchConfirmerStatusChanged(address,bool)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 92u8, 50u8, 101u8, 245u8, 251u8, 70u8, 46u8, 244u8, 147u8, 15u8, 228u8, 123u8, + 234u8, 161u8, 131u8, 100u8, 124u8, 151u8, 241u8, 155u8, 165u8, 69u8, 183u8, + 97u8, 244u8, 27u8, 200u8, 205u8, 70u8, 33u8, 212u8, 20u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { + batchConfirmer: data.0, + status: data.1, + } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + ::tokenize( + &self.batchConfirmer, + ), + ::tokenize( + &self.status, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(),) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for BatchConfirmerStatusChanged { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&BatchConfirmerStatusChanged> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &BatchConfirmerStatusChanged) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `Initialized(uint8)` and selector `0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498`. + ```solidity + event Initialized(uint8 version); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct Initialized { + #[allow(missing_docs)] + pub version: u8, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for Initialized { + type DataTuple<'a> = (alloy::sol_types::sol_data::Uint<8>,); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = (alloy_sol_types::sol_data::FixedBytes<32>,); + const SIGNATURE: &'static str = "Initialized(uint8)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 127u8, 38u8, 184u8, 63u8, 249u8, 110u8, 31u8, 43u8, 106u8, 104u8, 47u8, 19u8, + 56u8, 82u8, 246u8, 121u8, 138u8, 9u8, 196u8, 101u8, 218u8, 149u8, 146u8, 20u8, + 96u8, 206u8, 251u8, 56u8, 71u8, 64u8, 36u8, 152u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { version: data.0 } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.version, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(),) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for Initialized { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&Initialized> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &Initialized) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `OwnershipTransferred(address,address)` and selector `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0`. + ```solidity + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct OwnershipTransferred { + #[allow(missing_docs)] + pub previousOwner: alloy::sol_types::private::Address, + #[allow(missing_docs)] + pub newOwner: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for OwnershipTransferred { + type DataTuple<'a> = (); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = ( + alloy_sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + ); + const SIGNATURE: &'static str = "OwnershipTransferred(address,address)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 139u8, 224u8, 7u8, 156u8, 83u8, 22u8, 89u8, 20u8, 19u8, 68u8, 205u8, 31u8, + 208u8, 164u8, 242u8, 132u8, 25u8, 73u8, 127u8, 151u8, 34u8, 163u8, 218u8, + 175u8, 227u8, 180u8, 24u8, 111u8, 107u8, 100u8, 87u8, 224u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { + previousOwner: topics.1, + newOwner: topics.2, + } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + () + } + #[inline] + fn topics(&self) -> ::RustType { + ( + Self::SIGNATURE_HASH.into(), + self.previousOwner.clone(), + self.newOwner.clone(), + ) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + out[1usize] = ::encode_topic( + &self.previousOwner, + ); + out[2usize] = ::encode_topic( + &self.newOwner, + ); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for OwnershipTransferred { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&OwnershipTransferred> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &OwnershipTransferred) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `Paused(address,uint256)` and selector `0xab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d`. + ```solidity + event Paused(address indexed account, uint256 newPausedStatus); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct Paused { + #[allow(missing_docs)] + pub account: alloy::sol_types::private::Address, + #[allow(missing_docs)] + pub newPausedStatus: alloy::sol_types::private::U256, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for Paused { + type DataTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = ( + alloy_sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::Address, + ); + const SIGNATURE: &'static str = "Paused(address,uint256)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 171u8, 64u8, 163u8, 116u8, 188u8, 81u8, 222u8, 55u8, 34u8, 0u8, 168u8, 188u8, + 152u8, 26u8, 248u8, 201u8, 236u8, 220u8, 8u8, 223u8, 218u8, 239u8, 11u8, 182u8, + 224u8, 159u8, 136u8, 243u8, 198u8, 22u8, 239u8, 61u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { + account: topics.1, + newPausedStatus: data.0, + } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.newPausedStatus, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(), self.account.clone()) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + out[1usize] = ::encode_topic( + &self.account, + ); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for Paused { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&Paused> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &Paused) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `PauserRegistrySet(address,address)` and selector `0x6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6`. + ```solidity + event PauserRegistrySet(address pauserRegistry, address newPauserRegistry); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct PauserRegistrySet { + #[allow(missing_docs)] + pub pauserRegistry: alloy::sol_types::private::Address, + #[allow(missing_docs)] + pub newPauserRegistry: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for PauserRegistrySet { + type DataTuple<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + ); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = (alloy_sol_types::sol_data::FixedBytes<32>,); + const SIGNATURE: &'static str = "PauserRegistrySet(address,address)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 110u8, 159u8, 205u8, 83u8, 152u8, 150u8, 252u8, 166u8, 14u8, 139u8, 15u8, 1u8, + 221u8, 88u8, 2u8, 51u8, 228u8, 138u8, 107u8, 15u8, 125u8, 240u8, 19u8, 184u8, + 155u8, 167u8, 245u8, 101u8, 134u8, 154u8, 205u8, 182u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { + pauserRegistry: data.0, + newPauserRegistry: data.1, + } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + ::tokenize( + &self.pauserRegistry, + ), + ::tokenize( + &self.newPauserRegistry, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(),) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for PauserRegistrySet { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&PauserRegistrySet> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &PauserRegistrySet) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `RewardsInitiatorUpdated(address,address)` and selector `0xe11cddf1816a43318ca175bbc52cd0185436e9cbead7c83acc54a73e461717e3`. + ```solidity + event RewardsInitiatorUpdated(address prevRewardsInitiator, address newRewardsInitiator); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct RewardsInitiatorUpdated { + #[allow(missing_docs)] + pub prevRewardsInitiator: alloy::sol_types::private::Address, + #[allow(missing_docs)] + pub newRewardsInitiator: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for RewardsInitiatorUpdated { + type DataTuple<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + ); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = (alloy_sol_types::sol_data::FixedBytes<32>,); + const SIGNATURE: &'static str = "RewardsInitiatorUpdated(address,address)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 225u8, 28u8, 221u8, 241u8, 129u8, 106u8, 67u8, 49u8, 140u8, 161u8, 117u8, + 187u8, 197u8, 44u8, 208u8, 24u8, 84u8, 54u8, 233u8, 203u8, 234u8, 215u8, 200u8, + 58u8, 204u8, 84u8, 167u8, 62u8, 70u8, 23u8, 23u8, 227u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { + prevRewardsInitiator: data.0, + newRewardsInitiator: data.1, + } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + ::tokenize( + &self.prevRewardsInitiator, + ), + ::tokenize( + &self.newRewardsInitiator, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(),) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for RewardsInitiatorUpdated { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&RewardsInitiatorUpdated> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &RewardsInitiatorUpdated) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `StaleStakesForbiddenUpdate(bool)` and selector `0x40e4ed880a29e0f6ddce307457fb75cddf4feef7d3ecb0301bfdf4976a0e2dfc`. + ```solidity + event StaleStakesForbiddenUpdate(bool value); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct StaleStakesForbiddenUpdate { + #[allow(missing_docs)] + pub value: bool, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for StaleStakesForbiddenUpdate { + type DataTuple<'a> = (alloy::sol_types::sol_data::Bool,); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = (alloy_sol_types::sol_data::FixedBytes<32>,); + const SIGNATURE: &'static str = "StaleStakesForbiddenUpdate(bool)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 64u8, 228u8, 237u8, 136u8, 10u8, 41u8, 224u8, 246u8, 221u8, 206u8, 48u8, 116u8, + 87u8, 251u8, 117u8, 205u8, 223u8, 79u8, 238u8, 247u8, 211u8, 236u8, 176u8, + 48u8, 27u8, 253u8, 244u8, 151u8, 106u8, 14u8, 45u8, 252u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { value: data.0 } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + ::tokenize( + &self.value, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(),) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for StaleStakesForbiddenUpdate { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&StaleStakesForbiddenUpdate> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &StaleStakesForbiddenUpdate) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Event with signature `Unpaused(address,uint256)` and selector `0x3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c`. + ```solidity + event Unpaused(address indexed account, uint256 newPausedStatus); + ```*/ + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + #[derive(Clone)] + pub struct Unpaused { + #[allow(missing_docs)] + pub account: alloy::sol_types::private::Address, + #[allow(missing_docs)] + pub newPausedStatus: alloy::sol_types::private::U256, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + #[automatically_derived] + impl alloy_sol_types::SolEvent for Unpaused { + type DataTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + type TopicList = ( + alloy_sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::Address, + ); + const SIGNATURE: &'static str = "Unpaused(address,uint256)"; + const SIGNATURE_HASH: alloy_sol_types::private::B256 = + alloy_sol_types::private::B256::new([ + 53u8, 130u8, 209u8, 130u8, 142u8, 38u8, 191u8, 86u8, 189u8, 128u8, 21u8, 2u8, + 188u8, 2u8, 26u8, 192u8, 188u8, 138u8, 251u8, 87u8, 200u8, 38u8, 228u8, 152u8, + 107u8, 69u8, 89u8, 60u8, 143u8, 173u8, 56u8, 156u8, + ]); + const ANONYMOUS: bool = false; + #[allow(unused_variables)] + #[inline] + fn new( + topics: ::RustType, + data: as alloy_sol_types::SolType>::RustType, + ) -> Self { + Self { + account: topics.1, + newPausedStatus: data.0, + } + } + #[inline] + fn tokenize_body(&self) -> Self::DataToken<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.newPausedStatus, + ), + ) + } + #[inline] + fn topics(&self) -> ::RustType { + (Self::SIGNATURE_HASH.into(), self.account.clone()) + } + #[inline] + fn encode_topics_raw( + &self, + out: &mut [alloy_sol_types::abi::token::WordToken], + ) -> alloy_sol_types::Result<()> { + if out.len() < ::COUNT { + return Err(alloy_sol_types::Error::Overrun); + } + out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); + out[1usize] = ::encode_topic( + &self.account, + ); + Ok(()) + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for Unpaused { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + From::from(self) + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + From::from(&self) + } + } + #[automatically_derived] + impl From<&Unpaused> for alloy_sol_types::private::LogData { + #[inline] + fn from(this: &Unpaused) -> alloy_sol_types::private::LogData { + alloy_sol_types::SolEvent::encode_log_data(this) + } + } + }; + /**Constructor`. + ```solidity + constructor(address __avsDirectory, address __rewardsCoordinator, address __registryCoordinator, address __stakeRegistry); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct constructorCall { + pub __avsDirectory: alloy::sol_types::private::Address, + pub __rewardsCoordinator: alloy::sol_types::private::Address, + pub __registryCoordinator: alloy::sol_types::private::Address, + pub __stakeRegistry: alloy::sol_types::private::Address, + } + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Address, + alloy::sol_types::private::Address, + alloy::sol_types::private::Address, + alloy::sol_types::private::Address, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: constructorCall) -> Self { + ( + value.__avsDirectory, + value.__rewardsCoordinator, + value.__registryCoordinator, + value.__stakeRegistry, + ) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for constructorCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + __avsDirectory: tuple.0, + __rewardsCoordinator: tuple.1, + __registryCoordinator: tuple.2, + __stakeRegistry: tuple.3, + } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolConstructor for constructorCall { + type Parameters<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Address, + ); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.__avsDirectory, + ), + ::tokenize( + &self.__rewardsCoordinator, + ), + ::tokenize( + &self.__registryCoordinator, + ), + ::tokenize( + &self.__stakeRegistry, + ), + ) + } + } + }; + /**Function with signature `BLOCK_STALE_MEASURE()` and selector `0x5e8b3f2d`. + ```solidity + function BLOCK_STALE_MEASURE() external view returns (uint32); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct BLOCK_STALE_MEASURECall {} + ///Container type for the return parameters of the [`BLOCK_STALE_MEASURE()`](BLOCK_STALE_MEASURECall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct BLOCK_STALE_MEASUREReturn { + pub _0: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: BLOCK_STALE_MEASURECall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for BLOCK_STALE_MEASURECall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u32,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: BLOCK_STALE_MEASUREReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for BLOCK_STALE_MEASUREReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for BLOCK_STALE_MEASURECall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = BLOCK_STALE_MEASUREReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "BLOCK_STALE_MEASURE()"; + const SELECTOR: [u8; 4] = [94u8, 139u8, 63u8, 45u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `STORE_DURATION_BLOCKS()` and selector `0x5e033476`. + ```solidity + function STORE_DURATION_BLOCKS() external view returns (uint32); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct STORE_DURATION_BLOCKSCall {} + ///Container type for the return parameters of the [`STORE_DURATION_BLOCKS()`](STORE_DURATION_BLOCKSCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct STORE_DURATION_BLOCKSReturn { + pub _0: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: STORE_DURATION_BLOCKSCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for STORE_DURATION_BLOCKSCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u32,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: STORE_DURATION_BLOCKSReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for STORE_DURATION_BLOCKSReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for STORE_DURATION_BLOCKSCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = STORE_DURATION_BLOCKSReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "STORE_DURATION_BLOCKS()"; + const SELECTOR: [u8; 4] = [94u8, 3u8, 52u8, 118u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `THRESHOLD_DENOMINATOR()` and selector `0xef024458`. + ```solidity + function THRESHOLD_DENOMINATOR() external view returns (uint256); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct THRESHOLD_DENOMINATORCall {} + ///Container type for the return parameters of the [`THRESHOLD_DENOMINATOR()`](THRESHOLD_DENOMINATORCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct THRESHOLD_DENOMINATORReturn { + pub _0: alloy::sol_types::private::U256, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: THRESHOLD_DENOMINATORCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for THRESHOLD_DENOMINATORCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::U256,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: THRESHOLD_DENOMINATORReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for THRESHOLD_DENOMINATORReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for THRESHOLD_DENOMINATORCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = THRESHOLD_DENOMINATORReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "THRESHOLD_DENOMINATOR()"; + const SELECTOR: [u8; 4] = [239u8, 2u8, 68u8, 88u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `avsDirectory()` and selector `0x6b3aa72e`. + ```solidity + function avsDirectory() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct avsDirectoryCall {} + ///Container type for the return parameters of the [`avsDirectory()`](avsDirectoryCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct avsDirectoryReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: avsDirectoryCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for avsDirectoryCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: avsDirectoryReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for avsDirectoryReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for avsDirectoryCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = avsDirectoryReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "avsDirectory()"; + const SELECTOR: [u8; 4] = [107u8, 58u8, 167u8, 46u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `batchId()` and selector `0x4972134a`. + ```solidity + function batchId() external view returns (uint32); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct batchIdCall {} + ///Container type for the return parameters of the [`batchId()`](batchIdCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct batchIdReturn { + pub _0: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: batchIdCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for batchIdCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u32,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: batchIdReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for batchIdReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for batchIdCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = batchIdReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "batchId()"; + const SELECTOR: [u8; 4] = [73u8, 114u8, 19u8, 74u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `batchIdToBatchMetadataHash(uint32)` and selector `0xeccbbfc9`. + ```solidity + function batchIdToBatchMetadataHash(uint32) external view returns (bytes32); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct batchIdToBatchMetadataHashCall { + pub _0: u32, + } + ///Container type for the return parameters of the [`batchIdToBatchMetadataHash(uint32)`](batchIdToBatchMetadataHashCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct batchIdToBatchMetadataHashReturn { + pub _0: alloy::sol_types::private::FixedBytes<32>, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u32,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: batchIdToBatchMetadataHashCall) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for batchIdToBatchMetadataHashCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::FixedBytes<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::FixedBytes<32>,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: batchIdToBatchMetadataHashReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for batchIdToBatchMetadataHashReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for batchIdToBatchMetadataHashCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = batchIdToBatchMetadataHashReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::FixedBytes<32>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "batchIdToBatchMetadataHash(uint32)"; + const SELECTOR: [u8; 4] = [236u8, 203u8, 191u8, 201u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self._0, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `blsApkRegistry()` and selector `0x5df45946`. + ```solidity + function blsApkRegistry() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct blsApkRegistryCall {} + ///Container type for the return parameters of the [`blsApkRegistry()`](blsApkRegistryCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct blsApkRegistryReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: blsApkRegistryCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for blsApkRegistryCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: blsApkRegistryReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for blsApkRegistryReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for blsApkRegistryCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = blsApkRegistryReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "blsApkRegistry()"; + const SELECTOR: [u8; 4] = [93u8, 244u8, 89u8, 70u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `checkSignatures(bytes32,bytes,uint32,(uint32[],(uint256,uint256)[],(uint256,uint256)[],(uint256[2],uint256[2]),(uint256,uint256),uint32[],uint32[],uint32[][]))` and selector `0x6efb4636`. + ```solidity + function checkSignatures(bytes32 msgHash, bytes memory quorumNumbers, uint32 referenceBlockNumber, NonSignerStakesAndSignature memory params) external view returns (QuorumStakeTotals memory, bytes32); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct checkSignaturesCall { + pub msgHash: alloy::sol_types::private::FixedBytes<32>, + pub quorumNumbers: alloy::sol_types::private::Bytes, + pub referenceBlockNumber: u32, + pub params: ::RustType, + } + ///Container type for the return parameters of the [`checkSignatures(bytes32,bytes,uint32,(uint32[],(uint256,uint256)[],(uint256,uint256)[],(uint256[2],uint256[2]),(uint256,uint256),uint32[],uint32[],uint32[][]))`](checkSignaturesCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct checkSignaturesReturn { + pub _0: ::RustType, + pub _1: alloy::sol_types::private::FixedBytes<32>, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::Bytes, + alloy::sol_types::sol_data::Uint<32>, + NonSignerStakesAndSignature, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::FixedBytes<32>, + alloy::sol_types::private::Bytes, + u32, + ::RustType, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: checkSignaturesCall) -> Self { + ( + value.msgHash, + value.quorumNumbers, + value.referenceBlockNumber, + value.params, + ) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for checkSignaturesCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + msgHash: tuple.0, + quorumNumbers: tuple.1, + referenceBlockNumber: tuple.2, + params: tuple.3, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + QuorumStakeTotals, + alloy::sol_types::sol_data::FixedBytes<32>, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + ::RustType, + alloy::sol_types::private::FixedBytes<32>, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: checkSignaturesReturn) -> Self { + (value._0, value._1) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for checkSignaturesReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + _0: tuple.0, + _1: tuple.1, + } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for checkSignaturesCall { + type Parameters<'a> = ( + alloy::sol_types::sol_data::FixedBytes<32>, + alloy::sol_types::sol_data::Bytes, + alloy::sol_types::sol_data::Uint<32>, + NonSignerStakesAndSignature, + ); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = checkSignaturesReturn; + type ReturnTuple<'a> = ( + QuorumStakeTotals, + alloy::sol_types::sol_data::FixedBytes<32>, + ); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "checkSignatures(bytes32,bytes,uint32,(uint32[],(uint256,uint256)[],(uint256,uint256)[],(uint256[2],uint256[2]),(uint256,uint256),uint32[],uint32[],uint32[][]))"; + const SELECTOR: [u8; 4] = [110u8, 251u8, 70u8, 54u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize(&self.msgHash), + ::tokenize( + &self.quorumNumbers, + ), + as alloy_sol_types::SolType>::tokenize(&self.referenceBlockNumber), + ::tokenize( + &self.params, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `confirmBatch((bytes32,bytes,bytes,uint32),(uint32[],(uint256,uint256)[],(uint256,uint256)[],(uint256[2],uint256[2]),(uint256,uint256),uint32[],uint32[],uint32[][]))` and selector `0x7794965a`. + ```solidity + function confirmBatch(BatchHeader memory batchHeader, NonSignerStakesAndSignature memory nonSignerStakesAndSignature) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct confirmBatchCall { + pub batchHeader: ::RustType, + pub nonSignerStakesAndSignature: + ::RustType, + } + ///Container type for the return parameters of the [`confirmBatch((bytes32,bytes,bytes,uint32),(uint32[],(uint256,uint256)[],(uint256,uint256)[],(uint256[2],uint256[2]),(uint256,uint256),uint32[],uint32[],uint32[][]))`](confirmBatchCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct confirmBatchReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (BatchHeader, NonSignerStakesAndSignature); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + ::RustType, + ::RustType, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: confirmBatchCall) -> Self { + (value.batchHeader, value.nonSignerStakesAndSignature) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for confirmBatchCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + batchHeader: tuple.0, + nonSignerStakesAndSignature: tuple.1, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: confirmBatchReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for confirmBatchReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for confirmBatchCall { + type Parameters<'a> = (BatchHeader, NonSignerStakesAndSignature); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = confirmBatchReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "confirmBatch((bytes32,bytes,bytes,uint32),(uint32[],(uint256,uint256)[],(uint256,uint256)[],(uint256[2],uint256[2]),(uint256,uint256),uint32[],uint32[],uint32[][]))"; + const SELECTOR: [u8; 4] = [119u8, 148u8, 150u8, 90u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize(&self.batchHeader), + ::tokenize( + &self.nonSignerStakesAndSignature, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `createAVSRewardsSubmission(((address,uint96)[],address,uint256,uint32,uint32)[])` and selector `0xfce36c7d`. + ```solidity + function createAVSRewardsSubmission(RewardsSubmission[] memory rewardsSubmissions) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct createAVSRewardsSubmissionCall { + pub rewardsSubmissions: alloy::sol_types::private::Vec< + ::RustType, + >, + } + ///Container type for the return parameters of the [`createAVSRewardsSubmission(((address,uint96)[],address,uint256,uint32,uint32)[])`](createAVSRewardsSubmissionCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct createAVSRewardsSubmissionReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Array,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Vec< + ::RustType, + >, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: createAVSRewardsSubmissionCall) -> Self { + (value.rewardsSubmissions,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for createAVSRewardsSubmissionCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + rewardsSubmissions: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: createAVSRewardsSubmissionReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for createAVSRewardsSubmissionReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for createAVSRewardsSubmissionCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Array,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = createAVSRewardsSubmissionReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = + "createAVSRewardsSubmission(((address,uint96)[],address,uint256,uint32,uint32)[])"; + const SELECTOR: [u8; 4] = [252u8, 227u8, 108u8, 125u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize(&self.rewardsSubmissions), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `delegation()` and selector `0xdf5cf723`. + ```solidity + function delegation() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct delegationCall {} + ///Container type for the return parameters of the [`delegation()`](delegationCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct delegationReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: delegationCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for delegationCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: delegationReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for delegationReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for delegationCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = delegationReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "delegation()"; + const SELECTOR: [u8; 4] = [223u8, 92u8, 247u8, 35u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `deregisterOperatorFromAVS(address)` and selector `0xa364f4da`. + ```solidity + function deregisterOperatorFromAVS(address operator) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct deregisterOperatorFromAVSCall { + pub operator: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`deregisterOperatorFromAVS(address)`](deregisterOperatorFromAVSCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct deregisterOperatorFromAVSReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: deregisterOperatorFromAVSCall) -> Self { + (value.operator,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for deregisterOperatorFromAVSCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { operator: tuple.0 } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: deregisterOperatorFromAVSReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for deregisterOperatorFromAVSReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for deregisterOperatorFromAVSCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Address,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = deregisterOperatorFromAVSReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "deregisterOperatorFromAVS(address)"; + const SELECTOR: [u8; 4] = [163u8, 100u8, 244u8, 218u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.operator, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `getOperatorRestakedStrategies(address)` and selector `0x33cfb7b7`. + ```solidity + function getOperatorRestakedStrategies(address operator) external view returns (address[] memory); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct getOperatorRestakedStrategiesCall { + pub operator: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`getOperatorRestakedStrategies(address)`](getOperatorRestakedStrategiesCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct getOperatorRestakedStrategiesReturn { + pub _0: alloy::sol_types::private::Vec, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: getOperatorRestakedStrategiesCall) -> Self { + (value.operator,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for getOperatorRestakedStrategiesCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { operator: tuple.0 } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = + (alloy::sol_types::sol_data::Array,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = + (alloy::sol_types::private::Vec,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: getOperatorRestakedStrategiesReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for getOperatorRestakedStrategiesReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for getOperatorRestakedStrategiesCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Address,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = getOperatorRestakedStrategiesReturn; + type ReturnTuple<'a> = + (alloy::sol_types::sol_data::Array,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "getOperatorRestakedStrategies(address)"; + const SELECTOR: [u8; 4] = [51u8, 207u8, 183u8, 183u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.operator, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `getRestakeableStrategies()` and selector `0xe481af9d`. + ```solidity + function getRestakeableStrategies() external view returns (address[] memory); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct getRestakeableStrategiesCall {} + ///Container type for the return parameters of the [`getRestakeableStrategies()`](getRestakeableStrategiesCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct getRestakeableStrategiesReturn { + pub _0: alloy::sol_types::private::Vec, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: getRestakeableStrategiesCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for getRestakeableStrategiesCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = + (alloy::sol_types::sol_data::Array,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = + (alloy::sol_types::private::Vec,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: getRestakeableStrategiesReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for getRestakeableStrategiesReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for getRestakeableStrategiesCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = getRestakeableStrategiesReturn; + type ReturnTuple<'a> = + (alloy::sol_types::sol_data::Array,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "getRestakeableStrategies()"; + const SELECTOR: [u8; 4] = [228u8, 129u8, 175u8, 157u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `initialize(address,uint256,address,address[],address)` and selector `0x775bbcb5`. + ```solidity + function initialize(address _pauserRegistry, uint256 _initialPausedStatus, address _initialOwner, address[] memory _batchConfirmers, address _rewardsInitiator) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct initializeCall { + pub _pauserRegistry: alloy::sol_types::private::Address, + pub _initialPausedStatus: alloy::sol_types::private::U256, + pub _initialOwner: alloy::sol_types::private::Address, + pub _batchConfirmers: alloy::sol_types::private::Vec, + pub _rewardsInitiator: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`initialize(address,uint256,address,address[],address)`](initializeCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct initializeReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Uint<256>, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Array, + alloy::sol_types::sol_data::Address, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Address, + alloy::sol_types::private::U256, + alloy::sol_types::private::Address, + alloy::sol_types::private::Vec, + alloy::sol_types::private::Address, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: initializeCall) -> Self { + ( + value._pauserRegistry, + value._initialPausedStatus, + value._initialOwner, + value._batchConfirmers, + value._rewardsInitiator, + ) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for initializeCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + _pauserRegistry: tuple.0, + _initialPausedStatus: tuple.1, + _initialOwner: tuple.2, + _batchConfirmers: tuple.3, + _rewardsInitiator: tuple.4, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: initializeReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for initializeReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for initializeCall { + type Parameters<'a> = ( + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Uint<256>, + alloy::sol_types::sol_data::Address, + alloy::sol_types::sol_data::Array, + alloy::sol_types::sol_data::Address, + ); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = initializeReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "initialize(address,uint256,address,address[],address)"; + const SELECTOR: [u8; 4] = [119u8, 91u8, 188u8, 181u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self._pauserRegistry, + ), + as alloy_sol_types::SolType>::tokenize(&self._initialPausedStatus), + ::tokenize( + &self._initialOwner, + ), + as alloy_sol_types::SolType>::tokenize(&self._batchConfirmers), + ::tokenize( + &self._rewardsInitiator, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `isBatchConfirmer(address)` and selector `0xa5b7890a`. + ```solidity + function isBatchConfirmer(address) external view returns (bool); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct isBatchConfirmerCall { + pub _0: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`isBatchConfirmer(address)`](isBatchConfirmerCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct isBatchConfirmerReturn { + pub _0: bool, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: isBatchConfirmerCall) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for isBatchConfirmerCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Bool,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (bool,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: isBatchConfirmerReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for isBatchConfirmerReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for isBatchConfirmerCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Address,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = isBatchConfirmerReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Bool,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "isBatchConfirmer(address)"; + const SELECTOR: [u8; 4] = [165u8, 183u8, 137u8, 10u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self._0, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `latestServeUntilBlock(uint32)` and selector `0xeaefd27d`. + ```solidity + function latestServeUntilBlock(uint32 referenceBlockNumber) external view returns (uint32); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct latestServeUntilBlockCall { + pub referenceBlockNumber: u32, + } + ///Container type for the return parameters of the [`latestServeUntilBlock(uint32)`](latestServeUntilBlockCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct latestServeUntilBlockReturn { + pub _0: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u32,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: latestServeUntilBlockCall) -> Self { + (value.referenceBlockNumber,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for latestServeUntilBlockCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + referenceBlockNumber: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u32,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: latestServeUntilBlockReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for latestServeUntilBlockReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for latestServeUntilBlockCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = latestServeUntilBlockReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "latestServeUntilBlock(uint32)"; + const SELECTOR: [u8; 4] = [234u8, 239u8, 210u8, 125u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.referenceBlockNumber, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `owner()` and selector `0x8da5cb5b`. + ```solidity + function owner() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct ownerCall {} + ///Container type for the return parameters of the [`owner()`](ownerCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct ownerReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: ownerCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for ownerCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: ownerReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for ownerReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for ownerCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = ownerReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "owner()"; + const SELECTOR: [u8; 4] = [141u8, 165u8, 203u8, 91u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `pause(uint256)` and selector `0x136439dd`. + ```solidity + function pause(uint256 newPausedStatus) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct pauseCall { + pub newPausedStatus: alloy::sol_types::private::U256, + } + ///Container type for the return parameters of the [`pause(uint256)`](pauseCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct pauseReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::U256,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: pauseCall) -> Self { + (value.newPausedStatus,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for pauseCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + newPausedStatus: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: pauseReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for pauseReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for pauseCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Uint<256>,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = pauseReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "pause(uint256)"; + const SELECTOR: [u8; 4] = [19u8, 100u8, 57u8, 221u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.newPausedStatus, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `pauseAll()` and selector `0x595c6a67`. + ```solidity + function pauseAll() external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct pauseAllCall {} + ///Container type for the return parameters of the [`pauseAll()`](pauseAllCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct pauseAllReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: pauseAllCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for pauseAllCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: pauseAllReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for pauseAllReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for pauseAllCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = pauseAllReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "pauseAll()"; + const SELECTOR: [u8; 4] = [89u8, 92u8, 106u8, 103u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `paused(uint8)` and selector `0x5ac86ab7`. + ```solidity + function paused(uint8 index) external view returns (bool); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct paused_0Call { + pub index: u8, + } + ///Container type for the return parameters of the [`paused(uint8)`](paused_0Call) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct paused_0Return { + pub _0: bool, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<8>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u8,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: paused_0Call) -> Self { + (value.index,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for paused_0Call { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { index: tuple.0 } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Bool,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (bool,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: paused_0Return) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for paused_0Return { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for paused_0Call { + type Parameters<'a> = (alloy::sol_types::sol_data::Uint<8>,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = paused_0Return; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Bool,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "paused(uint8)"; + const SELECTOR: [u8; 4] = [90u8, 200u8, 106u8, 183u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.index, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `paused()` and selector `0x5c975abb`. + ```solidity + function paused() external view returns (uint256); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct paused_1Call {} + ///Container type for the return parameters of the [`paused()`](paused_1Call) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct paused_1Return { + pub _0: alloy::sol_types::private::U256, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: paused_1Call) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for paused_1Call { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::U256,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: paused_1Return) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for paused_1Return { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for paused_1Call { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = paused_1Return; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "paused()"; + const SELECTOR: [u8; 4] = [92u8, 151u8, 90u8, 187u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `pauserRegistry()` and selector `0x886f1195`. + ```solidity + function pauserRegistry() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct pauserRegistryCall {} + ///Container type for the return parameters of the [`pauserRegistry()`](pauserRegistryCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct pauserRegistryReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: pauserRegistryCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for pauserRegistryCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: pauserRegistryReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for pauserRegistryReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for pauserRegistryCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = pauserRegistryReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "pauserRegistry()"; + const SELECTOR: [u8; 4] = [136u8, 111u8, 17u8, 149u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `quorumAdversaryThresholdPercentages()` and selector `0x8687feae`. + ```solidity + function quorumAdversaryThresholdPercentages() external view returns (bytes memory); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct quorumAdversaryThresholdPercentagesCall {} + ///Container type for the return parameters of the [`quorumAdversaryThresholdPercentages()`](quorumAdversaryThresholdPercentagesCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct quorumAdversaryThresholdPercentagesReturn { + pub _0: alloy::sol_types::private::Bytes, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: quorumAdversaryThresholdPercentagesCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for quorumAdversaryThresholdPercentagesCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Bytes,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Bytes,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: quorumAdversaryThresholdPercentagesReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for quorumAdversaryThresholdPercentagesReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for quorumAdversaryThresholdPercentagesCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = quorumAdversaryThresholdPercentagesReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Bytes,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "quorumAdversaryThresholdPercentages()"; + const SELECTOR: [u8; 4] = [134u8, 135u8, 254u8, 174u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `quorumConfirmationThresholdPercentages()` and selector `0xbafa9107`. + ```solidity + function quorumConfirmationThresholdPercentages() external view returns (bytes memory); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct quorumConfirmationThresholdPercentagesCall {} + ///Container type for the return parameters of the [`quorumConfirmationThresholdPercentages()`](quorumConfirmationThresholdPercentagesCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct quorumConfirmationThresholdPercentagesReturn { + pub _0: alloy::sol_types::private::Bytes, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: quorumConfirmationThresholdPercentagesCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for quorumConfirmationThresholdPercentagesCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Bytes,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Bytes,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From + for UnderlyingRustTuple<'_> + { + fn from(value: quorumConfirmationThresholdPercentagesReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> + for quorumConfirmationThresholdPercentagesReturn + { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for quorumConfirmationThresholdPercentagesCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = quorumConfirmationThresholdPercentagesReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Bytes,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "quorumConfirmationThresholdPercentages()"; + const SELECTOR: [u8; 4] = [186u8, 250u8, 145u8, 7u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `quorumNumbersRequired()` and selector `0xe15234ff`. + ```solidity + function quorumNumbersRequired() external view returns (bytes memory); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct quorumNumbersRequiredCall {} + ///Container type for the return parameters of the [`quorumNumbersRequired()`](quorumNumbersRequiredCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct quorumNumbersRequiredReturn { + pub _0: alloy::sol_types::private::Bytes, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: quorumNumbersRequiredCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for quorumNumbersRequiredCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Bytes,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Bytes,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: quorumNumbersRequiredReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for quorumNumbersRequiredReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for quorumNumbersRequiredCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = quorumNumbersRequiredReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Bytes,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "quorumNumbersRequired()"; + const SELECTOR: [u8; 4] = [225u8, 82u8, 52u8, 255u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `registerOperatorToAVS(address,(bytes,bytes32,uint256))` and selector `0x9926ee7d`. + ```solidity + function registerOperatorToAVS(address operator, SignatureWithSaltAndExpiry memory operatorSignature) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct registerOperatorToAVSCall { + pub operator: alloy::sol_types::private::Address, + pub operatorSignature: ::RustType, + } + ///Container type for the return parameters of the [`registerOperatorToAVS(address,(bytes,bytes32,uint256))`](registerOperatorToAVSCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct registerOperatorToAVSReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Address, + SignatureWithSaltAndExpiry, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::Address, + ::RustType, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: registerOperatorToAVSCall) -> Self { + (value.operator, value.operatorSignature) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for registerOperatorToAVSCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + operator: tuple.0, + operatorSignature: tuple.1, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: registerOperatorToAVSReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for registerOperatorToAVSReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for registerOperatorToAVSCall { + type Parameters<'a> = ( + alloy::sol_types::sol_data::Address, + SignatureWithSaltAndExpiry, + ); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = registerOperatorToAVSReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = + "registerOperatorToAVS(address,(bytes,bytes32,uint256))"; + const SELECTOR: [u8; 4] = [153u8, 38u8, 238u8, 125u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.operator, + ), + ::tokenize( + &self.operatorSignature, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `registryCoordinator()` and selector `0x6d14a987`. + ```solidity + function registryCoordinator() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct registryCoordinatorCall {} + ///Container type for the return parameters of the [`registryCoordinator()`](registryCoordinatorCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct registryCoordinatorReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: registryCoordinatorCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for registryCoordinatorCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: registryCoordinatorReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for registryCoordinatorReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for registryCoordinatorCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = registryCoordinatorReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "registryCoordinator()"; + const SELECTOR: [u8; 4] = [109u8, 20u8, 169u8, 135u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `renounceOwnership()` and selector `0x715018a6`. + ```solidity + function renounceOwnership() external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct renounceOwnershipCall {} + ///Container type for the return parameters of the [`renounceOwnership()`](renounceOwnershipCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct renounceOwnershipReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: renounceOwnershipCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for renounceOwnershipCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: renounceOwnershipReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for renounceOwnershipReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for renounceOwnershipCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = renounceOwnershipReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "renounceOwnership()"; + const SELECTOR: [u8; 4] = [113u8, 80u8, 24u8, 166u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `rewardsInitiator()` and selector `0xfc299dee`. + ```solidity + function rewardsInitiator() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct rewardsInitiatorCall {} + ///Container type for the return parameters of the [`rewardsInitiator()`](rewardsInitiatorCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct rewardsInitiatorReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: rewardsInitiatorCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for rewardsInitiatorCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: rewardsInitiatorReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for rewardsInitiatorReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for rewardsInitiatorCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = rewardsInitiatorReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "rewardsInitiator()"; + const SELECTOR: [u8; 4] = [252u8, 41u8, 157u8, 238u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `setBatchConfirmer(address)` and selector `0xf1220983`. + ```solidity + function setBatchConfirmer(address _batchConfirmer) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setBatchConfirmerCall { + pub _batchConfirmer: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`setBatchConfirmer(address)`](setBatchConfirmerCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setBatchConfirmerReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setBatchConfirmerCall) -> Self { + (value._batchConfirmer,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setBatchConfirmerCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + _batchConfirmer: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setBatchConfirmerReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setBatchConfirmerReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for setBatchConfirmerCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Address,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = setBatchConfirmerReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "setBatchConfirmer(address)"; + const SELECTOR: [u8; 4] = [241u8, 34u8, 9u8, 131u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self._batchConfirmer, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `setPauserRegistry(address)` and selector `0x10d67a2f`. + ```solidity + function setPauserRegistry(address newPauserRegistry) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setPauserRegistryCall { + pub newPauserRegistry: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`setPauserRegistry(address)`](setPauserRegistryCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setPauserRegistryReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setPauserRegistryCall) -> Self { + (value.newPauserRegistry,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setPauserRegistryCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + newPauserRegistry: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setPauserRegistryReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setPauserRegistryReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for setPauserRegistryCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Address,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = setPauserRegistryReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "setPauserRegistry(address)"; + const SELECTOR: [u8; 4] = [16u8, 214u8, 122u8, 47u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.newPauserRegistry, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `setRewardsInitiator(address)` and selector `0x3bc28c8c`. + ```solidity + function setRewardsInitiator(address newRewardsInitiator) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setRewardsInitiatorCall { + pub newRewardsInitiator: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`setRewardsInitiator(address)`](setRewardsInitiatorCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setRewardsInitiatorReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setRewardsInitiatorCall) -> Self { + (value.newRewardsInitiator,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setRewardsInitiatorCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + newRewardsInitiator: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setRewardsInitiatorReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setRewardsInitiatorReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for setRewardsInitiatorCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Address,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = setRewardsInitiatorReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "setRewardsInitiator(address)"; + const SELECTOR: [u8; 4] = [59u8, 194u8, 140u8, 140u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.newRewardsInitiator, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `setStaleStakesForbidden(bool)` and selector `0x416c7e5e`. + ```solidity + function setStaleStakesForbidden(bool value) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setStaleStakesForbiddenCall { + pub value: bool, + } + ///Container type for the return parameters of the [`setStaleStakesForbidden(bool)`](setStaleStakesForbiddenCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct setStaleStakesForbiddenReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Bool,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (bool,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setStaleStakesForbiddenCall) -> Self { + (value.value,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setStaleStakesForbiddenCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { value: tuple.0 } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: setStaleStakesForbiddenReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for setStaleStakesForbiddenReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for setStaleStakesForbiddenCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Bool,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = setStaleStakesForbiddenReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "setStaleStakesForbidden(bool)"; + const SELECTOR: [u8; 4] = [65u8, 108u8, 126u8, 94u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.value, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `stakeRegistry()` and selector `0x68304835`. + ```solidity + function stakeRegistry() external view returns (address); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct stakeRegistryCall {} + ///Container type for the return parameters of the [`stakeRegistry()`](stakeRegistryCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct stakeRegistryReturn { + pub _0: alloy::sol_types::private::Address, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: stakeRegistryCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for stakeRegistryCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: stakeRegistryReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for stakeRegistryReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for stakeRegistryCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = stakeRegistryReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Address,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "stakeRegistry()"; + const SELECTOR: [u8; 4] = [104u8, 48u8, 72u8, 53u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `staleStakesForbidden()` and selector `0xb98d0908`. + ```solidity + function staleStakesForbidden() external view returns (bool); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct staleStakesForbiddenCall {} + ///Container type for the return parameters of the [`staleStakesForbidden()`](staleStakesForbiddenCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct staleStakesForbiddenReturn { + pub _0: bool, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: staleStakesForbiddenCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for staleStakesForbiddenCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Bool,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (bool,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: staleStakesForbiddenReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for staleStakesForbiddenReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for staleStakesForbiddenCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = staleStakesForbiddenReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Bool,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "staleStakesForbidden()"; + const SELECTOR: [u8; 4] = [185u8, 141u8, 9u8, 8u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `taskNumber()` and selector `0x72d18e8d`. + ```solidity + function taskNumber() external view returns (uint32); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct taskNumberCall {} + ///Container type for the return parameters of the [`taskNumber()`](taskNumberCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct taskNumberReturn { + pub _0: u32, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: taskNumberCall) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for taskNumberCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (u32,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: taskNumberReturn) -> Self { + (value._0,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for taskNumberReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { _0: tuple.0 } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for taskNumberCall { + type Parameters<'a> = (); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = taskNumberReturn; + type ReturnTuple<'a> = (alloy::sol_types::sol_data::Uint<32>,); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "taskNumber()"; + const SELECTOR: [u8; 4] = [114u8, 209u8, 142u8, 141u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + () + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `transferOwnership(address)` and selector `0xf2fde38b`. + ```solidity + function transferOwnership(address newOwner) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct transferOwnershipCall { + pub newOwner: alloy::sol_types::private::Address, + } + ///Container type for the return parameters of the [`transferOwnership(address)`](transferOwnershipCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct transferOwnershipReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Address,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::Address,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: transferOwnershipCall) -> Self { + (value.newOwner,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for transferOwnershipCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { newOwner: tuple.0 } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: transferOwnershipReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for transferOwnershipReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for transferOwnershipCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Address,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = transferOwnershipReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "transferOwnership(address)"; + const SELECTOR: [u8; 4] = [242u8, 253u8, 227u8, 139u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self.newOwner, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `trySignatureAndApkVerification(bytes32,(uint256,uint256),(uint256[2],uint256[2]),(uint256,uint256))` and selector `0x171f1d5b`. + ```solidity + function trySignatureAndApkVerification(bytes32 msgHash, G1Point memory apk, G2Point memory apkG2, G1Point memory sigma) external view returns (bool pairingSuccessful, bool siganatureIsValid); + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct trySignatureAndApkVerificationCall { + pub msgHash: alloy::sol_types::private::FixedBytes<32>, + pub apk: ::RustType, + pub apkG2: ::RustType, + pub sigma: ::RustType, + } + ///Container type for the return parameters of the [`trySignatureAndApkVerification(bytes32,(uint256,uint256),(uint256[2],uint256[2]),(uint256,uint256))`](trySignatureAndApkVerificationCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct trySignatureAndApkVerificationReturn { + pub pairingSuccessful: bool, + pub siganatureIsValid: bool, + } + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::FixedBytes<32>, + G1Point, + G2Point, + G1Point, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = ( + alloy::sol_types::private::FixedBytes<32>, + ::RustType, + ::RustType, + ::RustType, + ); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: trySignatureAndApkVerificationCall) -> Self { + (value.msgHash, value.apk, value.apkG2, value.sigma) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for trySignatureAndApkVerificationCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + msgHash: tuple.0, + apk: tuple.1, + apkG2: tuple.2, + sigma: tuple.3, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = ( + alloy::sol_types::sol_data::Bool, + alloy::sol_types::sol_data::Bool, + ); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (bool, bool); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: trySignatureAndApkVerificationReturn) -> Self { + (value.pairingSuccessful, value.siganatureIsValid) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for trySignatureAndApkVerificationReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + pairingSuccessful: tuple.0, + siganatureIsValid: tuple.1, + } + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for trySignatureAndApkVerificationCall { + type Parameters<'a> = ( + alloy::sol_types::sol_data::FixedBytes<32>, + G1Point, + G2Point, + G1Point, + ); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = trySignatureAndApkVerificationReturn; + type ReturnTuple<'a> = ( + alloy::sol_types::sol_data::Bool, + alloy::sol_types::sol_data::Bool, + ); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "trySignatureAndApkVerification(bytes32,(uint256,uint256),(uint256[2],uint256[2]),(uint256,uint256))"; + const SELECTOR: [u8; 4] = [23u8, 31u8, 29u8, 91u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize(&self.msgHash), + ::tokenize(&self.apk), + ::tokenize(&self.apkG2), + ::tokenize(&self.sigma), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `unpause(uint256)` and selector `0xfabc1cbc`. + ```solidity + function unpause(uint256 newPausedStatus) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct unpauseCall { + pub newPausedStatus: alloy::sol_types::private::U256, + } + ///Container type for the return parameters of the [`unpause(uint256)`](unpauseCall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct unpauseReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::Uint<256>,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::U256,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: unpauseCall) -> Self { + (value.newPausedStatus,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for unpauseCall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + newPausedStatus: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: unpauseReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for unpauseReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for unpauseCall { + type Parameters<'a> = (alloy::sol_types::sol_data::Uint<256>,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = unpauseReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "unpause(uint256)"; + const SELECTOR: [u8; 4] = [250u8, 188u8, 28u8, 188u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + as alloy_sol_types::SolType>::tokenize( + &self.newPausedStatus, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + /**Function with signature `updateAVSMetadataURI(string)` and selector `0xa98fb355`. + ```solidity + function updateAVSMetadataURI(string memory _metadataURI) external; + ```*/ + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct updateAVSMetadataURICall { + pub _metadataURI: alloy::sol_types::private::String, + } + ///Container type for the return parameters of the [`updateAVSMetadataURI(string)`](updateAVSMetadataURICall) function. + #[allow(non_camel_case_types, non_snake_case)] + #[derive(Clone)] + pub struct updateAVSMetadataURIReturn {} + #[allow(non_camel_case_types, non_snake_case, clippy::style)] + const _: () = { + use alloy::sol_types as alloy_sol_types; + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (alloy::sol_types::sol_data::String,); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (alloy::sol_types::private::String,); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: updateAVSMetadataURICall) -> Self { + (value._metadataURI,) + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for updateAVSMetadataURICall { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self { + _metadataURI: tuple.0, + } + } + } + } + { + #[doc(hidden)] + type UnderlyingSolTuple<'a> = (); + #[doc(hidden)] + type UnderlyingRustTuple<'a> = (); + #[cfg(test)] + #[allow(dead_code, unreachable_patterns)] + fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { + match _t { + alloy_sol_types::private::AssertTypeEq::< + ::RustType, + >(_) => {} + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From for UnderlyingRustTuple<'_> { + fn from(value: updateAVSMetadataURIReturn) -> Self { + () + } + } + #[automatically_derived] + #[doc(hidden)] + impl ::core::convert::From> for updateAVSMetadataURIReturn { + fn from(tuple: UnderlyingRustTuple<'_>) -> Self { + Self {} + } + } + } + #[automatically_derived] + impl alloy_sol_types::SolCall for updateAVSMetadataURICall { + type Parameters<'a> = (alloy::sol_types::sol_data::String,); + type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; + type Return = updateAVSMetadataURIReturn; + type ReturnTuple<'a> = (); + type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; + const SIGNATURE: &'static str = "updateAVSMetadataURI(string)"; + const SELECTOR: [u8; 4] = [169u8, 143u8, 179u8, 85u8]; + #[inline] + fn new<'a>( + tuple: as alloy_sol_types::SolType>::RustType, + ) -> Self { + tuple.into() + } + #[inline] + fn tokenize(&self) -> Self::Token<'_> { + ( + ::tokenize( + &self._metadataURI, + ), + ) + } + #[inline] + fn abi_decode_returns( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + as alloy_sol_types::SolType>::abi_decode_sequence( + data, validate, + ) + .map(Into::into) + } + } + }; + ///Container for all the [`EigenDAServiceManager`](self) function calls. + pub enum EigenDAServiceManagerCalls { + BLOCK_STALE_MEASURE(BLOCK_STALE_MEASURECall), + STORE_DURATION_BLOCKS(STORE_DURATION_BLOCKSCall), + THRESHOLD_DENOMINATOR(THRESHOLD_DENOMINATORCall), + avsDirectory(avsDirectoryCall), + batchId(batchIdCall), + batchIdToBatchMetadataHash(batchIdToBatchMetadataHashCall), + blsApkRegistry(blsApkRegistryCall), + checkSignatures(checkSignaturesCall), + confirmBatch(confirmBatchCall), + createAVSRewardsSubmission(createAVSRewardsSubmissionCall), + delegation(delegationCall), + deregisterOperatorFromAVS(deregisterOperatorFromAVSCall), + getOperatorRestakedStrategies(getOperatorRestakedStrategiesCall), + getRestakeableStrategies(getRestakeableStrategiesCall), + initialize(initializeCall), + isBatchConfirmer(isBatchConfirmerCall), + latestServeUntilBlock(latestServeUntilBlockCall), + owner(ownerCall), + pause(pauseCall), + pauseAll(pauseAllCall), + paused_0(paused_0Call), + paused_1(paused_1Call), + pauserRegistry(pauserRegistryCall), + quorumAdversaryThresholdPercentages(quorumAdversaryThresholdPercentagesCall), + quorumConfirmationThresholdPercentages(quorumConfirmationThresholdPercentagesCall), + quorumNumbersRequired(quorumNumbersRequiredCall), + registerOperatorToAVS(registerOperatorToAVSCall), + registryCoordinator(registryCoordinatorCall), + renounceOwnership(renounceOwnershipCall), + rewardsInitiator(rewardsInitiatorCall), + setBatchConfirmer(setBatchConfirmerCall), + setPauserRegistry(setPauserRegistryCall), + setRewardsInitiator(setRewardsInitiatorCall), + setStaleStakesForbidden(setStaleStakesForbiddenCall), + stakeRegistry(stakeRegistryCall), + staleStakesForbidden(staleStakesForbiddenCall), + taskNumber(taskNumberCall), + transferOwnership(transferOwnershipCall), + trySignatureAndApkVerification(trySignatureAndApkVerificationCall), + unpause(unpauseCall), + updateAVSMetadataURI(updateAVSMetadataURICall), + } + #[automatically_derived] + impl EigenDAServiceManagerCalls { + /// All the selectors of this enum. + /// + /// Note that the selectors might not be in the same order as the variants. + /// No guarantees are made about the order of the selectors. + /// + /// Prefer using `SolInterface` methods instead. + pub const SELECTORS: &'static [[u8; 4usize]] = &[ + [16u8, 214u8, 122u8, 47u8], + [19u8, 100u8, 57u8, 221u8], + [23u8, 31u8, 29u8, 91u8], + [51u8, 207u8, 183u8, 183u8], + [59u8, 194u8, 140u8, 140u8], + [65u8, 108u8, 126u8, 94u8], + [73u8, 114u8, 19u8, 74u8], + [89u8, 92u8, 106u8, 103u8], + [90u8, 200u8, 106u8, 183u8], + [92u8, 151u8, 90u8, 187u8], + [93u8, 244u8, 89u8, 70u8], + [94u8, 3u8, 52u8, 118u8], + [94u8, 139u8, 63u8, 45u8], + [104u8, 48u8, 72u8, 53u8], + [107u8, 58u8, 167u8, 46u8], + [109u8, 20u8, 169u8, 135u8], + [110u8, 251u8, 70u8, 54u8], + [113u8, 80u8, 24u8, 166u8], + [114u8, 209u8, 142u8, 141u8], + [119u8, 91u8, 188u8, 181u8], + [119u8, 148u8, 150u8, 90u8], + [134u8, 135u8, 254u8, 174u8], + [136u8, 111u8, 17u8, 149u8], + [141u8, 165u8, 203u8, 91u8], + [153u8, 38u8, 238u8, 125u8], + [163u8, 100u8, 244u8, 218u8], + [165u8, 183u8, 137u8, 10u8], + [169u8, 143u8, 179u8, 85u8], + [185u8, 141u8, 9u8, 8u8], + [186u8, 250u8, 145u8, 7u8], + [223u8, 92u8, 247u8, 35u8], + [225u8, 82u8, 52u8, 255u8], + [228u8, 129u8, 175u8, 157u8], + [234u8, 239u8, 210u8, 125u8], + [236u8, 203u8, 191u8, 201u8], + [239u8, 2u8, 68u8, 88u8], + [241u8, 34u8, 9u8, 131u8], + [242u8, 253u8, 227u8, 139u8], + [250u8, 188u8, 28u8, 188u8], + [252u8, 41u8, 157u8, 238u8], + [252u8, 227u8, 108u8, 125u8], + ]; + } + #[automatically_derived] + impl alloy_sol_types::SolInterface for EigenDAServiceManagerCalls { + const NAME: &'static str = "EigenDAServiceManagerCalls"; + const MIN_DATA_LENGTH: usize = 0usize; + const COUNT: usize = 41usize; + #[inline] + fn selector(&self) -> [u8; 4] { + match self { + Self::BLOCK_STALE_MEASURE(_) => { + ::SELECTOR + } + Self::STORE_DURATION_BLOCKS(_) => { + ::SELECTOR + } + Self::THRESHOLD_DENOMINATOR(_) => { + ::SELECTOR + } + Self::avsDirectory(_) => { + ::SELECTOR + } + Self::batchId(_) => ::SELECTOR, + Self::batchIdToBatchMetadataHash(_) => { + ::SELECTOR + } + Self::blsApkRegistry(_) => { + ::SELECTOR + } + Self::checkSignatures(_) => { + ::SELECTOR + } + Self::confirmBatch(_) => { + ::SELECTOR + } + Self::createAVSRewardsSubmission(_) => { + ::SELECTOR + } + Self::delegation(_) => { + ::SELECTOR + } + Self::deregisterOperatorFromAVS(_) => { + ::SELECTOR + } + Self::getOperatorRestakedStrategies(_) => { + ::SELECTOR + } + Self::getRestakeableStrategies(_) => { + ::SELECTOR + } + Self::initialize(_) => { + ::SELECTOR + } + Self::isBatchConfirmer(_) => { + ::SELECTOR + } + Self::latestServeUntilBlock(_) => { + ::SELECTOR + } + Self::owner(_) => ::SELECTOR, + Self::pause(_) => ::SELECTOR, + Self::pauseAll(_) => ::SELECTOR, + Self::paused_0(_) => ::SELECTOR, + Self::paused_1(_) => ::SELECTOR, + Self::pauserRegistry(_) => { + ::SELECTOR + } + Self::quorumAdversaryThresholdPercentages(_) => { + ::SELECTOR + } + Self::quorumConfirmationThresholdPercentages(_) => { + ::SELECTOR + } + Self::quorumNumbersRequired(_) => { + ::SELECTOR + } + Self::registerOperatorToAVS(_) => { + ::SELECTOR + } + Self::registryCoordinator(_) => { + ::SELECTOR + } + Self::renounceOwnership(_) => { + ::SELECTOR + } + Self::rewardsInitiator(_) => { + ::SELECTOR + } + Self::setBatchConfirmer(_) => { + ::SELECTOR + } + Self::setPauserRegistry(_) => { + ::SELECTOR + } + Self::setRewardsInitiator(_) => { + ::SELECTOR + } + Self::setStaleStakesForbidden(_) => { + ::SELECTOR + } + Self::stakeRegistry(_) => { + ::SELECTOR + } + Self::staleStakesForbidden(_) => { + ::SELECTOR + } + Self::taskNumber(_) => { + ::SELECTOR + } + Self::transferOwnership(_) => { + ::SELECTOR + } + Self::trySignatureAndApkVerification(_) => { + ::SELECTOR + } + Self::unpause(_) => ::SELECTOR, + Self::updateAVSMetadataURI(_) => { + ::SELECTOR + } + } + } + #[inline] + fn selector_at(i: usize) -> ::core::option::Option<[u8; 4]> { + Self::SELECTORS.get(i).copied() + } + #[inline] + fn valid_selector(selector: [u8; 4]) -> bool { + Self::SELECTORS.binary_search(&selector).is_ok() + } + #[inline] + #[allow(unsafe_code, non_snake_case)] + fn abi_decode_raw( + selector: [u8; 4], + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + static DECODE_SHIMS: &[fn( + &[u8], + bool, + ) + -> alloy_sol_types::Result] = &[ + { + fn setPauserRegistry( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::setPauserRegistry) + } + setPauserRegistry + }, + { + fn pause( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::pause) + } + pause + }, + { + fn trySignatureAndApkVerification( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, + validate, + ) + .map( + EigenDAServiceManagerCalls::trySignatureAndApkVerification, + ) + } + trySignatureAndApkVerification + }, + { + fn getOperatorRestakedStrategies( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, + validate, + ) + .map( + EigenDAServiceManagerCalls::getOperatorRestakedStrategies, + ) + } + getOperatorRestakedStrategies + }, + { + fn setRewardsInitiator( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::setRewardsInitiator) + } + setRewardsInitiator + }, + { + fn setStaleStakesForbidden( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::setStaleStakesForbidden) + } + setStaleStakesForbidden + }, + { + fn batchId( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::batchId) + } + batchId + }, + { + fn pauseAll( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::pauseAll) + } + pauseAll + }, + { + fn paused_0( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::paused_0) + } + paused_0 + }, + { + fn paused_1( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::paused_1) + } + paused_1 + }, + { + fn blsApkRegistry( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::blsApkRegistry) + } + blsApkRegistry + }, + { + fn STORE_DURATION_BLOCKS( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::STORE_DURATION_BLOCKS) + } + STORE_DURATION_BLOCKS + }, + { + fn BLOCK_STALE_MEASURE( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::BLOCK_STALE_MEASURE) + } + BLOCK_STALE_MEASURE + }, + { + fn stakeRegistry( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::stakeRegistry) + } + stakeRegistry + }, + { + fn avsDirectory( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::avsDirectory) + } + avsDirectory + }, + { + fn registryCoordinator( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::registryCoordinator) + } + registryCoordinator + }, + { + fn checkSignatures( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::checkSignatures) + } + checkSignatures + }, + { + fn renounceOwnership( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::renounceOwnership) + } + renounceOwnership + }, + { + fn taskNumber( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::taskNumber) + } + taskNumber + }, + { + fn initialize( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::initialize) + } + initialize + }, + { + fn confirmBatch( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::confirmBatch) + } + confirmBatch + }, + { + fn quorumAdversaryThresholdPercentages( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, + validate, + ) + .map( + EigenDAServiceManagerCalls::quorumAdversaryThresholdPercentages, + ) + } + quorumAdversaryThresholdPercentages + }, + { + fn pauserRegistry( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::pauserRegistry) + } + pauserRegistry + }, + { + fn owner( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::owner) + } + owner + }, + { + fn registerOperatorToAVS( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::registerOperatorToAVS) + } + registerOperatorToAVS + }, + { + fn deregisterOperatorFromAVS( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::deregisterOperatorFromAVS) + } + deregisterOperatorFromAVS + }, + { + fn isBatchConfirmer( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::isBatchConfirmer) + } + isBatchConfirmer + }, + { + fn updateAVSMetadataURI( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::updateAVSMetadataURI) + } + updateAVSMetadataURI + }, + { + fn staleStakesForbidden( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::staleStakesForbidden) + } + staleStakesForbidden + }, + { + fn quorumConfirmationThresholdPercentages( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, + validate, + ) + .map( + EigenDAServiceManagerCalls::quorumConfirmationThresholdPercentages, + ) + } + quorumConfirmationThresholdPercentages + }, + { + fn delegation( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::delegation) + } + delegation + }, + { + fn quorumNumbersRequired( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::quorumNumbersRequired) + } + quorumNumbersRequired + }, + { + fn getRestakeableStrategies( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::getRestakeableStrategies) + } + getRestakeableStrategies + }, + { + fn latestServeUntilBlock( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::latestServeUntilBlock) + } + latestServeUntilBlock + }, + { + fn batchIdToBatchMetadataHash( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, + validate, + ) + .map(EigenDAServiceManagerCalls::batchIdToBatchMetadataHash) + } + batchIdToBatchMetadataHash + }, + { + fn THRESHOLD_DENOMINATOR( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::THRESHOLD_DENOMINATOR) + } + THRESHOLD_DENOMINATOR + }, + { + fn setBatchConfirmer( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::setBatchConfirmer) + } + setBatchConfirmer + }, + { + fn transferOwnership( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::transferOwnership) + } + transferOwnership + }, + { + fn unpause( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw(data, validate) + .map(EigenDAServiceManagerCalls::unpause) + } + unpause + }, + { + fn rewardsInitiator( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, validate, + ) + .map(EigenDAServiceManagerCalls::rewardsInitiator) + } + rewardsInitiator + }, + { + fn createAVSRewardsSubmission( + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + ::abi_decode_raw( + data, + validate, + ) + .map(EigenDAServiceManagerCalls::createAVSRewardsSubmission) + } + createAVSRewardsSubmission + }, + ]; + let Ok(idx) = Self::SELECTORS.binary_search(&selector) else { + return Err(alloy_sol_types::Error::unknown_selector( + ::NAME, + selector, + )); + }; + (unsafe { DECODE_SHIMS.get_unchecked(idx) })(data, validate) + } + #[inline] + fn abi_encoded_size(&self) -> usize { + match self { + Self::BLOCK_STALE_MEASURE(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::STORE_DURATION_BLOCKS(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::THRESHOLD_DENOMINATOR(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::avsDirectory(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::batchId(inner) => { + ::abi_encoded_size(inner) + } + Self::batchIdToBatchMetadataHash(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::blsApkRegistry(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::checkSignatures(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::confirmBatch(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::createAVSRewardsSubmission(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::delegation(inner) => { + ::abi_encoded_size(inner) + } + Self::deregisterOperatorFromAVS(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::getOperatorRestakedStrategies(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::getRestakeableStrategies(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::initialize(inner) => { + ::abi_encoded_size(inner) + } + Self::isBatchConfirmer(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::latestServeUntilBlock(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::owner(inner) => { + ::abi_encoded_size(inner) + } + Self::pause(inner) => { + ::abi_encoded_size(inner) + } + Self::pauseAll(inner) => { + ::abi_encoded_size(inner) + } + Self::paused_0(inner) => { + ::abi_encoded_size(inner) + } + Self::paused_1(inner) => { + ::abi_encoded_size(inner) + } + Self::pauserRegistry(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::quorumAdversaryThresholdPercentages(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::quorumConfirmationThresholdPercentages(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::quorumNumbersRequired(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::registerOperatorToAVS(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::registryCoordinator(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::renounceOwnership(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::rewardsInitiator(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::setBatchConfirmer(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::setPauserRegistry(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::setRewardsInitiator(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::setStaleStakesForbidden(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::stakeRegistry(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::staleStakesForbidden(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::taskNumber(inner) => { + ::abi_encoded_size(inner) + } + Self::transferOwnership(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::trySignatureAndApkVerification(inner) => { + ::abi_encoded_size( + inner, + ) + } + Self::unpause(inner) => { + ::abi_encoded_size(inner) + } + Self::updateAVSMetadataURI(inner) => { + ::abi_encoded_size( + inner, + ) + } + } + } + #[inline] + fn abi_encode_raw(&self, out: &mut alloy_sol_types::private::Vec) { + match self { + Self::BLOCK_STALE_MEASURE(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::STORE_DURATION_BLOCKS(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::THRESHOLD_DENOMINATOR(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::avsDirectory(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::batchId(inner) => { + ::abi_encode_raw(inner, out) + } + Self::batchIdToBatchMetadataHash(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::blsApkRegistry(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::checkSignatures(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::confirmBatch(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::createAVSRewardsSubmission(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::delegation(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::deregisterOperatorFromAVS(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::getOperatorRestakedStrategies(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::getRestakeableStrategies(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::initialize(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::isBatchConfirmer(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::latestServeUntilBlock(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::owner(inner) => { + ::abi_encode_raw(inner, out) + } + Self::pause(inner) => { + ::abi_encode_raw(inner, out) + } + Self::pauseAll(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::paused_0(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::paused_1(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::pauserRegistry(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::quorumAdversaryThresholdPercentages(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::quorumConfirmationThresholdPercentages(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::quorumNumbersRequired(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::registerOperatorToAVS(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::registryCoordinator(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::renounceOwnership(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::rewardsInitiator(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::setBatchConfirmer(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::setPauserRegistry(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::setRewardsInitiator(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::setStaleStakesForbidden(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::stakeRegistry(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::staleStakesForbidden(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::taskNumber(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::transferOwnership(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::trySignatureAndApkVerification(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + Self::unpause(inner) => { + ::abi_encode_raw(inner, out) + } + Self::updateAVSMetadataURI(inner) => { + ::abi_encode_raw( + inner, + out, + ) + } + } + } + } + ///Container for all the [`EigenDAServiceManager`](self) events. + pub enum EigenDAServiceManagerEvents { + BatchConfirmed(BatchConfirmed), + BatchConfirmerStatusChanged(BatchConfirmerStatusChanged), + Initialized(Initialized), + OwnershipTransferred(OwnershipTransferred), + Paused(Paused), + PauserRegistrySet(PauserRegistrySet), + RewardsInitiatorUpdated(RewardsInitiatorUpdated), + StaleStakesForbiddenUpdate(StaleStakesForbiddenUpdate), + Unpaused(Unpaused), + } + #[automatically_derived] + impl EigenDAServiceManagerEvents { + /// All the selectors of this enum. + /// + /// Note that the selectors might not be in the same order as the variants. + /// No guarantees are made about the order of the selectors. + /// + /// Prefer using `SolInterface` methods instead. + pub const SELECTORS: &'static [[u8; 32usize]] = &[ + [ + 53u8, 130u8, 209u8, 130u8, 142u8, 38u8, 191u8, 86u8, 189u8, 128u8, 21u8, 2u8, + 188u8, 2u8, 26u8, 192u8, 188u8, 138u8, 251u8, 87u8, 200u8, 38u8, 228u8, 152u8, + 107u8, 69u8, 89u8, 60u8, 143u8, 173u8, 56u8, 156u8, + ], + [ + 64u8, 228u8, 237u8, 136u8, 10u8, 41u8, 224u8, 246u8, 221u8, 206u8, 48u8, 116u8, + 87u8, 251u8, 117u8, 205u8, 223u8, 79u8, 238u8, 247u8, 211u8, 236u8, 176u8, 48u8, + 27u8, 253u8, 244u8, 151u8, 106u8, 14u8, 45u8, 252u8, + ], + [ + 92u8, 50u8, 101u8, 245u8, 251u8, 70u8, 46u8, 244u8, 147u8, 15u8, 228u8, 123u8, + 234u8, 161u8, 131u8, 100u8, 124u8, 151u8, 241u8, 155u8, 165u8, 69u8, 183u8, 97u8, + 244u8, 27u8, 200u8, 205u8, 70u8, 33u8, 212u8, 20u8, + ], + [ + 110u8, 159u8, 205u8, 83u8, 152u8, 150u8, 252u8, 166u8, 14u8, 139u8, 15u8, 1u8, + 221u8, 88u8, 2u8, 51u8, 228u8, 138u8, 107u8, 15u8, 125u8, 240u8, 19u8, 184u8, + 155u8, 167u8, 245u8, 101u8, 134u8, 154u8, 205u8, 182u8, + ], + [ + 127u8, 38u8, 184u8, 63u8, 249u8, 110u8, 31u8, 43u8, 106u8, 104u8, 47u8, 19u8, 56u8, + 82u8, 246u8, 121u8, 138u8, 9u8, 196u8, 101u8, 218u8, 149u8, 146u8, 20u8, 96u8, + 206u8, 251u8, 56u8, 71u8, 64u8, 36u8, 152u8, + ], + [ + 139u8, 224u8, 7u8, 156u8, 83u8, 22u8, 89u8, 20u8, 19u8, 68u8, 205u8, 31u8, 208u8, + 164u8, 242u8, 132u8, 25u8, 73u8, 127u8, 151u8, 34u8, 163u8, 218u8, 175u8, 227u8, + 180u8, 24u8, 111u8, 107u8, 100u8, 87u8, 224u8, + ], + [ + 171u8, 64u8, 163u8, 116u8, 188u8, 81u8, 222u8, 55u8, 34u8, 0u8, 168u8, 188u8, + 152u8, 26u8, 248u8, 201u8, 236u8, 220u8, 8u8, 223u8, 218u8, 239u8, 11u8, 182u8, + 224u8, 159u8, 136u8, 243u8, 198u8, 22u8, 239u8, 61u8, + ], + [ + 199u8, 85u8, 87u8, 196u8, 173u8, 73u8, 105u8, 126u8, 35u8, 20u8, 73u8, 104u8, + 139u8, 225u8, 62u8, 241u8, 28u8, 182u8, 190u8, 142u8, 208u8, 209u8, 136u8, 25u8, + 216u8, 221u8, 224u8, 116u8, 165u8, 161u8, 111u8, 138u8, + ], + [ + 225u8, 28u8, 221u8, 241u8, 129u8, 106u8, 67u8, 49u8, 140u8, 161u8, 117u8, 187u8, + 197u8, 44u8, 208u8, 24u8, 84u8, 54u8, 233u8, 203u8, 234u8, 215u8, 200u8, 58u8, + 204u8, 84u8, 167u8, 62u8, 70u8, 23u8, 23u8, 227u8, + ], + ]; + } + #[automatically_derived] + impl alloy_sol_types::SolEventInterface for EigenDAServiceManagerEvents { + const NAME: &'static str = "EigenDAServiceManagerEvents"; + const COUNT: usize = 9usize; + fn decode_raw_log( + topics: &[alloy_sol_types::Word], + data: &[u8], + validate: bool, + ) -> alloy_sol_types::Result { + match topics.first().copied() { + Some(::SIGNATURE_HASH) => { + ::decode_raw_log( + topics, data, validate, + ) + .map(Self::BatchConfirmed) + } + Some( + ::SIGNATURE_HASH, + ) => ::decode_raw_log( + topics, data, validate, + ) + .map(Self::BatchConfirmerStatusChanged), + Some(::SIGNATURE_HASH) => { + ::decode_raw_log( + topics, data, validate, + ) + .map(Self::Initialized) + } + Some(::SIGNATURE_HASH) => { + ::decode_raw_log( + topics, data, validate, + ) + .map(Self::OwnershipTransferred) + } + Some(::SIGNATURE_HASH) => { + ::decode_raw_log(topics, data, validate) + .map(Self::Paused) + } + Some(::SIGNATURE_HASH) => { + ::decode_raw_log( + topics, data, validate, + ) + .map(Self::PauserRegistrySet) + } + Some(::SIGNATURE_HASH) => { + ::decode_raw_log( + topics, data, validate, + ) + .map(Self::RewardsInitiatorUpdated) + } + Some(::SIGNATURE_HASH) => { + ::decode_raw_log( + topics, data, validate, + ) + .map(Self::StaleStakesForbiddenUpdate) + } + Some(::SIGNATURE_HASH) => { + ::decode_raw_log(topics, data, validate) + .map(Self::Unpaused) + } + _ => alloy_sol_types::private::Err(alloy_sol_types::Error::InvalidLog { + name: ::NAME, + log: alloy_sol_types::private::Box::new( + alloy_sol_types::private::LogData::new_unchecked( + topics.to_vec(), + data.to_vec().into(), + ), + ), + }), + } + } + } + #[automatically_derived] + impl alloy_sol_types::private::IntoLogData for EigenDAServiceManagerEvents { + fn to_log_data(&self) -> alloy_sol_types::private::LogData { + match self { + Self::BatchConfirmed(inner) => { + alloy_sol_types::private::IntoLogData::to_log_data(inner) + } + Self::BatchConfirmerStatusChanged(inner) => { + alloy_sol_types::private::IntoLogData::to_log_data(inner) + } + Self::Initialized(inner) => { + alloy_sol_types::private::IntoLogData::to_log_data(inner) + } + Self::OwnershipTransferred(inner) => { + alloy_sol_types::private::IntoLogData::to_log_data(inner) + } + Self::Paused(inner) => alloy_sol_types::private::IntoLogData::to_log_data(inner), + Self::PauserRegistrySet(inner) => { + alloy_sol_types::private::IntoLogData::to_log_data(inner) + } + Self::RewardsInitiatorUpdated(inner) => { + alloy_sol_types::private::IntoLogData::to_log_data(inner) + } + Self::StaleStakesForbiddenUpdate(inner) => { + alloy_sol_types::private::IntoLogData::to_log_data(inner) + } + Self::Unpaused(inner) => alloy_sol_types::private::IntoLogData::to_log_data(inner), + } + } + fn into_log_data(self) -> alloy_sol_types::private::LogData { + match self { + Self::BatchConfirmed(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + Self::BatchConfirmerStatusChanged(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + Self::Initialized(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + Self::OwnershipTransferred(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + Self::Paused(inner) => alloy_sol_types::private::IntoLogData::into_log_data(inner), + Self::PauserRegistrySet(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + Self::RewardsInitiatorUpdated(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + Self::StaleStakesForbiddenUpdate(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + Self::Unpaused(inner) => { + alloy_sol_types::private::IntoLogData::into_log_data(inner) + } + } + } + } + use alloy::contract as alloy_contract; + /**Creates a new wrapper around an on-chain [`EigenDAServiceManager`](self) contract instance. + + See the [wrapper's documentation](`EigenDAServiceManagerInstance`) for more details.*/ + #[inline] + pub const fn new< + T: alloy_contract::private::Transport + ::core::clone::Clone, + P: alloy_contract::private::Provider, + N: alloy_contract::private::Network, + >( + address: alloy_sol_types::private::Address, + provider: P, + ) -> EigenDAServiceManagerInstance { + EigenDAServiceManagerInstance::::new(address, provider) + } + /**Deploys this contract using the given `provider` and constructor arguments, if any. + + Returns a new instance of the contract, if the deployment was successful. + + For more fine-grained control over the deployment process, use [`deploy_builder`] instead.*/ + #[inline] + pub fn deploy< + T: alloy_contract::private::Transport + ::core::clone::Clone, + P: alloy_contract::private::Provider, + N: alloy_contract::private::Network, + >( + provider: P, + __avsDirectory: alloy::sol_types::private::Address, + __rewardsCoordinator: alloy::sol_types::private::Address, + __registryCoordinator: alloy::sol_types::private::Address, + __stakeRegistry: alloy::sol_types::private::Address, + ) -> impl ::core::future::Future< + Output = alloy_contract::Result>, + > { + EigenDAServiceManagerInstance::::deploy( + provider, + __avsDirectory, + __rewardsCoordinator, + __registryCoordinator, + __stakeRegistry, + ) + } + /**Creates a `RawCallBuilder` for deploying this contract using the given `provider` + and constructor arguments, if any. + + This is a simple wrapper around creating a `RawCallBuilder` with the data set to + the bytecode concatenated with the constructor's ABI-encoded arguments.*/ + #[inline] + pub fn deploy_builder< + T: alloy_contract::private::Transport + ::core::clone::Clone, + P: alloy_contract::private::Provider, + N: alloy_contract::private::Network, + >( + provider: P, + __avsDirectory: alloy::sol_types::private::Address, + __rewardsCoordinator: alloy::sol_types::private::Address, + __registryCoordinator: alloy::sol_types::private::Address, + __stakeRegistry: alloy::sol_types::private::Address, + ) -> alloy_contract::RawCallBuilder { + EigenDAServiceManagerInstance::::deploy_builder( + provider, + __avsDirectory, + __rewardsCoordinator, + __registryCoordinator, + __stakeRegistry, + ) + } + /**A [`EigenDAServiceManager`](self) instance. + + Contains type-safe methods for interacting with an on-chain instance of the + [`EigenDAServiceManager`](self) contract located at a given `address`, using a given + provider `P`. + + If the contract bytecode is available (see the [`sol!`](alloy_sol_types::sol!) + documentation on how to provide it), the `deploy` and `deploy_builder` methods can + be used to deploy a new instance of the contract. + + See the [module-level documentation](self) for all the available methods.*/ + #[derive(Clone)] + pub struct EigenDAServiceManagerInstance { + address: alloy_sol_types::private::Address, + provider: P, + _network_transport: ::core::marker::PhantomData<(N, T)>, + } + #[automatically_derived] + impl ::core::fmt::Debug for EigenDAServiceManagerInstance { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("EigenDAServiceManagerInstance") + .field(&self.address) + .finish() + } + } + /// Instantiation and getters/setters. + #[automatically_derived] + impl< + T: alloy_contract::private::Transport + ::core::clone::Clone, + P: alloy_contract::private::Provider, + N: alloy_contract::private::Network, + > EigenDAServiceManagerInstance + { + /**Creates a new wrapper around an on-chain [`EigenDAServiceManager`](self) contract instance. + + See the [wrapper's documentation](`EigenDAServiceManagerInstance`) for more details.*/ + #[inline] + pub const fn new(address: alloy_sol_types::private::Address, provider: P) -> Self { + Self { + address, + provider, + _network_transport: ::core::marker::PhantomData, + } + } + /**Deploys this contract using the given `provider` and constructor arguments, if any. + + Returns a new instance of the contract, if the deployment was successful. + + For more fine-grained control over the deployment process, use [`deploy_builder`] instead.*/ + #[inline] + pub async fn deploy( + provider: P, + __avsDirectory: alloy::sol_types::private::Address, + __rewardsCoordinator: alloy::sol_types::private::Address, + __registryCoordinator: alloy::sol_types::private::Address, + __stakeRegistry: alloy::sol_types::private::Address, + ) -> alloy_contract::Result> { + let call_builder = Self::deploy_builder( + provider, + __avsDirectory, + __rewardsCoordinator, + __registryCoordinator, + __stakeRegistry, + ); + let contract_address = call_builder.deploy().await?; + Ok(Self::new(contract_address, call_builder.provider)) + } + /**Creates a `RawCallBuilder` for deploying this contract using the given `provider` + and constructor arguments, if any. + + This is a simple wrapper around creating a `RawCallBuilder` with the data set to + the bytecode concatenated with the constructor's ABI-encoded arguments.*/ + #[inline] + pub fn deploy_builder( + provider: P, + __avsDirectory: alloy::sol_types::private::Address, + __rewardsCoordinator: alloy::sol_types::private::Address, + __registryCoordinator: alloy::sol_types::private::Address, + __stakeRegistry: alloy::sol_types::private::Address, + ) -> alloy_contract::RawCallBuilder { + alloy_contract::RawCallBuilder::new_raw_deploy( + provider, + [ + &BYTECODE[..], + &alloy_sol_types::SolConstructor::abi_encode(&constructorCall { + __avsDirectory, + __rewardsCoordinator, + __registryCoordinator, + __stakeRegistry, + })[..], + ] + .concat() + .into(), + ) + } + /// Returns a reference to the address. + #[inline] + pub const fn address(&self) -> &alloy_sol_types::private::Address { + &self.address + } + /// Sets the address. + #[inline] + pub fn set_address(&mut self, address: alloy_sol_types::private::Address) { + self.address = address; + } + /// Sets the address and returns `self`. + pub fn at(mut self, address: alloy_sol_types::private::Address) -> Self { + self.set_address(address); + self + } + /// Returns a reference to the provider. + #[inline] + pub const fn provider(&self) -> &P { + &self.provider + } + } + impl EigenDAServiceManagerInstance { + /// Clones the provider and returns a new instance with the cloned provider. + #[inline] + pub fn with_cloned_provider(self) -> EigenDAServiceManagerInstance { + EigenDAServiceManagerInstance { + address: self.address, + provider: ::core::clone::Clone::clone(&self.provider), + _network_transport: ::core::marker::PhantomData, + } + } + } + /// Function calls. + #[automatically_derived] + impl< + T: alloy_contract::private::Transport + ::core::clone::Clone, + P: alloy_contract::private::Provider, + N: alloy_contract::private::Network, + > EigenDAServiceManagerInstance + { + /// Creates a new call builder using this contract instance's provider and address. + /// + /// Note that the call can be any function call, not just those defined in this + /// contract. Prefer using the other methods for building type-safe contract calls. + pub fn call_builder( + &self, + call: &C, + ) -> alloy_contract::SolCallBuilder { + alloy_contract::SolCallBuilder::new_sol(&self.provider, &self.address, call) + } + ///Creates a new call builder for the [`BLOCK_STALE_MEASURE`] function. + pub fn BLOCK_STALE_MEASURE( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&BLOCK_STALE_MEASURECall {}) + } + ///Creates a new call builder for the [`STORE_DURATION_BLOCKS`] function. + pub fn STORE_DURATION_BLOCKS( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&STORE_DURATION_BLOCKSCall {}) + } + ///Creates a new call builder for the [`THRESHOLD_DENOMINATOR`] function. + pub fn THRESHOLD_DENOMINATOR( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&THRESHOLD_DENOMINATORCall {}) + } + ///Creates a new call builder for the [`avsDirectory`] function. + pub fn avsDirectory(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&avsDirectoryCall {}) + } + ///Creates a new call builder for the [`batchId`] function. + pub fn batchId(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&batchIdCall {}) + } + ///Creates a new call builder for the [`batchIdToBatchMetadataHash`] function. + pub fn batchIdToBatchMetadataHash( + &self, + _0: u32, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&batchIdToBatchMetadataHashCall { _0 }) + } + ///Creates a new call builder for the [`blsApkRegistry`] function. + pub fn blsApkRegistry( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&blsApkRegistryCall {}) + } + ///Creates a new call builder for the [`checkSignatures`] function. + pub fn checkSignatures( + &self, + msgHash: alloy::sol_types::private::FixedBytes<32>, + quorumNumbers: alloy::sol_types::private::Bytes, + referenceBlockNumber: u32, + params: ::RustType, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&checkSignaturesCall { + msgHash, + quorumNumbers, + referenceBlockNumber, + params, + }) + } + ///Creates a new call builder for the [`confirmBatch`] function. + pub fn confirmBatch( + &self, + batchHeader: ::RustType, + nonSignerStakesAndSignature: ::RustType, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&confirmBatchCall { + batchHeader, + nonSignerStakesAndSignature, + }) + } + ///Creates a new call builder for the [`createAVSRewardsSubmission`] function. + pub fn createAVSRewardsSubmission( + &self, + rewardsSubmissions: alloy::sol_types::private::Vec< + ::RustType, + >, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&createAVSRewardsSubmissionCall { rewardsSubmissions }) + } + ///Creates a new call builder for the [`delegation`] function. + pub fn delegation(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&delegationCall {}) + } + ///Creates a new call builder for the [`deregisterOperatorFromAVS`] function. + pub fn deregisterOperatorFromAVS( + &self, + operator: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&deregisterOperatorFromAVSCall { operator }) + } + ///Creates a new call builder for the [`getOperatorRestakedStrategies`] function. + pub fn getOperatorRestakedStrategies( + &self, + operator: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&getOperatorRestakedStrategiesCall { operator }) + } + ///Creates a new call builder for the [`getRestakeableStrategies`] function. + pub fn getRestakeableStrategies( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&getRestakeableStrategiesCall {}) + } + ///Creates a new call builder for the [`initialize`] function. + pub fn initialize( + &self, + _pauserRegistry: alloy::sol_types::private::Address, + _initialPausedStatus: alloy::sol_types::private::U256, + _initialOwner: alloy::sol_types::private::Address, + _batchConfirmers: alloy::sol_types::private::Vec, + _rewardsInitiator: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&initializeCall { + _pauserRegistry, + _initialPausedStatus, + _initialOwner, + _batchConfirmers, + _rewardsInitiator, + }) + } + ///Creates a new call builder for the [`isBatchConfirmer`] function. + pub fn isBatchConfirmer( + &self, + _0: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&isBatchConfirmerCall { _0 }) + } + ///Creates a new call builder for the [`latestServeUntilBlock`] function. + pub fn latestServeUntilBlock( + &self, + referenceBlockNumber: u32, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&latestServeUntilBlockCall { + referenceBlockNumber, + }) + } + ///Creates a new call builder for the [`owner`] function. + pub fn owner(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&ownerCall {}) + } + ///Creates a new call builder for the [`pause`] function. + pub fn pause( + &self, + newPausedStatus: alloy::sol_types::private::U256, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&pauseCall { newPausedStatus }) + } + ///Creates a new call builder for the [`pauseAll`] function. + pub fn pauseAll(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&pauseAllCall {}) + } + ///Creates a new call builder for the [`paused_0`] function. + pub fn paused_0( + &self, + index: u8, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&paused_0Call { index }) + } + ///Creates a new call builder for the [`paused_1`] function. + pub fn paused_1(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&paused_1Call {}) + } + ///Creates a new call builder for the [`pauserRegistry`] function. + pub fn pauserRegistry( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&pauserRegistryCall {}) + } + ///Creates a new call builder for the [`quorumAdversaryThresholdPercentages`] function. + pub fn quorumAdversaryThresholdPercentages( + &self, + ) -> alloy_contract::SolCallBuilder + { + self.call_builder(&quorumAdversaryThresholdPercentagesCall {}) + } + ///Creates a new call builder for the [`quorumConfirmationThresholdPercentages`] function. + pub fn quorumConfirmationThresholdPercentages( + &self, + ) -> alloy_contract::SolCallBuilder + { + self.call_builder(&quorumConfirmationThresholdPercentagesCall {}) + } + ///Creates a new call builder for the [`quorumNumbersRequired`] function. + pub fn quorumNumbersRequired( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&quorumNumbersRequiredCall {}) + } + ///Creates a new call builder for the [`registerOperatorToAVS`] function. + pub fn registerOperatorToAVS( + &self, + operator: alloy::sol_types::private::Address, + operatorSignature: ::RustType, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(®isterOperatorToAVSCall { + operator, + operatorSignature, + }) + } + ///Creates a new call builder for the [`registryCoordinator`] function. + pub fn registryCoordinator( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(®istryCoordinatorCall {}) + } + ///Creates a new call builder for the [`renounceOwnership`] function. + pub fn renounceOwnership( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&renounceOwnershipCall {}) + } + ///Creates a new call builder for the [`rewardsInitiator`] function. + pub fn rewardsInitiator( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&rewardsInitiatorCall {}) + } + ///Creates a new call builder for the [`setBatchConfirmer`] function. + pub fn setBatchConfirmer( + &self, + _batchConfirmer: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&setBatchConfirmerCall { _batchConfirmer }) + } + ///Creates a new call builder for the [`setPauserRegistry`] function. + pub fn setPauserRegistry( + &self, + newPauserRegistry: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&setPauserRegistryCall { newPauserRegistry }) + } + ///Creates a new call builder for the [`setRewardsInitiator`] function. + pub fn setRewardsInitiator( + &self, + newRewardsInitiator: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&setRewardsInitiatorCall { + newRewardsInitiator, + }) + } + ///Creates a new call builder for the [`setStaleStakesForbidden`] function. + pub fn setStaleStakesForbidden( + &self, + value: bool, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&setStaleStakesForbiddenCall { value }) + } + ///Creates a new call builder for the [`stakeRegistry`] function. + pub fn stakeRegistry(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&stakeRegistryCall {}) + } + ///Creates a new call builder for the [`staleStakesForbidden`] function. + pub fn staleStakesForbidden( + &self, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&staleStakesForbiddenCall {}) + } + ///Creates a new call builder for the [`taskNumber`] function. + pub fn taskNumber(&self) -> alloy_contract::SolCallBuilder { + self.call_builder(&taskNumberCall {}) + } + ///Creates a new call builder for the [`transferOwnership`] function. + pub fn transferOwnership( + &self, + newOwner: alloy::sol_types::private::Address, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&transferOwnershipCall { newOwner }) + } + ///Creates a new call builder for the [`trySignatureAndApkVerification`] function. + pub fn trySignatureAndApkVerification( + &self, + msgHash: alloy::sol_types::private::FixedBytes<32>, + apk: ::RustType, + apkG2: ::RustType, + sigma: ::RustType, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&trySignatureAndApkVerificationCall { + msgHash, + apk, + apkG2, + sigma, + }) + } + ///Creates a new call builder for the [`unpause`] function. + pub fn unpause( + &self, + newPausedStatus: alloy::sol_types::private::U256, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&unpauseCall { newPausedStatus }) + } + ///Creates a new call builder for the [`updateAVSMetadataURI`] function. + pub fn updateAVSMetadataURI( + &self, + _metadataURI: alloy::sol_types::private::String, + ) -> alloy_contract::SolCallBuilder { + self.call_builder(&updateAVSMetadataURICall { _metadataURI }) + } + } + /// Event filters. + #[automatically_derived] + impl< + T: alloy_contract::private::Transport + ::core::clone::Clone, + P: alloy_contract::private::Provider, + N: alloy_contract::private::Network, + > EigenDAServiceManagerInstance + { + /// Creates a new event filter using this contract instance's provider and address. + /// + /// Note that the type can be any event, not just those defined in this contract. + /// Prefer using the other methods for building type-safe event filters. + pub fn event_filter( + &self, + ) -> alloy_contract::Event { + alloy_contract::Event::new_sol(&self.provider, &self.address) + } + ///Creates a new event filter for the [`BatchConfirmed`] event. + pub fn BatchConfirmed_filter(&self) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`BatchConfirmerStatusChanged`] event. + pub fn BatchConfirmerStatusChanged_filter( + &self, + ) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`Initialized`] event. + pub fn Initialized_filter(&self) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`OwnershipTransferred`] event. + pub fn OwnershipTransferred_filter( + &self, + ) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`Paused`] event. + pub fn Paused_filter(&self) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`PauserRegistrySet`] event. + pub fn PauserRegistrySet_filter( + &self, + ) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`RewardsInitiatorUpdated`] event. + pub fn RewardsInitiatorUpdated_filter( + &self, + ) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`StaleStakesForbiddenUpdate`] event. + pub fn StaleStakesForbiddenUpdate_filter( + &self, + ) -> alloy_contract::Event { + self.event_filter::() + } + ///Creates a new event filter for the [`Unpaused`] event. + pub fn Unpaused_filter(&self) -> alloy_contract::Event { + self.event_filter::() + } + } +} diff --git a/core/node/da_clients/src/eigen/generated/mod.rs b/core/node/da_clients/src/eigen/generated/mod.rs new file mode 100644 index 000000000000..7d60a2c10678 --- /dev/null +++ b/core/node/da_clients/src/eigen/generated/mod.rs @@ -0,0 +1 @@ +pub mod eigendaservicemanager; diff --git a/core/node/da_clients/src/eigen/memstore.rs b/core/node/da_clients/src/eigen/memstore.rs new file mode 100644 index 000000000000..8a10137a2b93 --- /dev/null +++ b/core/node/da_clients/src/eigen/memstore.rs @@ -0,0 +1,221 @@ +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, + time::{Duration, Instant}, +}; + +use anyhow::Error; +use rand::{rngs::OsRng, Rng, RngCore}; +use sha3::{Digest, Keccak256}; +use tokio::time::interval; +use zksync_config::configs::da_client::eigen::MemStoreConfig; +use zksync_da_client::types::{DAError, InclusionData}; + +use super::blob_info::{self, BlobInfo}; + +#[derive(Debug, PartialEq)] +pub enum MemStoreError { + BlobToLarge, + BlobAlreadyExists, + IncorrectCommitment, + #[cfg(test)] + BlobNotFound, +} + +impl From for Error { + fn from(val: MemStoreError) -> Self { + match val { + MemStoreError::BlobToLarge => Error::msg("Blob too large"), + MemStoreError::BlobAlreadyExists => Error::msg("Blob already exists"), + MemStoreError::IncorrectCommitment => Error::msg("Incorrect commitment"), + #[cfg(test)] + MemStoreError::BlobNotFound => Error::msg("Blob not found"), + } + } +} + +#[derive(Debug)] +struct MemStoreData { + store: HashMap>, + key_starts: HashMap, +} + +/// This struct represents a memory store for blobs. +/// It should be used for testing purposes only. +#[derive(Clone, Debug)] +pub struct MemStore { + pub config: MemStoreConfig, + data: Arc>, +} + +impl MemStore { + pub fn new(config: MemStoreConfig) -> Arc { + let memstore = Arc::new(Self { + config, + data: Arc::new(RwLock::new(MemStoreData { + store: HashMap::new(), + key_starts: HashMap::new(), + })), + }); + let store_clone = Arc::clone(&memstore); + tokio::spawn(async move { + store_clone.pruning_loop().await; + }); + memstore + } + + /// Saves a blob to the memory store, it harcodes the blob info, since we don't care about it in a memory based store + pub async fn put_blob(self: Arc, value: Vec) -> Result { + tokio::time::sleep(Duration::from_millis(self.config.put_latency)).await; + if value.len() as u64 > self.config.max_blob_size_bytes { + return Err(MemStoreError::BlobToLarge); + } + + let mut entropy = [0u8; 10]; + OsRng.fill_bytes(&mut entropy); + + let mut hasher = Keccak256::new(); + hasher.update(entropy); + let mock_batch_root = hasher.finalize().to_vec(); + + let block_num = OsRng.gen_range(0u32..1000); + + let blob_info = blob_info::BlobInfo { + blob_header: blob_info::BlobHeader { + commitment: blob_info::G1Commitment { + // todo: generate real commitment + x: vec![0u8; 32], + y: vec![0u8; 32], + }, + data_length: value.len() as u32, + blob_quorum_params: vec![blob_info::BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 29, + confirmation_threshold_percentage: 30, + chunk_length: 300, + }], + }, + blob_verification_proof: blob_info::BlobVerificationProof { + batch_medatada: blob_info::BatchMetadata { + batch_header: blob_info::BatchHeader { + batch_root: mock_batch_root.clone(), + quorum_numbers: vec![0x1, 0x0], + quorum_signed_percentages: vec![0x60, 0x90], + reference_block_number: block_num, + }, + signatory_record_hash: mock_batch_root, + fee: vec![], + confirmation_block_number: block_num, + batch_header_hash: vec![], + }, + batch_id: 69, + blob_index: 420, + inclusion_proof: entropy.to_vec(), + quorum_indexes: vec![0x1, 0x0], + }, + }; + + let cert_bytes = rlp::encode(&blob_info).to_vec(); + + let key = String::from_utf8_lossy( + blob_info + .blob_verification_proof + .inclusion_proof + .clone() + .as_slice(), + ) + .to_string(); + + let mut data = self.data.write().unwrap(); + + if data.store.contains_key(key.as_str()) { + return Err(MemStoreError::BlobAlreadyExists); + } + + data.key_starts.insert(key.clone(), Instant::now()); + data.store.insert(key, value); + Ok(hex::encode(cert_bytes)) + } + + /// It returns the inclusion proof + pub async fn get_inclusion_data( + self: Arc, + blob_id: &str, + ) -> anyhow::Result, DAError> { + let rlp_encoded_bytes = hex::decode(blob_id).map_err(|_| DAError { + error: MemStoreError::IncorrectCommitment.into(), + is_retriable: false, + })?; + let blob_info: BlobInfo = rlp::decode(&rlp_encoded_bytes).map_err(|_| DAError { + error: MemStoreError::IncorrectCommitment.into(), + is_retriable: false, + })?; + let inclusion_data = blob_info.blob_verification_proof.inclusion_proof; + Ok(Some(InclusionData { + data: inclusion_data, + })) + } + + /// This function is only used on tests, it returns the blob data + #[cfg(test)] + pub async fn get_blob_data( + self: Arc, + blob_id: &str, + ) -> anyhow::Result>, DAError> { + tokio::time::sleep(Duration::from_millis(self.config.get_latency)).await; + let request_id = hex::decode(blob_id).map_err(|_| DAError { + error: MemStoreError::IncorrectCommitment.into(), + is_retriable: false, + })?; + let blob_info: BlobInfo = rlp::decode(&request_id).map_err(|_| DAError { + error: MemStoreError::IncorrectCommitment.into(), + is_retriable: false, + })?; + let key = String::from_utf8_lossy( + blob_info + .blob_verification_proof + .inclusion_proof + .clone() + .as_slice(), + ) + .to_string(); + + let data = self.data.read().map_err(|_| DAError { + error: MemStoreError::BlobNotFound.into(), + is_retriable: false, + })?; + match data.store.get(&key) { + Some(value) => Ok(Some(value.clone())), + None => Err(DAError { + error: MemStoreError::BlobNotFound.into(), + is_retriable: false, + }), + } + } + + /// After some time has passed, blobs are removed from the store + async fn prune_expired(self: Arc) { + let mut data = self.data.write().unwrap(); + let mut to_remove = vec![]; + for (key, start) in data.key_starts.iter() { + if start.elapsed() > Duration::from_secs(self.config.blob_expiration) { + to_remove.push(key.clone()); + } + } + for key in to_remove { + data.store.remove(&key); + data.key_starts.remove(&key); + } + } + + /// Loop used to prune expired blobs + async fn pruning_loop(self: Arc) { + let mut interval = interval(Duration::from_secs(self.config.blob_expiration)); + + loop { + interval.tick().await; + let self_clone = Arc::clone(&self); + self_clone.prune_expired().await; + } + } +} diff --git a/core/node/da_clients/src/eigen/mod.rs b/core/node/da_clients/src/eigen/mod.rs index 699eae894246..02ec82a70815 100644 --- a/core/node/da_clients/src/eigen/mod.rs +++ b/core/node/da_clients/src/eigen/mod.rs @@ -1,5 +1,14 @@ +mod blob_info; mod client; +mod generated; +mod memstore; mod sdk; +mod verifier; + +use std::sync::Arc; + +use memstore::MemStore; +use sdk::RawEigenClient; pub use self::client::EigenClient; @@ -12,3 +21,9 @@ pub(crate) mod disperser { pub(crate) mod common { include!("generated/common.rs"); } + +#[derive(Clone, Debug)] +pub(crate) enum Disperser { + Remote(Arc), + Memory(Arc), +} diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 7ab7ea3ce33b..b7ae37260ff3 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -1,28 +1,37 @@ use std::{str::FromStr, time::Duration}; use secp256k1::{ecdsa::RecoverableSignature, SecretKey}; -use tokio::sync::mpsc; +use tokio::{sync::mpsc, time::Instant}; use tokio_stream::{wrappers::ReceiverStream, StreamExt}; use tonic::{ transport::{Channel, ClientTlsConfig, Endpoint}, Streaming, }; - +use zksync_config::configs::da_client::eigen::DisperserConfig; +#[cfg(test)] +use zksync_da_client::types::DAError; + +use super::{ + blob_info::BlobInfo, + disperser::BlobInfo as DisperserBlobInfo, + verifier::{Verifier, VerifierConfig}, +}; use crate::eigen::{ - disperser, + blob_info, disperser::{ + self, authenticated_request::Payload::{AuthenticationData, DisperseRequest}, disperser_client::DisperserClient, - AuthenticatedReply, BlobAuthHeader, BlobVerificationProof, DisperseBlobReply, + AuthenticatedReply, BlobAuthHeader, DisperseBlobReply, }, }; #[derive(Debug, Clone)] -pub struct RawEigenClient { +pub(crate) struct RawEigenClient { client: DisperserClient, - polling_interval: Duration, private_key: SecretKey, - account_id: String, + pub config: DisperserConfig, + verifier: Verifier, } pub(crate) const DATA_CHUNK_SIZE: usize = 32; @@ -30,32 +39,89 @@ 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 { + pub async fn new(private_key: SecretKey, config: DisperserConfig) -> anyhow::Result { let endpoint = - Endpoint::from_str(rpc_node_url.as_str())?.tls_config(ClientTlsConfig::new())?; + Endpoint::from_str(config.disperser_rpc.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); + let verifier_config = VerifierConfig { + verify_certs: true, + rpc_url: config.eigenda_eth_rpc.clone(), + svc_manager_addr: config.eigenda_svc_manager_address.clone(), + max_blob_size: config.blob_size_limit, + path_to_points: config.path_to_points.clone(), + eth_confirmation_depth: config.eth_confirmation_depth.max(0) as u32, + }; + let verifier = Verifier::new(verifier_config) + .map_err(|e| anyhow::anyhow!(format!("Failed to create verifier {:?}", e)))?; Ok(RawEigenClient { client, - polling_interval, private_key, - account_id, + config, + verifier, }) } - pub async fn dispatch_blob(&self, data: Vec) -> anyhow::Result { + async fn dispatch_blob_non_authenticated(&self, data: Vec) -> anyhow::Result { + let padded_data = convert_by_padding_empty_byte(&data); + let request = disperser::DisperseBlobRequest { + data: padded_data, + custom_quorum_numbers: vec![], + account_id: String::default(), // Account Id is not used in non-authenticated mode + }; + + let mut client_clone = self.client.clone(); + let disperse_reply = client_clone.disperse_blob(request).await?.into_inner(); + + let disperse_time = Instant::now(); + let blob_info = self + .await_for_inclusion(client_clone, disperse_reply) + .await?; + let disperse_elapsed = Instant::now() - disperse_time; + + let blob_info = blob_info::BlobInfo::try_from(blob_info) + .map_err(|e| anyhow::anyhow!("Failed to convert blob info: {}", e))?; + self.verifier + .verify_commitment(blob_info.blob_header.commitment.clone(), data) + .map_err(|_| anyhow::anyhow!("Failed to verify commitment"))?; + + self.loop_verify_certificate(blob_info.clone(), disperse_elapsed) + .await?; + let verification_proof = blob_info.blob_verification_proof.clone(); + let blob_id = format!( + "{}:{}", + verification_proof.batch_id, verification_proof.blob_index + ); + tracing::info!("Blob dispatch confirmed, blob id: {}", blob_id); + + Ok(hex::encode(rlp::encode(&blob_info))) + } + + async fn loop_verify_certificate( + &self, + blob_info: BlobInfo, + disperse_elapsed: Duration, + ) -> anyhow::Result<()> { + let start = Instant::now(); + while Instant::now() - start + < (Duration::from_millis(self.config.status_query_timeout) - disperse_elapsed) + { + tokio::time::sleep(Duration::from_secs(12)).await; // avg block time + match self.verifier.verify_certificate(blob_info.clone()).await { + Ok(_) => return Ok(()), + Err(_) => continue, + } + } + Err(anyhow::anyhow!("Failed to validate certificate")) + } + + async fn dispatch_blob_authenticated(&self, data: Vec) -> anyhow::Result { let mut client_clone = self.client.clone(); let (tx, rx) = mpsc::channel(Self::BUFFER_SIZE); + let disperse_time = Instant::now(); let response_stream = client_clone.disperse_blob_authenticated(ReceiverStream::new(rx)); let padded_data = convert_by_padding_empty_byte(&data); @@ -86,16 +152,35 @@ impl RawEigenClient { }; // 5. poll for blob status until it reaches the Confirmed state - let verification_proof = self + let blob_info = self .await_for_inclusion(client_clone, disperse_reply) .await?; + + let blob_info = blob_info::BlobInfo::try_from(blob_info) + .map_err(|e| anyhow::anyhow!("Failed to convert blob info: {}", e))?; + + let disperse_elapsed = Instant::now() - disperse_time; + self.verifier + .verify_commitment(blob_info.blob_header.commitment.clone(), data) + .map_err(|_| anyhow::anyhow!("Failed to verify commitment"))?; + + self.loop_verify_certificate(blob_info.clone(), disperse_elapsed) + .await?; + + let verification_proof = blob_info.blob_verification_proof.clone(); let blob_id = format!( "{}:{}", verification_proof.batch_id, verification_proof.blob_index ); tracing::info!("Blob dispatch confirmed, blob id: {}", blob_id); + Ok(hex::encode(rlp::encode(&blob_info))) + } - Ok(blob_id) + pub async fn dispatch_blob(&self, data: Vec) -> anyhow::Result { + match self.config.authenticated { + true => self.dispatch_blob_authenticated(data).await, + false => self.dispatch_blob_non_authenticated(data).await, + } } async fn disperse_data( @@ -107,7 +192,7 @@ impl RawEigenClient { payload: Some(DisperseRequest(disperser::DisperseBlobRequest { data, custom_quorum_numbers: vec![], - account_id: self.account_id.clone(), + account_id: get_account_id(&self.private_key), })), }; @@ -175,13 +260,15 @@ impl RawEigenClient { &self, mut client: DisperserClient, disperse_blob_reply: DisperseBlobReply, - ) -> anyhow::Result { + ) -> anyhow::Result { let polling_request = disperser::BlobStatusRequest { request_id: disperse_blob_reply.request_id, }; - loop { - tokio::time::sleep(self.polling_interval).await; + let start_time = Instant::now(); + while Instant::now() - start_time < Duration::from_millis(self.config.status_query_timeout) + { + tokio::time::sleep(Duration::from_millis(self.config.status_query_interval)).await; let resp = client .get_blob_status(polling_request.clone()) .await? @@ -195,19 +282,71 @@ impl RawEigenClient { disperser::BlobStatus::InsufficientSignatures => { return Err(anyhow::anyhow!("Insufficient signatures")) } - disperser::BlobStatus::Confirmed | disperser::BlobStatus::Finalized => { - let verification_proof = resp + disperser::BlobStatus::Confirmed => { + if !self.config.wait_for_finalization { + let blob_info = resp + .info + .ok_or_else(|| anyhow::anyhow!("No blob header in response"))?; + return Ok(blob_info); + } + } + disperser::BlobStatus::Finalized => { + let blob_info = 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); + .ok_or_else(|| anyhow::anyhow!("No blob header in response"))?; + return Ok(blob_info); } _ => return Err(anyhow::anyhow!("Received unknown blob status")), } } + + Err(anyhow::anyhow!("Failed to disperse blob (timeout)")) + } + + #[cfg(test)] + pub async fn get_blob_data(&self, blob_id: &str) -> anyhow::Result>, DAError> { + use anyhow::anyhow; + use zksync_da_client::types::DAError; + + use crate::eigen::blob_info::BlobInfo; + + let commit = hex::decode(blob_id).map_err(|_| DAError { + error: anyhow!("Failed to decode blob_id"), + is_retriable: false, + })?; + let blob_info: BlobInfo = rlp::decode(&commit).map_err(|_| DAError { + error: anyhow!("Failed to decode blob_info"), + is_retriable: false, + })?; + let blob_index = blob_info.blob_verification_proof.blob_index; + let batch_header_hash = blob_info + .blob_verification_proof + .batch_medatada + .batch_header_hash; + let get_response = self + .client + .clone() + .retrieve_blob(disperser::RetrieveBlobRequest { + batch_header_hash, + blob_index, + }) + .await + .map_err(|e| DAError { + error: anyhow!(e), + is_retriable: true, + })? + .into_inner(); + + if get_response.data.is_empty() { + return Err(DAError { + error: anyhow!("Failed to get blob data"), + is_retriable: false, + }); + } + //TODO: remove zkgpad_rs + let data = kzgpad_rs::remove_empty_byte_from_padded_bytes(&get_response.data); + Ok(Some(data)) } } diff --git a/core/node/da_clients/src/eigen/verifier.rs b/core/node/da_clients/src/eigen/verifier.rs new file mode 100644 index 000000000000..9e70689ad1dc --- /dev/null +++ b/core/node/da_clients/src/eigen/verifier.rs @@ -0,0 +1,741 @@ +use std::{collections::HashMap, str::FromStr}; + +use alloy::{ + network::Ethereum, + providers::{Provider, RootProvider}, +}; +use ark_bn254::{Fq, G1Affine}; +use ethabi::{encode, Token}; +use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; +use tiny_keccak::{Hasher, Keccak}; + +use super::{ + blob_info::{BatchHeader, BlobHeader, BlobInfo, G1Commitment}, + generated::eigendaservicemanager::EigenDAServiceManager, +}; + +#[derive(Debug)] +pub enum VerificationError { + ServiceManagerError, + WrongUrl, + KzgError, + WrongProof, + DifferentCommitments, + DifferentRoots, + EmptyHash, + DifferentHashes, + WrongQuorumParams, + QuorumNotConfirmed, + CommitmentNotOnCurve, + CommitmentNotOnCorrectSubgroup, +} + +/// Configuration for the verifier used for authenticated dispersals +#[derive(Debug, Clone)] +pub struct VerifierConfig { + pub verify_certs: bool, + pub rpc_url: String, + pub svc_manager_addr: String, + pub max_blob_size: u32, + pub path_to_points: String, + pub eth_confirmation_depth: u32, +} + +/// Verifier used to verify the integrity of the blob info +/// Kzg is used for commitment verification +/// EigenDA service manager is used to connect to the service manager contract +#[derive(Debug, Clone)] +pub struct Verifier { + kzg: Kzg, + eigenda_svc_manager: EigenDAServiceManager::EigenDAServiceManagerInstance< + alloy::transports::http::Http, + RootProvider>, + >, + cfg: VerifierConfig, +} + +impl Verifier { + pub fn new(cfg: VerifierConfig) -> Result { + let srs_points_to_load = cfg.max_blob_size / 32; + let kzg = Kzg::setup( + &format!("{}{}", cfg.path_to_points, "/g1.point"), + "", + &format!("{}{}", cfg.path_to_points, "/g2.point.powerOf2"), + 268435456, // 2 ^ 28 + srs_points_to_load, + "".to_string(), + ); + let kzg = kzg.map_err(|e| { + tracing::error!("Failed to setup KZG: {:?}", e); + VerificationError::KzgError + })?; + let url = alloy::transports::http::reqwest::Url::from_str(&cfg.rpc_url) + .map_err(|_| VerificationError::WrongUrl)?; + let provider: RootProvider< + alloy::transports::http::Http, + Ethereum, + > = RootProvider::new_http(url); + + let svc_manager_addr = alloy::primitives::Address::from_str(&cfg.svc_manager_addr) + .map_err(|_| VerificationError::ServiceManagerError)?; + + let eigenda_svc_manager = EigenDAServiceManager::new(svc_manager_addr, provider); + Ok(Self { + kzg, + eigenda_svc_manager, + cfg, + }) + } + + /// Return the commitment from a blob + fn commit(&self, blob: Vec) -> Result { + let blob = Blob::from_bytes_and_pad(&blob.to_vec()); + self.kzg + .blob_to_kzg_commitment(&blob, PolynomialFormat::InEvaluationForm) + .map_err(|_| VerificationError::KzgError) + } + + /// Compare the given commitment with the commitment generated with the blob + pub fn verify_commitment( + &self, + expected_commitment: G1Commitment, + blob: Vec, + ) -> Result<(), VerificationError> { + let actual_commitment = self.commit(blob)?; + let expected_commitment = G1Affine::new_unchecked( + Fq::from(num_bigint::BigUint::from_bytes_be(&expected_commitment.x)), + Fq::from(num_bigint::BigUint::from_bytes_be(&expected_commitment.y)), + ); + if !expected_commitment.is_on_curve() { + return Err(VerificationError::CommitmentNotOnCurve); + } + if !expected_commitment.is_in_correct_subgroup_assuming_on_curve() { + return Err(VerificationError::CommitmentNotOnCorrectSubgroup); + } + if actual_commitment != expected_commitment { + return Err(VerificationError::DifferentCommitments); + } + Ok(()) + } + + fn hash_encode_blob_header(&self, blob_header: BlobHeader) -> Vec { + let mut blob_quorums = vec![]; + for quorum in blob_header.blob_quorum_params { + let quorum = Token::Tuple(vec![ + Token::Uint(ethabi::Uint::from(quorum.quorum_number)), + Token::Uint(ethabi::Uint::from(quorum.adversary_threshold_percentage)), + Token::Uint(ethabi::Uint::from(quorum.confirmation_threshold_percentage)), + Token::Uint(ethabi::Uint::from(quorum.chunk_length)), + ]); + blob_quorums.push(quorum); + } + let blob_header = Token::Tuple(vec![ + Token::Tuple(vec![ + Token::Uint(ethabi::Uint::from_big_endian(&blob_header.commitment.x)), + Token::Uint(ethabi::Uint::from_big_endian(&blob_header.commitment.y)), + ]), + Token::Uint(ethabi::Uint::from(blob_header.data_length)), + Token::Array(blob_quorums), + ]); + + let encoded = encode(&[blob_header]); + + let mut keccak = Keccak::v256(); + keccak.update(&encoded); + let mut hash = [0u8; 32]; + keccak.finalize(&mut hash); + hash.to_vec() + } + + fn process_inclusion_proof( + &self, + proof: &[u8], + leaf: &[u8], + index: u32, + ) -> Result, VerificationError> { + let mut index = index; + if proof.is_empty() || proof.len() % 32 != 0 { + return Err(VerificationError::WrongProof); + } + let mut computed_hash = leaf.to_vec(); + for i in 0..proof.len() / 32 { + let mut combined = proof[i * 32..(i + 1) * 32] + .iter() + .chain(computed_hash.iter()) + .cloned() + .collect::>(); + if index % 2 == 0 { + combined = computed_hash + .iter() + .chain(proof[i * 32..(i + 1) * 32].iter()) + .cloned() + .collect::>(); + }; + let mut keccak = Keccak::v256(); + keccak.update(&combined); + let mut hash = [0u8; 32]; + keccak.finalize(&mut hash); + computed_hash = hash.to_vec(); + index /= 2; + } + + Ok(computed_hash) + } + + /// Verifies the certificate's batch root + fn verify_merkle_proof(&self, cert: BlobInfo) -> Result<(), VerificationError> { + let inclusion_proof = cert.blob_verification_proof.inclusion_proof; + let root = cert + .blob_verification_proof + .batch_medatada + .batch_header + .batch_root; + let blob_index = cert.blob_verification_proof.blob_index; + let blob_header = cert.blob_header; + + let blob_header_hash = self.hash_encode_blob_header(blob_header); + let mut keccak = Keccak::v256(); + keccak.update(&blob_header_hash); + let mut leaf_hash = [0u8; 32]; + keccak.finalize(&mut leaf_hash); + + let generated_root = + self.process_inclusion_proof(&inclusion_proof, &leaf_hash, blob_index)?; + + if generated_root != root { + return Err(VerificationError::DifferentRoots); + } + Ok(()) + } + + fn hash_batch_metadata( + &self, + batch_header: BatchHeader, + signatory_record_hash: Vec, + confirmation_block_number: u32, + ) -> Vec { + let batch_header_token = Token::Tuple(vec![ + Token::FixedBytes(batch_header.batch_root), + Token::Bytes(batch_header.quorum_numbers), + Token::Bytes(batch_header.quorum_signed_percentages), + Token::Uint(ethabi::Uint::from(batch_header.reference_block_number)), + ]); + + let encoded = encode(&[batch_header_token]); + + let mut keccak = Keccak::v256(); + keccak.update(&encoded); + let mut header_hash = [0u8; 32]; + keccak.finalize(&mut header_hash); + + let hash_token = Token::Tuple(vec![ + Token::FixedBytes(header_hash.to_vec()), + Token::FixedBytes(signatory_record_hash), + ]); + + let mut hash_encoded = encode(&[hash_token]); + + hash_encoded.append(&mut confirmation_block_number.to_be_bytes().to_vec()); + + let mut keccak = Keccak::v256(); + keccak.update(&hash_encoded); + let mut hash = [0u8; 32]; + keccak.finalize(&mut hash); + + hash.to_vec() + } + + /// Retrieves the block to make the request to the service manager + async fn get_context_block(&self) -> Result { + let latest = self + .eigenda_svc_manager + .provider() + .get_block_number() + .await + .map_err(|_| VerificationError::ServiceManagerError)?; + + if self.cfg.eth_confirmation_depth == 0 { + return Ok(latest); + } + Ok(latest - (self.cfg.eth_confirmation_depth as u64 - 1)) + } + + /// Verifies the certificate batch hash + async fn verify_batch(&self, cert: BlobInfo) -> Result<(), VerificationError> { + let context_block = self.get_context_block().await?; + let expected_hash = self + .eigenda_svc_manager + .batchIdToBatchMetadataHash(cert.blob_verification_proof.batch_id) + .block(context_block.into()) + .call() + .await + .map_err(|_| VerificationError::ServiceManagerError)? + ._0 + .to_vec(); + + if expected_hash == vec![0u8; 32] { + return Err(VerificationError::EmptyHash); + } + + let actual_hash = self.hash_batch_metadata( + cert.blob_verification_proof.batch_medatada.batch_header, + cert.blob_verification_proof + .batch_medatada + .signatory_record_hash, + cert.blob_verification_proof + .batch_medatada + .confirmation_block_number, + ); + + if expected_hash != actual_hash { + return Err(VerificationError::DifferentHashes); + } + Ok(()) + } + + async fn get_quorum_adversary_threshold( + &self, + quorum_number: u32, + ) -> Result { + let percentages = self + .eigenda_svc_manager + .quorumAdversaryThresholdPercentages() + .call() + .await + .map_err(|_| VerificationError::ServiceManagerError)? + ._0; + if percentages.len() > quorum_number as usize { + return Ok(percentages[quorum_number as usize]); + } + Ok(0) + } + + /// Verifies that the certificate's blob quorum params are correct + async fn verify_security_params(&self, cert: BlobInfo) -> Result<(), VerificationError> { + let blob_header = cert.blob_header; + let batch_header = cert.blob_verification_proof.batch_medatada.batch_header; + + let mut confirmed_quorums: HashMap = HashMap::new(); + for i in 0..blob_header.blob_quorum_params.len() { + if batch_header.quorum_numbers[i] as u32 + != blob_header.blob_quorum_params[i].quorum_number + { + return Err(VerificationError::WrongQuorumParams); + } + if blob_header.blob_quorum_params[i].adversary_threshold_percentage + > blob_header.blob_quorum_params[i].confirmation_threshold_percentage + { + return Err(VerificationError::WrongQuorumParams); + } + let quorum_adversary_threshold = self + .get_quorum_adversary_threshold(blob_header.blob_quorum_params[i].quorum_number) + .await?; + + if quorum_adversary_threshold > 0 + && blob_header.blob_quorum_params[i].adversary_threshold_percentage + < quorum_adversary_threshold as u32 + { + return Err(VerificationError::WrongQuorumParams); + } + + if (batch_header.quorum_signed_percentages[i] as u32) + < blob_header.blob_quorum_params[i].confirmation_threshold_percentage + { + return Err(VerificationError::WrongQuorumParams); + } + + confirmed_quorums.insert(blob_header.blob_quorum_params[i].quorum_number, true); + } + + let required_quorums = self + .eigenda_svc_manager + .quorumNumbersRequired() + .call() + .await + .map_err(|_| VerificationError::ServiceManagerError)? + ._0 + .to_vec(); + + for quorum in required_quorums { + if !confirmed_quorums.contains_key(&(quorum as u32)) { + return Err(VerificationError::QuorumNotConfirmed); + } + } + Ok(()) + } + + /// Verifies that the certificate is valid + pub async fn verify_certificate(&self, cert: BlobInfo) -> Result<(), VerificationError> { + if !self.cfg.verify_certs { + return Ok(()); + } + self.verify_batch(cert.clone()).await?; + self.verify_merkle_proof(cert.clone())?; + self.verify_security_params(cert.clone()).await?; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::eigen::blob_info::{ + BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, BlobVerificationProof, + G1Commitment, + }; + + #[test] + fn test_verify_commitment() { + let verifier = super::Verifier::new(super::VerifierConfig { + verify_certs: true, + rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + svc_manager_addr: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + max_blob_size: 2 * 1024 * 1024, + path_to_points: "../../../resources".to_string(), + eth_confirmation_depth: 0, + }) + .unwrap(); + let commitment = G1Commitment { + x: vec![ + 22, 11, 176, 29, 82, 48, 62, 49, 51, 119, 94, 17, 156, 142, 248, 96, 240, 183, 134, + 85, 152, 5, 74, 27, 175, 83, 162, 148, 17, 110, 201, 74, + ], + y: vec![ + 12, 132, 236, 56, 147, 6, 176, 135, 244, 166, 21, 18, 87, 76, 122, 3, 23, 22, 254, + 236, 148, 129, 110, 207, 131, 116, 58, 170, 4, 130, 191, 157, + ], + }; + let blob = vec![1u8; 100]; // Actual blob sent was this blob but kzg-padded, but Blob::from_bytes_and_pad padds it inside, so we don't need to pad it here. + let result = verifier.verify_commitment(commitment, blob); + assert!(result.is_ok()); + } + + #[test] + fn test_verify_merkle_proof() { + let verifier = super::Verifier::new(super::VerifierConfig { + verify_certs: true, + rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + svc_manager_addr: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + max_blob_size: 2 * 1024 * 1024, + path_to_points: "../../../resources".to_string(), + eth_confirmation_depth: 0, + }) + .unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + }, + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, + 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, + 211, 43, + ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, + }, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, + 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + ], + }, + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, + 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, + 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, + 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, + 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, + 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, + 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, + 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, + 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, + 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, + 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, + 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, + 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, + 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_merkle_proof(cert); + assert!(result.is_ok()); + } + + #[test] + fn test_hash_blob_header() { + let verifier = super::Verifier::new(super::VerifierConfig { + verify_certs: true, + rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + svc_manager_addr: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + max_blob_size: 2 * 1024 * 1024, + path_to_points: "../../../resources".to_string(), + eth_confirmation_depth: 0, + }) + .unwrap(); + let blob_header = BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + ], + }, + data_length: 2, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 2, + adversary_threshold_percentage: 4, + confirmation_threshold_percentage: 5, + chunk_length: 6, + }, + BlobQuorumParam { + quorum_number: 2, + adversary_threshold_percentage: 4, + confirmation_threshold_percentage: 5, + chunk_length: 6, + }, + ], + }; + let result = verifier.hash_encode_blob_header(blob_header); + let expected = "ba4675a31c9bf6b2f7abfdcedd34b74645cb7332b35db39bff00ae8516a67393"; + assert_eq!(result, hex::decode(expected).unwrap()); + } + + #[test] + fn test_inclusion_proof() { + let verifier = super::Verifier::new(super::VerifierConfig { + verify_certs: true, + rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + svc_manager_addr: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + max_blob_size: 2 * 1024 * 1024, + path_to_points: "../../../resources".to_string(), + eth_confirmation_depth: 0, + }) + .unwrap(); + + let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); + let leaf = hex::decode("f6106e6ae4631e68abe0fa898cedbe97dbae6c7efb1b088c5aa2e8b91190ff96") + .unwrap(); + let expected_root = + hex::decode("7390b8023db8248123dcaeca57fa6c9340bef639e204f2278fc7ec3d46ad071b") + .unwrap(); + + let actual_root = verifier + .process_inclusion_proof(&proof, &leaf, 580) + .unwrap(); + + assert_eq!(actual_root, expected_root); + } + + #[tokio::test] + async fn test_verify_batch() { + let verifier = super::Verifier::new(super::VerifierConfig { + verify_certs: true, + rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + svc_manager_addr: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + max_blob_size: 2 * 1024 * 1024, + path_to_points: "../../../resources".to_string(), + eth_confirmation_depth: 0, + }) + .unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + }, + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, + 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, + 211, 43, + ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, + }, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, + 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + ], + }, + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, + 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, + 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, + 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, + 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, + 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, + 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, + 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, + 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, + 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, + 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, + 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, + 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, + 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_batch(cert).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_verify_security_params() { + let verifier = super::Verifier::new(super::VerifierConfig { + verify_certs: true, + rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + svc_manager_addr: "0xD4A7E1Bd8015057293f0D0A557088c286942e84b".to_string(), + max_blob_size: 2 * 1024 * 1024, + path_to_points: "../../../resources".to_string(), + eth_confirmation_depth: 0, + }) + .unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + ], + }, + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, + 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, + 211, 43, + ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, + }, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, + 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + ], + }, + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, + 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, + 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, + 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, + 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, + 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, + 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, + 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, + 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, + 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, + 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, + 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, + 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, + 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_security_params(cert).await; + assert!(result.is_ok()); + } +} diff --git a/core/node/eth_watch/src/tests.rs b/core/node/eth_watch/src/tests.rs index d9faf7b664e6..12ac8bdbf3f7 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/metadata_calculator/src/pruning.rs b/core/node/metadata_calculator/src/pruning.rs index abbf9bf6865a..4ac05e55c302 100644 --- a/core/node/metadata_calculator/src/pruning.rs +++ b/core/node/metadata_calculator/src/pruning.rs @@ -304,6 +304,7 @@ mod tests { extend_db_state_from_l1_batch( &mut storage, snapshot_recovery.l1_batch_number + 1, + snapshot_recovery.l2_block_number + 1, new_logs, ) .await; diff --git a/core/node/metadata_calculator/src/recovery/mod.rs b/core/node/metadata_calculator/src/recovery/mod.rs index dcbc0a68af92..ce7207471791 100644 --- a/core/node/metadata_calculator/src/recovery/mod.rs +++ b/core/node/metadata_calculator/src/recovery/mod.rs @@ -32,16 +32,14 @@ use std::{ }; use anyhow::Context as _; +use async_trait::async_trait; use futures::future; use tokio::sync::{watch, Mutex, Semaphore}; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_health_check::HealthUpdater; use zksync_merkle_tree::TreeEntry; use zksync_shared_metrics::{SnapshotRecoveryStage, APP_METRICS}; -use zksync_types::{ - snapshots::{uniform_hashed_keys_chunk, SnapshotRecoveryStatus}, - L2BlockNumber, H256, -}; +use zksync_types::{snapshots::uniform_hashed_keys_chunk, L1BatchNumber, L2BlockNumber, H256}; use super::{ helpers::{AsyncTree, AsyncTreeRecovery, GenericAsyncTree, MerkleTreeHealth}, @@ -54,12 +52,13 @@ mod tests; /// Handler of recovery life cycle events. This functionality is encapsulated in a trait to be able /// to control recovery behavior in tests. +#[async_trait] trait HandleRecoveryEvent: fmt::Debug + Send + Sync { fn recovery_started(&mut self, _chunk_count: u64, _recovered_chunk_count: u64) { // Default implementation does nothing } - fn chunk_recovered(&self) { + async fn chunk_recovered(&self) { // Default implementation does nothing } } @@ -82,6 +81,7 @@ impl<'a> RecoveryHealthUpdater<'a> { } } +#[async_trait] impl HandleRecoveryEvent for RecoveryHealthUpdater<'_> { fn recovery_started(&mut self, chunk_count: u64, recovered_chunk_count: u64) { self.chunk_count = chunk_count; @@ -91,7 +91,7 @@ impl HandleRecoveryEvent for RecoveryHealthUpdater<'_> { .set(recovered_chunk_count); } - fn chunk_recovered(&self) { + async fn chunk_recovered(&self) { let recovered_chunk_count = self.recovered_chunk_count.fetch_add(1, Ordering::SeqCst) + 1; let chunks_left = self.chunk_count.saturating_sub(recovered_chunk_count); tracing::info!( @@ -110,34 +110,68 @@ impl HandleRecoveryEvent for RecoveryHealthUpdater<'_> { } #[derive(Debug, Clone, Copy)] -struct SnapshotParameters { +struct InitParameters { + l1_batch: L1BatchNumber, l2_block: L2BlockNumber, - expected_root_hash: H256, + expected_root_hash: Option, log_count: u64, desired_chunk_size: u64, } -impl SnapshotParameters { +impl InitParameters { async fn new( pool: &ConnectionPool, - recovery: &SnapshotRecoveryStatus, config: &MetadataCalculatorRecoveryConfig, - ) -> anyhow::Result { - let l2_block = recovery.l2_block_number; - let expected_root_hash = recovery.l1_batch_root_hash; - + ) -> anyhow::Result> { let mut storage = pool.connection_tagged("metadata_calculator").await?; + let recovery_status = storage + .snapshot_recovery_dal() + .get_applied_snapshot_status() + .await?; + let pruning_info = storage.pruning_dal().get_pruning_info().await?; + + let (l1_batch, l2_block); + let mut expected_root_hash = None; + match (recovery_status, pruning_info.last_hard_pruned_l2_block) { + (Some(recovery), None) => { + tracing::warn!( + "Snapshot recovery {recovery:?} is present on the node, but pruning info is empty; assuming no pruning happened" + ); + l1_batch = recovery.l1_batch_number; + l2_block = recovery.l2_block_number; + expected_root_hash = Some(recovery.l1_batch_root_hash); + } + (Some(recovery), Some(pruned_l2_block)) => { + // We have both recovery and some pruning on top of it. + l2_block = pruned_l2_block.max(recovery.l2_block_number); + l1_batch = pruning_info + .last_hard_pruned_l1_batch + .with_context(|| format!("malformed pruning info: {pruning_info:?}"))?; + if l1_batch == recovery.l1_batch_number { + expected_root_hash = Some(recovery.l1_batch_root_hash); + } + } + (None, Some(pruned_l2_block)) => { + l2_block = pruned_l2_block; + l1_batch = pruning_info + .last_hard_pruned_l1_batch + .with_context(|| format!("malformed pruning info: {pruning_info:?}"))?; + } + (None, None) => return Ok(None), + }; + let log_count = storage .storage_logs_dal() .get_storage_logs_row_count(l2_block) .await?; - Ok(Self { + Ok(Some(Self { + l1_batch, l2_block, expected_root_hash, log_count, desired_chunk_size: config.desired_chunk_size, - }) + })) } fn chunk_count(&self) -> u64 { @@ -168,29 +202,27 @@ impl GenericAsyncTree { stop_receiver: &watch::Receiver, ) -> anyhow::Result> { let started_at = Instant::now(); - let (tree, snapshot_recovery) = match self { + let (tree, init_params) = match self { Self::Ready(tree) => return Ok(Some(tree)), Self::Recovering(tree) => { - let snapshot_recovery = get_snapshot_recovery(main_pool).await?.context( + let params = InitParameters::new(main_pool, config).await?.context( "Merkle tree is recovering, but Postgres doesn't contain snapshot recovery information", )?; let recovered_version = tree.recovered_version(); anyhow::ensure!( - u64::from(snapshot_recovery.l1_batch_number.0) == recovered_version, - "Snapshot L1 batch in Postgres ({snapshot_recovery:?}) differs from the recovered Merkle tree version \ + u64::from(params.l1_batch.0) == recovered_version, + "Snapshot L1 batch in Postgres ({params:?}) differs from the recovered Merkle tree version \ ({recovered_version})" ); - tracing::info!("Resuming tree recovery with status: {snapshot_recovery:?}"); - (tree, snapshot_recovery) + tracing::info!("Resuming tree recovery with status: {params:?}"); + (tree, params) } Self::Empty { db, mode } => { - if let Some(snapshot_recovery) = get_snapshot_recovery(main_pool).await? { - tracing::info!( - "Starting Merkle tree recovery with status {snapshot_recovery:?}" - ); - let l1_batch = snapshot_recovery.l1_batch_number; + if let Some(params) = InitParameters::new(main_pool, config).await? { + tracing::info!("Starting Merkle tree recovery with status {params:?}"); + let l1_batch = params.l1_batch; let tree = AsyncTreeRecovery::new(db, l1_batch.0.into(), mode, config)?; - (tree, snapshot_recovery) + (tree, params) } else { // Start the tree from scratch. The genesis block will be filled in `TreeUpdater::loop_updating_tree()`. return Ok(Some(AsyncTree::new(db, mode)?)); @@ -198,17 +230,16 @@ impl GenericAsyncTree { } }; - let snapshot = SnapshotParameters::new(main_pool, &snapshot_recovery, config).await?; tracing::debug!( - "Obtained snapshot parameters: {snapshot:?} based on recovery configuration {config:?}" + "Obtained recovery init parameters: {init_params:?} based on recovery configuration {config:?}" ); let recovery_options = RecoveryOptions { - chunk_count: snapshot.chunk_count(), + chunk_count: init_params.chunk_count(), concurrency_limit: recovery_pool.max_size() as usize, events: Box::new(RecoveryHealthUpdater::new(health_updater)), }; let tree = tree - .recover(snapshot, recovery_options, &recovery_pool, stop_receiver) + .recover(init_params, recovery_options, &recovery_pool, stop_receiver) .await?; if tree.is_some() { // Only report latency if recovery wasn't canceled @@ -223,12 +254,12 @@ impl GenericAsyncTree { impl AsyncTreeRecovery { async fn recover( mut self, - snapshot: SnapshotParameters, + init_params: InitParameters, mut options: RecoveryOptions<'_>, pool: &ConnectionPool, stop_receiver: &watch::Receiver, ) -> anyhow::Result> { - self.ensure_desired_chunk_size(snapshot.desired_chunk_size) + self.ensure_desired_chunk_size(init_params.desired_chunk_size) .await?; let start_time = Instant::now(); @@ -237,13 +268,15 @@ impl AsyncTreeRecovery { .map(|chunk_id| uniform_hashed_keys_chunk(chunk_id, chunk_count)) .collect(); tracing::info!( - "Recovering Merkle tree from Postgres snapshot in {chunk_count} chunks with max concurrency {}", + "Recovering Merkle tree from Postgres snapshot in {chunk_count} chunks with max concurrency {}. \ + Be aware that enabling node pruning during recovery will probably result in a recovery error; always disable pruning \ + until recovery is complete", options.concurrency_limit ); let mut storage = pool.connection_tagged("metadata_calculator").await?; let remaining_chunks = self - .filter_chunks(&mut storage, snapshot.l2_block, &chunks) + .filter_chunks(&mut storage, init_params.l2_block, &chunks) .await?; drop(storage); options @@ -261,9 +294,10 @@ impl AsyncTreeRecovery { .acquire() .await .context("semaphore is never closed")?; - if Self::recover_key_chunk(&tree, snapshot.l2_block, chunk, pool, stop_receiver).await? + if Self::recover_key_chunk(&tree, init_params.l2_block, chunk, pool, stop_receiver) + .await? { - options.events.chunk_recovered(); + options.events.chunk_recovered().await; } anyhow::Ok(()) }); @@ -279,13 +313,18 @@ impl AsyncTreeRecovery { let finalize_latency = RECOVERY_METRICS.latency[&RecoveryStage::Finalize].start(); let actual_root_hash = tree.root_hash().await; - anyhow::ensure!( - actual_root_hash == snapshot.expected_root_hash, - "Root hash of recovered tree {actual_root_hash:?} differs from expected root hash {:?}. \ - If pruning is enabled and the tree is initialized some time after node recovery, \ - this is caused by snapshot storage logs getting pruned; this setup is currently not supported", - snapshot.expected_root_hash - ); + if let Some(expected_root_hash) = init_params.expected_root_hash { + anyhow::ensure!( + actual_root_hash == expected_root_hash, + "Root hash of recovered tree {actual_root_hash:?} differs from expected root hash {expected_root_hash:?}" + ); + } + + // Check pruning info one last time before finalizing the tree. + let mut storage = pool.connection_tagged("metadata_calculator").await?; + Self::check_pruning_info(&mut storage, init_params.l2_block).await?; + drop(storage); + let tree = tree.finalize().await?; finalize_latency.observe(); tracing::info!( @@ -340,6 +379,21 @@ impl AsyncTreeRecovery { Ok(output) } + async fn check_pruning_info( + storage: &mut Connection<'_, Core>, + snapshot_l2_block: L2BlockNumber, + ) -> anyhow::Result<()> { + let pruning_info = storage.pruning_dal().get_pruning_info().await?; + if let Some(last_hard_pruned_l2_block) = pruning_info.last_hard_pruned_l2_block { + anyhow::ensure!( + last_hard_pruned_l2_block == snapshot_l2_block, + "Additional data was pruned compared to tree recovery L2 block #{snapshot_l2_block}: {pruning_info:?}. \ + Continuing recovery is impossible; to recover the tree, drop its RocksDB directory, stop pruning and restart recovery" + ); + } + Ok(()) + } + /// Returns `Ok(true)` if the chunk was recovered, `Ok(false)` if the recovery process was interrupted. async fn recover_key_chunk( tree: &Mutex, @@ -363,7 +417,9 @@ impl AsyncTreeRecovery { .storage_logs_dal() .get_tree_entries_for_l2_block(snapshot_l2_block, key_chunk.clone()) .await?; + Self::check_pruning_info(&mut storage, snapshot_l2_block).await?; drop(storage); + let entries_latency = entries_latency.observe(); tracing::debug!( "Loaded {} entries for chunk {key_chunk:?} in {entries_latency:?}", @@ -414,13 +470,3 @@ impl AsyncTreeRecovery { Ok(true) } } - -async fn get_snapshot_recovery( - pool: &ConnectionPool, -) -> anyhow::Result> { - let mut storage = pool.connection_tagged("metadata_calculator").await?; - Ok(storage - .snapshot_recovery_dal() - .get_applied_snapshot_status() - .await?) -} diff --git a/core/node/metadata_calculator/src/recovery/tests.rs b/core/node/metadata_calculator/src/recovery/tests.rs index 3861e8a5a84e..1d83c2f06031 100644 --- a/core/node/metadata_calculator/src/recovery/tests.rs +++ b/core/node/metadata_calculator/src/recovery/tests.rs @@ -1,6 +1,6 @@ //! Tests for metadata calculator snapshot recovery. -use std::{path::Path, sync::Mutex}; +use std::{collections::HashMap, path::Path, sync::Mutex}; use assert_matches::assert_matches; use tempfile::TempDir; @@ -15,7 +15,7 @@ use zksync_health_check::{CheckHealth, HealthStatus, ReactiveHealthCheck}; use zksync_merkle_tree::{domain::ZkSyncTree, recovery::PersistenceThreadHandle, TreeInstruction}; use zksync_node_genesis::{insert_genesis_batch, GenesisParams}; use zksync_node_test_utils::prepare_recovery_snapshot; -use zksync_types::{L1BatchNumber, ProtocolVersionId, StorageLog}; +use zksync_types::{L1BatchNumber, U256}; use super::*; use crate::{ @@ -29,10 +29,11 @@ use crate::{ #[test] fn calculating_chunk_count() { - let mut snapshot = SnapshotParameters { + let mut snapshot = InitParameters { + l1_batch: L1BatchNumber(1), l2_block: L2BlockNumber(1), log_count: 160_000_000, - expected_root_hash: H256::zero(), + expected_root_hash: Some(H256::zero()), desired_chunk_size: 200_000, }; assert_eq!(snapshot.chunk_count(), 800); @@ -57,13 +58,15 @@ async fn create_tree_recovery( async fn basic_recovery_workflow() { let pool = ConnectionPool::::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); - let snapshot_recovery = prepare_recovery_snapshot_with_genesis(pool.clone(), &temp_dir).await; + let root_hash = prepare_storage_logs(pool.clone(), &temp_dir).await; + prune_storage(&pool, L1BatchNumber(1)).await; + let config = MetadataCalculatorRecoveryConfig::default(); - let snapshot = SnapshotParameters::new(&pool, &snapshot_recovery, &config) + let init_params = InitParameters::new(&pool, &config) .await - .unwrap(); - - assert!(snapshot.log_count > 200); + .unwrap() + .expect("no init params"); + assert!(init_params.log_count > 200, "{init_params:?}"); let (_stop_sender, stop_receiver) = watch::channel(false); for chunk_count in [1, 4, 9, 16, 60, 256] { @@ -78,54 +81,94 @@ async fn basic_recovery_workflow() { events: Box::new(RecoveryHealthUpdater::new(&health_updater)), }; let tree = tree - .recover(snapshot, recovery_options, &pool, &stop_receiver) + .recover(init_params, recovery_options, &pool, &stop_receiver) .await .unwrap() .expect("Tree recovery unexpectedly aborted"); - assert_eq!(tree.root_hash(), snapshot_recovery.l1_batch_root_hash); + assert_eq!(tree.root_hash(), root_hash); let health = health_check.check_health().await; assert_matches!(health.status(), HealthStatus::Affected); } } -async fn prepare_recovery_snapshot_with_genesis( - pool: ConnectionPool, - temp_dir: &TempDir, -) -> SnapshotRecoveryStatus { +async fn prepare_storage_logs(pool: ConnectionPool, temp_dir: &TempDir) -> H256 { let mut storage = pool.connection().await.unwrap(); insert_genesis_batch(&mut storage, &GenesisParams::mock()) .await .unwrap(); - let mut logs = gen_storage_logs(100..300, 1).pop().unwrap(); - - // Add all logs from the genesis L1 batch to `logs` so that they cover all state keys. - let genesis_logs = storage - .storage_logs_dal() - .get_touched_slots_for_executed_l1_batch(L1BatchNumber(0)) - .await - .unwrap(); - let genesis_logs = genesis_logs - .into_iter() - .map(|(key, value)| StorageLog::new_write_log(key, value)); - logs.extend(genesis_logs); + let logs = gen_storage_logs(100..300, 1).pop().unwrap(); extend_db_state(&mut storage, vec![logs]).await; drop(storage); // Ensure that metadata for L1 batch #1 is present in the DB. let (calculator, _) = setup_calculator(&temp_dir.path().join("init"), pool, true).await; - let l1_batch_root_hash = run_calculator(calculator).await; - - SnapshotRecoveryStatus { - l1_batch_number: L1BatchNumber(1), - l1_batch_timestamp: 1, - l1_batch_root_hash, - l2_block_number: L2BlockNumber(1), - l2_block_timestamp: 1, - l2_block_hash: H256::zero(), // not used - protocol_version: ProtocolVersionId::latest(), - storage_logs_chunks_processed: vec![], - } + run_calculator(calculator).await +} + +async fn prune_storage(pool: &ConnectionPool, pruned_l1_batch: L1BatchNumber) { + // Emulate pruning batches in the storage. + let mut storage = pool.connection().await.unwrap(); + let (_, pruned_l2_block) = storage + .blocks_dal() + .get_l2_block_range_of_l1_batch(pruned_l1_batch) + .await + .unwrap() + .expect("L1 batch not present in Postgres"); + storage + .pruning_dal() + .soft_prune_batches_range(pruned_l1_batch, pruned_l2_block) + .await + .unwrap(); + let pruning_stats = storage + .pruning_dal() + .hard_prune_batches_range(pruned_l1_batch, pruned_l2_block) + .await + .unwrap(); + assert!( + pruning_stats.deleted_l1_batches > 0 && pruning_stats.deleted_l2_blocks > 0, + "{pruning_stats:?}" + ); +} + +#[tokio::test] +async fn recovery_workflow_for_partial_pruning() { + let pool = ConnectionPool::::test_pool().await; + let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); + let recovery_root_hash = prepare_storage_logs(pool.clone(), &temp_dir).await; + + // Add more storage logs and prune initial logs. + let logs = gen_storage_logs(200..400, 5); + extend_db_state(&mut pool.connection().await.unwrap(), logs).await; + let (calculator, _) = setup_calculator(&temp_dir.path().join("init"), pool.clone(), true).await; + let final_root_hash = run_calculator(calculator).await; + prune_storage(&pool, L1BatchNumber(1)).await; + + let tree_path = temp_dir.path().join("recovery"); + let db = create_db(mock_config(&tree_path)).await.unwrap(); + let tree = GenericAsyncTree::Empty { + db, + mode: MerkleTreeMode::Lightweight, + }; + let (_stop_sender, stop_receiver) = watch::channel(false); + let tree = tree + .ensure_ready( + &MetadataCalculatorRecoveryConfig::default(), + &pool, + pool.clone(), + &ReactiveHealthCheck::new("tree").1, + &stop_receiver, + ) + .await + .unwrap() + .expect("Tree recovery unexpectedly aborted"); + + assert_eq!(tree.root_hash(), recovery_root_hash); + drop(tree); // Release exclusive lock on RocksDB + + // Check that tree operates as intended after recovery + let (calculator, _) = setup_calculator(&tree_path, pool, true).await; + assert_eq!(run_calculator(calculator).await, final_root_hash); } #[derive(Debug)] @@ -164,12 +207,13 @@ impl TestEventListener { } } +#[async_trait] impl HandleRecoveryEvent for TestEventListener { fn recovery_started(&mut self, _chunk_count: u64, recovered_chunk_count: u64) { assert_eq!(recovered_chunk_count, self.expected_recovered_chunks); } - fn chunk_recovered(&self) { + async fn chunk_recovered(&self) { let processed_chunk_count = self.processed_chunk_count.fetch_add(1, Ordering::SeqCst) + 1; if processed_chunk_count >= self.stop_threshold { self.stop_sender.send_replace(true); @@ -201,7 +245,8 @@ impl FaultToleranceCase { async fn recovery_fault_tolerance(chunk_count: u64, case: FaultToleranceCase) { let pool = ConnectionPool::::test_pool().await; let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); - let snapshot_recovery = prepare_recovery_snapshot_with_genesis(pool.clone(), &temp_dir).await; + let root_hash = prepare_storage_logs(pool.clone(), &temp_dir).await; + prune_storage(&pool, L1BatchNumber(1)).await; let tree_path = temp_dir.path().join("recovery"); let mut config = MetadataCalculatorRecoveryConfig::default(); @@ -217,18 +262,19 @@ async fn recovery_fault_tolerance(chunk_count: u64, case: FaultToleranceCase) { concurrency_limit: 1, events: Box::new(TestEventListener::new(1, stop_sender)), }; - let snapshot = SnapshotParameters::new(&pool, &snapshot_recovery, &config) + let init_params = InitParameters::new(&pool, &config) .await - .unwrap(); + .unwrap() + .expect("no init params"); assert!(tree - .recover(snapshot, recovery_options, &pool, &stop_receiver) + .recover(init_params, recovery_options, &pool, &stop_receiver) .await .unwrap() .is_none()); // Emulate a restart and recover 2 more chunks (or 1 + emulated persistence crash). let (mut tree, handle) = create_tree_recovery(&tree_path, L1BatchNumber(1), &config).await; - assert_ne!(tree.root_hash().await, snapshot_recovery.l1_batch_root_hash); + assert_ne!(tree.root_hash().await, root_hash); let (stop_sender, stop_receiver) = watch::channel(false); let mut event_listener = TestEventListener::new(2, stop_sender).expect_recovered_chunks(1); let expected_recovered_chunks = if matches!(case, FaultToleranceCase::ParallelWithCrash) { @@ -244,7 +290,7 @@ async fn recovery_fault_tolerance(chunk_count: u64, case: FaultToleranceCase) { events: Box::new(event_listener), }; let recovery_result = tree - .recover(snapshot, recovery_options, &pool, &stop_receiver) + .recover(init_params, recovery_options, &pool, &stop_receiver) .await; if matches!(case, FaultToleranceCase::ParallelWithCrash) { let err = format!("{:#}", recovery_result.unwrap_err()); @@ -255,7 +301,7 @@ async fn recovery_fault_tolerance(chunk_count: u64, case: FaultToleranceCase) { // Emulate another restart and recover remaining chunks. let (mut tree, _) = create_tree_recovery(&tree_path, L1BatchNumber(1), &config).await; - assert_ne!(tree.root_hash().await, snapshot_recovery.l1_batch_root_hash); + assert_ne!(tree.root_hash().await, root_hash); let (stop_sender, stop_receiver) = watch::channel(false); let recovery_options = RecoveryOptions { chunk_count, @@ -266,11 +312,11 @@ async fn recovery_fault_tolerance(chunk_count: u64, case: FaultToleranceCase) { ), }; let tree = tree - .recover(snapshot, recovery_options, &pool, &stop_receiver) + .recover(init_params, recovery_options, &pool, &stop_receiver) .await .unwrap() .expect("Tree recovery unexpectedly aborted"); - assert_eq!(tree.root_hash(), snapshot_recovery.l1_batch_root_hash); + assert_eq!(tree.root_hash(), root_hash); } #[derive(Debug)] @@ -345,6 +391,7 @@ async fn entire_recovery_workflow(case: RecoveryWorkflowCase) { extend_db_state_from_l1_batch( &mut storage, snapshot_recovery.l1_batch_number + 1, + snapshot_recovery.l2_block_number + 1, [new_logs.clone()], ) .await; @@ -376,3 +423,124 @@ async fn entire_recovery_workflow(case: RecoveryWorkflowCase) { stop_sender.send_replace(true); calculator_task.await.expect("calculator panicked").unwrap(); } + +/// `pruned_batches == 0` is a sanity check. +#[test_casing(4, [0, 1, 2, 4])] +#[tokio::test] +async fn recovery_with_further_pruning(pruned_batches: u32) { + const NEW_BATCH_COUNT: usize = 5; + + assert!( + (pruned_batches as usize) < NEW_BATCH_COUNT, + "at least 1 batch should remain in DB" + ); + + let pool = ConnectionPool::::test_pool().await; + let snapshot_logs = gen_storage_logs(100..300, 1).pop().unwrap(); + let mut storage = pool.connection().await.unwrap(); + let mut db_transaction = storage.start_transaction().await.unwrap(); + let snapshot_recovery = prepare_recovery_snapshot( + &mut db_transaction, + L1BatchNumber(23), + L2BlockNumber(42), + &snapshot_logs, + ) + .await; + + // Add some batches after recovery. + let logs = gen_storage_logs(200..400, NEW_BATCH_COUNT); + extend_db_state_from_l1_batch( + &mut db_transaction, + snapshot_recovery.l1_batch_number + 1, + snapshot_recovery.l2_block_number + 1, + logs, + ) + .await; + db_transaction.commit().await.unwrap(); + + let all_logs = storage + .storage_logs_dal() + .dump_all_storage_logs_for_tests() + .await; + assert_eq!(all_logs.len(), 400); + let initial_writes = storage + .storage_logs_dedup_dal() + .dump_all_initial_writes_for_tests() + .await; + let initial_writes: HashMap<_, _> = initial_writes + .into_iter() + .map(|write| (write.hashed_key, write.index)) + .collect(); + drop(storage); + + let instructions: Vec<_> = all_logs + .iter() + .map(|log| { + let leaf_index = initial_writes[&log.hashed_key]; + let key = U256::from_little_endian(log.hashed_key.as_bytes()); + TreeInstruction::write(key, leaf_index, log.value) + }) + .collect(); + let expected_root_hash = ZkSyncTree::process_genesis_batch(&instructions).root_hash; + + if pruned_batches > 0 { + prune_storage(&pool, snapshot_recovery.l1_batch_number + pruned_batches).await; + } + + // Create a new tree instance. It should recover and process the remaining batches. + let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); + let (calculator, _) = setup_calculator(temp_dir.path(), pool, true).await; + assert_eq!(run_calculator(calculator).await, expected_root_hash); +} + +#[derive(Debug)] +struct PruningEventListener { + pool: ConnectionPool, + pruned_l1_batch: L1BatchNumber, +} + +#[async_trait] +impl HandleRecoveryEvent for PruningEventListener { + async fn chunk_recovered(&self) { + prune_storage(&self.pool, self.pruned_l1_batch).await; + } +} + +#[tokio::test] +async fn pruning_during_recovery_is_detected() { + let pool = ConnectionPool::::test_pool().await; + let temp_dir = TempDir::new().expect("failed get temporary directory for RocksDB"); + + let mut storage = pool.connection().await.unwrap(); + insert_genesis_batch(&mut storage, &GenesisParams::mock()) + .await + .unwrap(); + let logs = gen_storage_logs(200..400, 5); + extend_db_state(&mut storage, logs).await; + drop(storage); + prune_storage(&pool, L1BatchNumber(1)).await; + + let tree_path = temp_dir.path().join("recovery"); + let config = MetadataCalculatorRecoveryConfig::default(); + let (tree, _) = create_tree_recovery(&tree_path, L1BatchNumber(1), &config).await; + let (_stop_sender, stop_receiver) = watch::channel(false); + let recovery_options = RecoveryOptions { + chunk_count: 5, + concurrency_limit: 1, + events: Box::new(PruningEventListener { + pool: pool.clone(), + pruned_l1_batch: L1BatchNumber(3), + }), + }; + let init_params = InitParameters::new(&pool, &config) + .await + .unwrap() + .expect("no init params"); + + let err = tree + .recover(init_params, recovery_options, &pool, &stop_receiver) + .await + .unwrap_err(); + let err = format!("{err:#}").to_lowercase(); + assert!(err.contains("continuing recovery is impossible"), "{err}"); +} diff --git a/core/node/metadata_calculator/src/tests.rs b/core/node/metadata_calculator/src/tests.rs index b878b0c4a533..1c003c4ecf78 100644 --- a/core/node/metadata_calculator/src/tests.rs +++ b/core/node/metadata_calculator/src/tests.rs @@ -696,7 +696,9 @@ async fn setup_calculator_with_options( object_store: Option>, ) -> MetadataCalculator { let mut storage = pool.connection().await.unwrap(); - if storage.blocks_dal().is_genesis_needed().await.unwrap() { + let pruning_info = storage.pruning_dal().get_pruning_info().await.unwrap(); + let has_pruning_logs = pruning_info.last_hard_pruned_l1_batch.is_some(); + if !has_pruning_logs && storage.blocks_dal().is_genesis_needed().await.unwrap() { insert_genesis_batch(&mut storage, &GenesisParams::mock()) .await .unwrap(); @@ -782,13 +784,26 @@ pub(super) async fn extend_db_state( .await .unwrap() .expect("no L1 batches in Postgres"); - extend_db_state_from_l1_batch(&mut storage, sealed_l1_batch + 1, new_logs).await; + let sealed_l2_block = storage + .blocks_dal() + .get_sealed_l2_block_number() + .await + .unwrap() + .expect("no L2 blocks in Postgres"); + extend_db_state_from_l1_batch( + &mut storage, + sealed_l1_batch + 1, + sealed_l2_block + 1, + new_logs, + ) + .await; storage.commit().await.unwrap(); } pub(super) async fn extend_db_state_from_l1_batch( storage: &mut Connection<'_, Core>, next_l1_batch: L1BatchNumber, + mut next_l2_block: L2BlockNumber, new_logs: impl IntoIterator>, ) { assert!(storage.in_transaction(), "must be called in DB transaction"); @@ -797,8 +812,7 @@ pub(super) async fn extend_db_state_from_l1_batch( let header = create_l1_batch(idx); let batch_number = header.number; // Assumes that L1 batch consists of only one L2 block. - let l2_block_header = create_l2_block(idx); - let l2_block_number = l2_block_header.number; + let l2_block_header = create_l2_block(next_l2_block.0); storage .blocks_dal() @@ -812,7 +826,7 @@ pub(super) async fn extend_db_state_from_l1_batch( .unwrap(); storage .storage_logs_dal() - .insert_storage_logs(l2_block_number, &batch_logs) + .insert_storage_logs(next_l2_block, &batch_logs) .await .unwrap(); storage @@ -831,6 +845,8 @@ pub(super) async fn extend_db_state_from_l1_batch( .await .unwrap(); insert_initial_writes_for_batch(storage, batch_number).await; + + next_l2_block += 1; } } diff --git a/core/node/node_framework/src/implementations/layers/contract_verification_api.rs b/core/node/node_framework/src/implementations/layers/contract_verification_api.rs index 3f1f76cc1c12..2ca7cc25a1fd 100644 --- a/core/node/node_framework/src/implementations/layers/contract_verification_api.rs +++ b/core/node/node_framework/src/implementations/layers/contract_verification_api.rs @@ -69,7 +69,7 @@ impl Task for ContractVerificationApiTask { zksync_contract_verification_server::start_server( self.master_pool, self.replica_pool, - self.config, + self.config.bind_addr(), stop_receiver.0, ) .await diff --git a/core/node/node_sync/src/external_io.rs b/core/node/node_sync/src/external_io.rs index 1be7e00543f1..0f5f4d6253fa 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 0e924b9f066d..75d7c9f1e943 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/executor/tests/tester.rs b/core/node/state_keeper/src/executor/tests/tester.rs index a02aeb47cafa..800bf398938d 100644 --- a/core/node/state_keeper/src/executor/tests/tester.rs +++ b/core/node/state_keeper/src/executor/tests/tester.rs @@ -19,7 +19,7 @@ use zksync_multivm::{ utils::StorageWritesDeduplicator, vm_latest::constants::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, }; -use zksync_node_genesis::{create_genesis_l1_batch, GenesisParams}; +use zksync_node_genesis::create_genesis_l1_batch; use zksync_node_test_utils::{recover, Snapshot}; use zksync_state::{OwnedStorage, ReadStorageFactory, RocksdbStorageOptions}; use zksync_test_account::{Account, DeployContractsTx, TxType}; @@ -602,7 +602,8 @@ impl StorageSnapshot { L1BatchNumber(1), self.l2_block_number, snapshot_logs, - GenesisParams::mock(), + &BASE_SYSTEM_CONTRACTS, + ProtocolVersionId::latest(), ); let mut snapshot = recover(&mut storage, snapshot).await; snapshot.l2_block_hash = self.l2_block_hash; diff --git a/core/node/state_keeper/src/io/common/tests.rs b/core/node/state_keeper/src/io/common/tests.rs index ec9f906b1cd7..2298d4c2ee74 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 dfddd36aba71..370d46fd544c 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 e2461e72d7b2..fbc481fb678d 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/persistence.rs b/core/node/state_keeper/src/io/persistence.rs index 06f1972a02aa..8bfd812c8a1f 100644 --- a/core/node/state_keeper/src/io/persistence.rs +++ b/core/node/state_keeper/src/io/persistence.rs @@ -379,7 +379,7 @@ impl StateKeeperOutputHandler for TreeWritesPersistence { #[cfg(test)] mod tests { - use std::collections::{HashMap, HashSet}; + use std::collections::HashSet; use assert_matches::assert_matches; use futures::FutureExt; @@ -510,7 +510,6 @@ mod tests { tx, tx_result, vec![], - HashMap::new(), BlockGasCount::default(), VmExecutionMetrics::default(), vec![], 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 4fc58bce5c9e..53871c54a19f 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 ece5b67767f6..7196236475df 100644 --- a/core/node/state_keeper/src/io/tests/mod.rs +++ b/core/node/state_keeper/src/io/tests/mod.rs @@ -1,22 +1,29 @@ -use std::{collections::HashMap, time::Duration}; +use std::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; +use zksync_system_constants::KNOWN_CODES_STORAGE_ADDRESS; use zksync_types::{ block::{BlockGasCount, L2BlockHasher}, - commitment::L1BatchCommitmentMode, + commitment::{L1BatchCommitmentMode, PubdataParams}, fee_model::{BatchFeeInput, PubdataIndependentBatchFeeModelInput}, + l2::L2Tx, AccountTreeId, Address, L1BatchNumber, L2BlockNumber, L2ChainId, ProtocolVersion, - ProtocolVersionId, StorageKey, H256, U256, + ProtocolVersionId, StorageKey, TransactionTimeRangeConstraint, H256, U256, +}; +use zksync_utils::{ + bytecode::{hash_bytecode, hash_evm_bytecode}, + time::seconds_since_epoch, }; -use zksync_utils::time::seconds_since_epoch; use self::tester::Tester; use crate::{ @@ -130,6 +137,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 +177,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)) @@ -217,6 +230,29 @@ async fn l1_batch_timestamp_respects_prev_l2_block_with_clock_skew( test_timestamps_are_distinct(connection_pool, current_timestamp + 2, true, tester).await; } +fn create_block_seal_command( + l1_batch_number: L1BatchNumber, + l2_block: L2BlockUpdates, +) -> L2BlockSealCommand { + L2BlockSealCommand { + l1_batch_number, + l2_block, + first_tx_index: 0, + fee_account_address: Address::repeat_byte(0x23), + fee_input: BatchFeeInput::PubdataIndependent(PubdataIndependentBatchFeeModelInput { + l1_gas_price: 100, + fair_l2_gas_price: 100, + fair_pubdata_price: 100, + }), + base_fee_per_gas: 10, + base_system_contracts_hashes: BaseSystemContractsHashes::default(), + protocol_version: Some(ProtocolVersionId::latest()), + l2_legacy_shared_bridge_addr: Some(Address::default()), + pre_insert_txs: false, + pubdata_params: PubdataParams::default(), + } +} + #[tokio::test] async fn processing_storage_logs_when_sealing_l2_block() { let connection_pool = @@ -249,7 +285,6 @@ async fn processing_storage_logs_when_sealing_l2_block() { BlockGasCount::default(), VmExecutionMetrics::default(), vec![], - HashMap::new(), vec![], ); @@ -268,28 +303,11 @@ async fn processing_storage_logs_when_sealing_l2_block() { BlockGasCount::default(), VmExecutionMetrics::default(), vec![], - HashMap::new(), vec![], ); let l1_batch_number = L1BatchNumber(2); - let seal_command = L2BlockSealCommand { - l1_batch_number, - l2_block, - first_tx_index: 0, - fee_account_address: Address::repeat_byte(0x23), - fee_input: BatchFeeInput::PubdataIndependent(PubdataIndependentBatchFeeModelInput { - l1_gas_price: 100, - fair_l2_gas_price: 100, - fair_pubdata_price: 100, - }), - base_fee_per_gas: 10, - base_system_contracts_hashes: BaseSystemContractsHashes::default(), - protocol_version: Some(ProtocolVersionId::latest()), - l2_legacy_shared_bridge_addr: Some(Address::default()), - pre_insert_txs: false, - pubdata_params: Default::default(), - }; + let seal_command = create_block_seal_command(l1_batch_number, l2_block); connection_pool .connection() .await @@ -359,28 +377,11 @@ async fn processing_events_when_sealing_l2_block() { BlockGasCount::default(), VmExecutionMetrics::default(), vec![], - HashMap::new(), vec![], ); } - let seal_command = L2BlockSealCommand { - l1_batch_number, - l2_block, - first_tx_index: 0, - fee_account_address: Address::repeat_byte(0x23), - fee_input: BatchFeeInput::PubdataIndependent(PubdataIndependentBatchFeeModelInput { - l1_gas_price: 100, - fair_l2_gas_price: 100, - fair_pubdata_price: 100, - }), - base_fee_per_gas: 10, - base_system_contracts_hashes: BaseSystemContractsHashes::default(), - protocol_version: Some(ProtocolVersionId::latest()), - l2_legacy_shared_bridge_addr: Some(Address::default()), - pre_insert_txs: false, - pubdata_params: Default::default(), - }; + let seal_command = create_block_seal_command(l1_batch_number, l2_block); pool.connection() .await .unwrap() @@ -404,6 +405,114 @@ async fn processing_events_when_sealing_l2_block() { } } +fn bytecode_publishing_events( + l1_batch_number: L1BatchNumber, + tx_index: u32, + bytecode_hashes: impl Iterator, +) -> Vec { + bytecode_hashes + .map(|bytecode_hash| VmEvent { + location: (l1_batch_number, tx_index), + address: KNOWN_CODES_STORAGE_ADDRESS, + indexed_topics: vec![ + VmEvent::PUBLISHED_BYTECODE_SIGNATURE, + bytecode_hash, + H256::from_low_u64_be(1), // sentBytecodeToL1 + ], + value: vec![], + }) + .collect() +} + +#[tokio::test] +async fn processing_dynamic_factory_deps_when_sealing_l2_block() { + let pool = + ConnectionPool::::constrained_test_pool(L2BlockSealProcess::subtasks_len()).await; + let l1_batch_number = L1BatchNumber(2); + let l2_block_number = L2BlockNumber(3); + let mut l2_block = L2BlockUpdates::new( + 0, + l2_block_number, + H256::zero(), + 1, + ProtocolVersionId::latest(), + ); + + let static_factory_deps: Vec<_> = (0_u8..10) + .map(|byte| { + let era_bytecode = vec![byte; 32]; + (hash_bytecode(&era_bytecode), era_bytecode) + }) + .collect(); + let dynamic_factory_deps: Vec<_> = (0_u8..10) + .map(|byte| { + let evm_bytecode = vec![byte; 96]; + (hash_evm_bytecode(&evm_bytecode), evm_bytecode) + }) + .collect(); + let mut all_factory_deps = static_factory_deps.clone(); + all_factory_deps.extend_from_slice(&dynamic_factory_deps); + + let events = bytecode_publishing_events( + l1_batch_number, + 0, + static_factory_deps + .iter() + .chain(&dynamic_factory_deps) + .map(|(hash, _)| *hash), + ); + + let mut tx = create_transaction(10, 100); + tx.execute.factory_deps = static_factory_deps + .into_iter() + .map(|(_, bytecode)| bytecode) + .collect(); + let mut execution_result = create_execution_result([]); + execution_result.dynamic_factory_deps = dynamic_factory_deps.into_iter().collect(); + execution_result.logs.events = events; + l2_block.extend_from_executed_transaction( + tx, + execution_result, + BlockGasCount::default(), + VmExecutionMetrics::default(), + vec![], + vec![], + ); + + assert_eq!( + l2_block.new_factory_deps.len(), + all_factory_deps.len(), + "{:?}", + l2_block.new_factory_deps + ); + for (hash, bytecode) in &all_factory_deps { + assert_eq!( + l2_block.new_factory_deps.get(hash), + Some(bytecode), + "{hash:?}" + ); + } + + let seal_command = create_block_seal_command(l1_batch_number, l2_block); + pool.connection() + .await + .unwrap() + .protocol_versions_dal() + .save_protocol_version_with_tx(&ProtocolVersion::default()) + .await + .unwrap(); + seal_command.seal(pool.clone()).await.unwrap(); + + let mut conn = pool.connection().await.unwrap(); + let persisted_factory_deps = conn + .factory_deps_dal() + .dump_all_factory_deps_for_tests() + .await; + for (hash, bytecode) in &all_factory_deps { + assert_eq!(persisted_factory_deps.get(hash), Some(bytecode), "{hash:?}"); + } +} + #[test_casing(2, COMMITMENT_MODES)] #[tokio::test] async fn l2_block_processing_after_snapshot_recovery(commitment_mode: L1BatchCommitmentMode) { @@ -431,12 +540,9 @@ 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()) - .await - .unwrap(); + insert_l2_transaction(&mut storage, &tx).await; let previous_batch_hash = mempool .load_batch_state_hash(snapshot_recovery.l1_batch_number) @@ -462,7 +568,6 @@ async fn l2_block_processing_after_snapshot_recovery(commitment_mode: L1BatchCom tx.into(), create_execution_result([]), vec![], - HashMap::new(), BlockGasCount::default(), VmExecutionMetrics::default(), vec![], @@ -584,12 +689,9 @@ 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()) - .await - .unwrap(); + insert_l2_transaction(&mut storage, &tx).await; let old_l1_batch_params = mempool .wait_for_new_batch_params(&cursor, Duration::from_secs(10)) @@ -644,3 +746,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 daedbebc75e0..32a746eecdfb 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 523dd8ecebad..fe37ee8d8dd6 100644 --- a/core/node/state_keeper/src/keeper.rs +++ b/core/node/state_keeper/src/keeper.rs @@ -503,9 +503,8 @@ impl ZkSyncStateKeeper { updates_manager.extend_from_executed_transaction( tx, - *tx_result.clone(), + *tx_result, compressed_bytecodes, - tx_result.new_known_factory_deps.unwrap_or_default(), tx_l1_gas_this_tx, tx_execution_metrics, call_tracer_result, @@ -589,11 +588,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")? @@ -630,9 +628,8 @@ impl ZkSyncStateKeeper { } = *tx_metrics; updates_manager.extend_from_executed_transaction( tx, - *tx_result.clone(), + *tx_result, compressed_bytecodes, - tx_result.new_known_factory_deps.unwrap_or_default(), tx_l1_gas_this_tx, tx_execution_metrics, call_tracer_result, @@ -712,9 +709,8 @@ impl ZkSyncStateKeeper { } = *tx_metrics; updates_manager.extend_from_executed_transaction( tx, - *tx_result.clone(), + *tx_result, compressed_bytecodes, - tx_result.new_known_factory_deps.unwrap_or_default(), tx_l1_gas_this_tx, tx_execution_metrics, call_tracer_result, diff --git a/core/node/state_keeper/src/mempool_actor.rs b/core/node/state_keeper/src/mempool_actor.rs index a17f2670cbb9..8e9d674f8787 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 962cc807318b..c10b01e7e73d 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", } } @@ -277,8 +278,6 @@ impl L2BlockMaxPayloadSizeSealer { #[cfg(test)] mod tests { - use std::collections::HashMap; - use zksync_utils::time::seconds_since_epoch; use super::*; @@ -289,7 +288,6 @@ mod tests { tx, create_execution_result([]), vec![], - HashMap::new(), BlockGasCount::default(), VmExecutionMetrics::default(), vec![], diff --git a/core/node/state_keeper/src/testonly/mod.rs b/core/node/state_keeper/src/testonly/mod.rs index ad50c8ca8ce6..b0f641ccbc1a 100644 --- a/core/node/state_keeper/src/testonly/mod.rs +++ b/core/node/state_keeper/src/testonly/mod.rs @@ -8,8 +8,8 @@ use zksync_dal::{ConnectionPool, Core, CoreDal as _}; use zksync_multivm::interface::{ executor::{BatchExecutor, BatchExecutorFactory}, storage::{InMemoryStorage, StorageView}, - BatchTransactionExecutionResult, ExecutionResult, FinishedL1Batch, L1BatchEnv, L2BlockEnv, - SystemEnv, VmExecutionResultAndLogs, + BatchTransactionExecutionResult, FinishedL1Batch, L1BatchEnv, L2BlockEnv, SystemEnv, + VmExecutionResultAndLogs, }; use zksync_state::OwnedStorage; use zksync_test_account::Account; @@ -28,13 +28,7 @@ pub(super) static BASE_SYSTEM_CONTRACTS: Lazy = /// Creates a `TxExecutionResult` object denoting a successful tx execution. pub(crate) fn successful_exec() -> BatchTransactionExecutionResult { BatchTransactionExecutionResult { - tx_result: Box::new(VmExecutionResultAndLogs { - result: ExecutionResult::Success { output: vec![] }, - logs: Default::default(), - statistics: Default::default(), - refunds: Default::default(), - new_known_factory_deps: None, - }), + tx_result: Box::new(VmExecutionResultAndLogs::mock_success()), compressed_bytecodes: vec![], call_traces: vec![], } 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 45787b18f3c9..5625add021bf 100644 --- a/core/node/state_keeper/src/testonly/test_batch_executor.rs +++ b/core/node/state_keeper/src/testonly/test_batch_executor.rs @@ -258,14 +258,11 @@ pub(crate) fn random_upgrade_tx(tx_number: u64) -> ProtocolUpgradeTx { pub(crate) fn successful_exec_with_log() -> BatchTransactionExecutionResult { BatchTransactionExecutionResult { tx_result: Box::new(VmExecutionResultAndLogs { - result: ExecutionResult::Success { output: vec![] }, logs: VmExecutionLogs { user_l2_to_l1_logs: vec![UserL2ToL1Log::default()], ..VmExecutionLogs::default() }, - statistics: Default::default(), - refunds: Default::default(), - new_known_factory_deps: None, + ..VmExecutionResultAndLogs::mock_success() }), compressed_bytecodes: vec![], call_traces: vec![], @@ -275,13 +272,9 @@ pub(crate) fn successful_exec_with_log() -> BatchTransactionExecutionResult { /// Creates a `TxExecutionResult` object denoting a tx that was rejected. pub(crate) fn rejected_exec(reason: Halt) -> BatchTransactionExecutionResult { BatchTransactionExecutionResult { - tx_result: Box::new(VmExecutionResultAndLogs { - result: ExecutionResult::Halt { reason }, - logs: Default::default(), - statistics: Default::default(), - refunds: Default::default(), - new_known_factory_deps: None, - }), + tx_result: Box::new(VmExecutionResultAndLogs::mock(ExecutionResult::Halt { + reason, + })), compressed_bytecodes: vec![], call_traces: vec![], } @@ -731,6 +724,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/tests/mod.rs b/core/node/state_keeper/src/tests/mod.rs index 16eed0b2f7f7..28e2f9886b49 100644 --- a/core/node/state_keeper/src/tests/mod.rs +++ b/core/node/state_keeper/src/tests/mod.rs @@ -10,8 +10,8 @@ use tokio::sync::watch; use zksync_config::configs::chain::StateKeeperConfig; use zksync_multivm::{ interface::{ - ExecutionResult, Halt, L1BatchEnv, L2BlockEnv, Refunds, SystemEnv, TxExecutionMode, - VmExecutionLogs, VmExecutionResultAndLogs, VmExecutionStatistics, + Halt, L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, VmExecutionLogs, + VmExecutionResultAndLogs, VmExecutionStatistics, }, vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, }; @@ -120,26 +120,16 @@ pub(super) fn create_execution_result( let total_log_queries = storage_logs.len() + 2; VmExecutionResultAndLogs { - result: ExecutionResult::Success { output: vec![] }, logs: VmExecutionLogs { - events: vec![], - system_l2_to_l1_logs: vec![], - user_l2_to_l1_logs: vec![], storage_logs, total_log_queries_count: total_log_queries, + ..VmExecutionLogs::default() }, statistics: VmExecutionStatistics { - contracts_used: 0, - cycles_used: 0, - gas_used: 0, - gas_remaining: 0, - computational_gas_used: 0, total_log_queries, - pubdata_published: 0, - circuit_statistic: Default::default(), + ..VmExecutionStatistics::default() }, - refunds: Refunds::default(), - new_known_factory_deps: None, + ..VmExecutionResultAndLogs::mock_success() } } diff --git a/core/node/state_keeper/src/types.rs b/core/node/state_keeper/src/types.rs index e112871a6475..db18e32e0963 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/state_keeper/src/updates/l1_batch_updates.rs b/core/node/state_keeper/src/updates/l1_batch_updates.rs index 2979ebbd8c26..aa2e22cac483 100644 --- a/core/node/state_keeper/src/updates/l1_batch_updates.rs +++ b/core/node/state_keeper/src/updates/l1_batch_updates.rs @@ -49,8 +49,6 @@ impl L1BatchUpdates { #[cfg(test)] mod tests { - use std::collections::HashMap; - use zksync_multivm::vm_latest::TransactionVmExt; use zksync_types::{L2BlockNumber, ProtocolVersionId, H256}; @@ -78,7 +76,6 @@ mod tests { BlockGasCount::default(), VmExecutionMetrics::default(), vec![], - HashMap::new(), vec![], ); diff --git a/core/node/state_keeper/src/updates/l2_block_updates.rs b/core/node/state_keeper/src/updates/l2_block_updates.rs index 27995b384abe..6faa098d40a2 100644 --- a/core/node/state_keeper/src/updates/l2_block_updates.rs +++ b/core/node/state_keeper/src/updates/l2_block_updates.rs @@ -5,7 +5,7 @@ use zksync_multivm::{ Call, CompressedBytecodeInfo, ExecutionResult, L2BlockEnv, TransactionExecutionResult, TxExecutionStatus, VmEvent, VmExecutionMetrics, VmExecutionResultAndLogs, }, - vm_latest::{utils::extract_bytecodes_marked_as_known, TransactionVmExt}, + vm_latest::TransactionVmExt, }; use zksync_types::{ block::{BlockGasCount, L2BlockHasher}, @@ -88,16 +88,10 @@ impl L2BlockUpdates { tx_l1_gas_this_tx: BlockGasCount, execution_metrics: VmExecutionMetrics, compressed_bytecodes: Vec, - new_known_factory_deps: HashMap>, call_traces: Vec, ) { let saved_factory_deps = - extract_bytecodes_marked_as_known(&tx_execution_result.logs.events); - self.events.extend(tx_execution_result.logs.events); - self.user_l2_to_l1_logs - .extend(tx_execution_result.logs.user_l2_to_l1_logs); - self.system_l2_to_l1_logs - .extend(tx_execution_result.logs.system_l2_to_l1_logs); + VmEvent::extract_bytecodes_marked_as_known(&tx_execution_result.logs.events); let gas_refunded = tx_execution_result.refunds.gas_refunded; let operator_suggested_refund = tx_execution_result.refunds.operator_suggested_refund; @@ -129,10 +123,10 @@ impl L2BlockUpdates { .collect(); // Ensure that *dynamic* factory deps (ones that may be created when executing EVM contracts) // are added into the lookup map as well. - tx_factory_deps.extend(new_known_factory_deps); + tx_factory_deps.extend(tx_execution_result.dynamic_factory_deps); // Save all bytecodes that were marked as known in the bootloader - let known_bytecodes = saved_factory_deps.into_iter().map(|bytecode_hash| { + let known_bytecodes = saved_factory_deps.map(|bytecode_hash| { let bytecode = tx_factory_deps.get(&bytecode_hash).unwrap_or_else(|| { panic!( "Failed to get factory deps on tx: bytecode hash: {:?}, tx hash: {}", @@ -140,7 +134,7 @@ impl L2BlockUpdates { tx.hash() ) }); - (bytecode_hash, bytecode.to_vec()) + (bytecode_hash, bytecode.clone()) }); self.new_factory_deps.extend(known_bytecodes); @@ -149,6 +143,11 @@ impl L2BlockUpdates { self.txs_encoding_size += tx.bootloader_encoding_size(); self.payload_encoding_size += zksync_protobuf::repr::encode::(&tx).len(); + self.events.extend(tx_execution_result.logs.events); + self.user_l2_to_l1_logs + .extend(tx_execution_result.logs.user_l2_to_l1_logs); + self.system_l2_to_l1_logs + .extend(tx_execution_result.logs.system_l2_to_l1_logs); self.storage_logs .extend(tx_execution_result.logs.storage_logs); @@ -211,7 +210,6 @@ mod tests { BlockGasCount::default(), VmExecutionMetrics::default(), vec![], - HashMap::new(), vec![], ); diff --git a/core/node/state_keeper/src/updates/mod.rs b/core/node/state_keeper/src/updates/mod.rs index b1bd35c921ca..752963580e37 100644 --- a/core/node/state_keeper/src/updates/mod.rs +++ b/core/node/state_keeper/src/updates/mod.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use zksync_contracts::BaseSystemContractsHashes; use zksync_multivm::{ interface::{ @@ -10,7 +8,7 @@ use zksync_multivm::{ }; use zksync_types::{ block::BlockGasCount, commitment::PubdataParams, fee_model::BatchFeeInput, Address, - L1BatchNumber, L2BlockNumber, ProtocolVersionId, Transaction, H256, + L1BatchNumber, L2BlockNumber, ProtocolVersionId, Transaction, }; pub(crate) use self::{l1_batch_updates::L1BatchUpdates, l2_block_updates::L2BlockUpdates}; @@ -119,7 +117,6 @@ impl UpdatesManager { tx: Transaction, tx_execution_result: VmExecutionResultAndLogs, compressed_bytecodes: Vec, - new_known_factory_deps: HashMap>, tx_l1_gas_this_tx: BlockGasCount, execution_metrics: VmExecutionMetrics, call_traces: Vec, @@ -135,7 +132,6 @@ impl UpdatesManager { tx_l1_gas_this_tx, execution_metrics, compressed_bytecodes, - new_known_factory_deps, call_traces, ); latency.observe(); @@ -246,7 +242,6 @@ mod tests { tx, create_execution_result([]), vec![], - HashMap::new(), new_block_gas_count(), VmExecutionMetrics::default(), vec![], diff --git a/core/node/test_utils/Cargo.toml b/core/node/test_utils/Cargo.toml index af60008df570..6df100c51a7d 100644 --- a/core/node/test_utils/Cargo.toml +++ b/core/node/test_utils/Cargo.toml @@ -11,11 +11,10 @@ keywords.workspace = true categories.workspace = true [dependencies] -zksync_multivm.workspace = true zksync_types.workspace = true zksync_dal.workspace = true zksync_contracts.workspace = true zksync_merkle_tree.workspace = true zksync_system_constants.workspace = true +zksync_vm_interface.workspace = true zksync_utils.workspace = true -zksync_node_genesis.workspace = true diff --git a/core/node/test_utils/src/lib.rs b/core/node/test_utils/src/lib.rs index 86ce3aadd9a1..2b446fff12c5 100644 --- a/core/node/test_utils/src/lib.rs +++ b/core/node/test_utils/src/lib.rs @@ -2,14 +2,9 @@ use std::collections::HashMap; -use zksync_contracts::BaseSystemContractsHashes; +use zksync_contracts::{BaseSystemContracts, BaseSystemContractsHashes}; use zksync_dal::{Connection, Core, CoreDal}; use zksync_merkle_tree::{domain::ZkSyncTree, TreeInstruction}; -use zksync_multivm::{ - interface::{TransactionExecutionResult, TxExecutionStatus, VmExecutionMetrics}, - utils::get_max_gas_per_pubdata_byte, -}; -use zksync_node_genesis::GenesisParams; use zksync_system_constants::{get_intrinsic_constants, ZKPORTER_IS_AVAILABLE}; use zksync_types::{ block::{L1BatchHeader, L2BlockHeader}, @@ -27,6 +22,10 @@ use zksync_types::{ Address, K256PrivateKey, L1BatchNumber, L2BlockNumber, L2ChainId, Nonce, ProtocolVersion, ProtocolVersionId, StorageLog, H256, U256, }; +use zksync_vm_interface::{TransactionExecutionResult, TxExecutionStatus, VmExecutionMetrics}; + +/// Value for recent protocol versions. +const MAX_GAS_PER_PUBDATA_BYTE: u64 = 50_000; /// Creates an L2 block header with the specified number and deterministic contents. pub fn create_l2_block(number: u32) -> L2BlockHeader { @@ -39,7 +38,7 @@ pub fn create_l2_block(number: u32) -> L2BlockHeader { base_fee_per_gas: 100, batch_fee_input: BatchFeeInput::l1_pegged(100, 100), fee_account_address: Address::zero(), - gas_per_pubdata_limit: get_max_gas_per_pubdata_byte(ProtocolVersionId::latest().into()), + gas_per_pubdata_limit: MAX_GAS_PER_PUBDATA_BYTE, base_system_contracts_hashes: BaseSystemContractsHashes::default(), protocol_version: Some(ProtocolVersionId::latest()), virtual_blocks: 1, @@ -195,14 +194,14 @@ impl Snapshot { l1_batch: L1BatchNumber, l2_block: L2BlockNumber, storage_logs: Vec, - genesis_params: GenesisParams, + contracts: &BaseSystemContracts, + protocol_version: ProtocolVersionId, ) -> Self { - let contracts = genesis_params.base_system_contracts(); let l1_batch = L1BatchHeader::new( l1_batch, l1_batch.0.into(), contracts.hashes(), - genesis_params.minor_protocol_version(), + protocol_version, ); let l2_block = L2BlockHeader { number: l2_block, @@ -213,11 +212,9 @@ impl Snapshot { base_fee_per_gas: 100, batch_fee_input: BatchFeeInput::l1_pegged(100, 100), fee_account_address: Address::zero(), - gas_per_pubdata_limit: get_max_gas_per_pubdata_byte( - genesis_params.minor_protocol_version().into(), - ), + gas_per_pubdata_limit: MAX_GAS_PER_PUBDATA_BYTE, base_system_contracts_hashes: contracts.hashes(), - protocol_version: Some(genesis_params.minor_protocol_version()), + protocol_version: Some(protocol_version), virtual_blocks: 1, gas_limit: 0, logs_bloom: Default::default(), @@ -253,7 +250,13 @@ pub async fn prepare_recovery_snapshot( enumeration_index: i as u64 + 1, }) .collect(); - let snapshot = Snapshot::new(l1_batch, l2_block, storage_logs, GenesisParams::mock()); + let snapshot = Snapshot::new( + l1_batch, + l2_block, + storage_logs, + &BaseSystemContracts::load_from_disk(), + ProtocolVersionId::latest(), + ); recover(storage, snapshot).await } diff --git a/core/node/vm_runner/src/tests/mod.rs b/core/node/vm_runner/src/tests/mod.rs index 575fd59be042..a3438d5a4e11 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 fc90355ac64e..991772124771 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 596872ab9c57..26f99d75780f 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, @@ -313,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 c513480c1b41..014031a3dd7e 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/tests/custom-account.test.ts b/core/tests/ts-integration/tests/custom-account.test.ts index 46ddba95323a..ebbe11b87195 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/docker/contract-verifier/Dockerfile b/docker/contract-verifier/Dockerfile index e9d83903d117..b1b63429a637 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/docs/guides/external-node/08_pruning.md b/docs/guides/external-node/08_pruning.md index 7f7dfc34d4a9..06bd9f8d8a9d 100644 --- a/docs/guides/external-node/08_pruning.md +++ b/docs/guides/external-node/08_pruning.md @@ -53,6 +53,12 @@ be pruned after it has been executed on Ethereum. Pruning can be disabled or enabled and the data retention period can be freely changed during the node lifetime. +> [!WARNING] +> +> Pruning should be disabled when recovering the Merkle tree (e.g., if a node ran in +> [the treeless mode](09_treeless_mode.md) before, or if its tree needs a reset for whatever reason). Otherwise, tree +> recovery will with almost definitely result in an error, or worse, in a corrupted tree. + ## Storage requirements for pruned nodes The storage requirements depend on how long you configure to retain the data, but are roughly: diff --git a/docs/guides/external-node/09_treeless_mode.md b/docs/guides/external-node/09_treeless_mode.md index 59e6f6412d31..ceeea6f86c67 100644 --- a/docs/guides/external-node/09_treeless_mode.md +++ b/docs/guides/external-node/09_treeless_mode.md @@ -59,12 +59,6 @@ or not running it when initializing a node. > (order of 2–3 hours for the mainnet) because the node no longer needs to recover the Merkle tree before starting > catching up. -> [!WARNING] -> -> In contrast to the tree fetcher, the Merkle tree cannot be safely switched on after a significant delay if pruning is -> enabled (some data necessary for tree update may have been pruned while the tree was off). We plan to fix this flaw in -> the future. If pruning is disabled, the Merkle tree _can_ be freely switched on / off. - ## Monitoring tree fetcher Tree fetcher information is logged with the `zksync_node_sync::tree_data_fetcher` target. diff --git a/etc/contracts-test-data/contracts/mock-evm/mock-evm.sol b/etc/contracts-test-data/contracts/mock-evm/mock-evm.sol index baa0d37b7530..3a7ee40db228 100644 --- a/etc/contracts-test-data/contracts/mock-evm/mock-evm.sol +++ b/etc/contracts-test-data/contracts/mock-evm/mock-evm.sol @@ -90,6 +90,25 @@ contract MockContractDeployer { ACCOUNT_CODE_STORAGE_CONTRACT.storeAccountConstructedCodeHash(newAddress, _salt); return newAddress; } + + bytes32 constant CREATE2_PREFIX = keccak256("zksyncCreate2"); + + /// Mocks `create2` with real counterpart semantics, other than bytecode passed in `_input`. + /// @param _input bytecode to publish + function create2( + bytes32 _salt, + bytes32 _bytecodeHash, + bytes calldata _input + ) external payable returns (address newAddress) { + KNOWN_CODE_STORAGE_CONTRACT.setEVMBytecodeHash(_bytecodeHash); + KNOWN_CODE_STORAGE_CONTRACT.publishEVMBytecode(_input); + + bytes32 hash = keccak256( + bytes.concat(CREATE2_PREFIX, bytes32(uint256(uint160(msg.sender))), _salt, _bytecodeHash) + ); + newAddress = address(uint160(uint256(hash))); + ACCOUNT_CODE_STORAGE_CONTRACT.storeAccountConstructedCodeHash(newAddress, _bytecodeHash); + } } interface IAccountCodeStorage { @@ -101,6 +120,16 @@ interface IRecursiveContract { function recurse(uint _depth) external returns (uint); } +interface IRecursiveDeployment { + struct EvmDeployment { + bytes32 bytecodeHash; + /// Has fixed length to enable array slicing. + bytes32 bytecode; + } + + function testRecursiveDeployment(EvmDeployment[] calldata _deployments) external; +} + /// Native incrementing library. Not actually a library to simplify deployment. contract IncrementingContract { // Should not collide with other storage slots @@ -154,7 +183,7 @@ uint constant EVM_EMULATOR_STIPEND = 1 << 30; /** * Mock EVM emulator used in low-level tests. */ -contract MockEvmEmulator is IRecursiveContract, IncrementingContract { +contract MockEvmEmulator is IRecursiveContract, IRecursiveDeployment, IncrementingContract { IAccountCodeStorage constant ACCOUNT_CODE_STORAGE_CONTRACT = IAccountCodeStorage(address(0x8002)); /// Set to `true` for testing logic sanity. @@ -210,7 +239,11 @@ contract MockEvmEmulator is IRecursiveContract, IncrementingContract { MockContractDeployer constant CONTRACT_DEPLOYER_CONTRACT = MockContractDeployer(address(0x8006)); /// Emulates EVM contract deployment and a subsequent call to it in a single transaction. - function testDeploymentAndCall(bytes32 _evmBytecodeHash, bytes calldata _evmBytecode) external validEvmEntry { + function testDeploymentAndCall( + bytes32 _evmBytecodeHash, + bytes calldata _evmBytecode, + bool _revert + ) external validEvmEntry { IRecursiveContract newContract = IRecursiveContract(CONTRACT_DEPLOYER_CONTRACT.create( _evmBytecodeHash, _evmBytecodeHash, @@ -222,6 +255,69 @@ contract MockEvmEmulator is IRecursiveContract, IncrementingContract { uint gasToSend = gasleft() - EVM_EMULATOR_STIPEND; require(newContract.recurse{gas: gasToSend}(5) == 120, "unexpected recursive result"); + require(!_revert, "requested revert"); + } + + function testCallToPreviousDeployment() external validEvmEntry { + IRecursiveContract newContract = IRecursiveContract(address(uint160(address(this)) + 1)); + require(address(newContract).code.length > 0, "contract code length"); + require(address(newContract).codehash != bytes32(0), "contract code hash"); + + uint gasToSend = gasleft() - EVM_EMULATOR_STIPEND; + require(newContract.recurse{gas: gasToSend}(5) == 120, "unexpected recursive result"); + } + + function testRecursiveDeployment(EvmDeployment[] calldata _deployments) external override validEvmEntry { + if (_deployments.length == 0) { + return; + } + + IRecursiveDeployment newContract = IRecursiveDeployment(CONTRACT_DEPLOYER_CONTRACT.create( + _deployments[0].bytecodeHash, + _deployments[0].bytecodeHash, + bytes.concat(_deployments[0].bytecode) + )); + uint gasToSend = gasleft() - EVM_EMULATOR_STIPEND; + newContract.testRecursiveDeployment{gas: gasToSend}(_deployments[1:]); + } + + function testDeploymentWithPartialRevert( + EvmDeployment[] calldata _deployments, + bool[] calldata _shouldRevert + ) external validEvmEntry { + require(_deployments.length == _shouldRevert.length, "length mismatch"); + + for (uint i = 0; i < _deployments.length; i++) { + uint gasToSend = gasleft() - EVM_EMULATOR_STIPEND; + try this.deployThenRevert{gas: gasToSend}( + _deployments[i], + bytes32(i), + _shouldRevert[i] + ) returns(address newAddress) { + require(!_shouldRevert[i], "unexpected deploy success"); + require(newAddress.code.length > 0, "contract code length"); + require(newAddress.codehash != bytes32(0), "contract code hash"); + } catch Error(string memory reason) { + require(_shouldRevert[i], "unexpected revert"); + require(keccak256(bytes(reason)) == keccak256("requested revert"), "unexpected error"); + } + } + } + + function deployThenRevert( + EvmDeployment calldata _deployment, + bytes32 _salt, + bool _shouldRevert + ) external validEvmEntry returns (address newAddress) { + newAddress = CONTRACT_DEPLOYER_CONTRACT.create2( + _salt, + _deployment.bytecodeHash, + bytes.concat(_deployment.bytecode) + ); + require(newAddress.code.length > 0, "contract code length"); + require(newAddress.codehash != bytes32(0), "contract code hash"); + + require(!_shouldRevert, "requested revert"); } fallback() external validEvmEntry { diff --git a/etc/env/base/contract_verifier.toml b/etc/env/base/contract_verifier.toml index 223e6f166f8b..952b8c70823d 100644 --- a/etc/env/base/contract_verifier.toml +++ b/etc/env/base/contract_verifier.toml @@ -1,7 +1,4 @@ [contract_verifier] compilation_timeout = 30 -polling_interval = 1000 prometheus_port = 3314 port = 3070 -url = "http://127.0.0.1:3070" -threads_per_server = 128 diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index 94758d92e180..a4005e9477a8 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -79,11 +79,8 @@ operations_manager: delay_interval: 100 contract_verifier: compilation_timeout: 240 - polling_interval: 1000 prometheus_port: 3318 port: 3070 - url: http://127.0.0.1:3070 - threads_per_server: 128 circuit_breaker: sync_interval_ms: 120000 @@ -377,6 +374,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/Cargo.lock b/prover/Cargo.lock index d68ef368a4aa..f119d4bd1951 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -1071,6 +1071,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5241cd7938b1b415942e943ea96f615953d500b50347b505b0b507080bad5a6f" +[[package]] +name = "const-decoder" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381abde2cdc1bc3817e394b24e05667a2dc89f37570cbd34d9c397d99e56e3f" +dependencies = [ + "compile-fmt", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -2616,6 +2625,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "0.14.29" @@ -7887,12 +7906,7 @@ dependencies = [ "rand 0.8.5", "secrecy 0.8.0", "serde", - "strum", - "strum_macros", - "time", "tracing", - "url", - "vise", "zksync_basic_types", "zksync_concurrency", "zksync_consensus_utils", @@ -8341,7 +8355,6 @@ dependencies = [ "secrecy 0.8.0", "serde_json", "serde_yaml", - "time", "tracing", "zksync_basic_types", "zksync_config", @@ -8358,10 +8371,10 @@ dependencies = [ "async-trait", "axum", "chrono", - "clap 4.5.4", "ctrlc", "debug-map-sorted", "futures 0.3.30", + "humantime-serde", "k8s-openapi", "kube", "once_cell", @@ -8371,9 +8384,10 @@ dependencies = [ "rustls", "serde", "serde_json", + "serde_yaml", "structopt", "strum", - "time", + "strum_macros", "tokio", "tracing", "tracing-subscriber", @@ -8381,10 +8395,7 @@ dependencies = [ "url", "vise", "zksync_config", - "zksync_core_leftovers", - "zksync_protobuf_config", "zksync_prover_job_monitor", - "zksync_types", "zksync_utils", "zksync_vlog", ] @@ -8496,9 +8507,9 @@ dependencies = [ "serde", "serde_with", "strum", - "zksync_multivm", "zksync_object_store", "zksync_types", + "zksync_vm_interface", ] [[package]] @@ -8622,6 +8633,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bigdecimal", + "const-decoder 0.4.0", "futures 0.3.30", "hex", "num", @@ -8685,7 +8697,7 @@ dependencies = [ [[package]] name = "zksync_vm2" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=df5bec3d04d64d434f9b0ccb285ba4681008f7b3#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "enum_dispatch", "primitive-types", @@ -8697,7 +8709,7 @@ dependencies = [ [[package]] name = "zksync_vm2_interface" version = "0.2.1" -source = "git+https://github.com/matter-labs/vm2.git?rev=df5bec3d04d64d434f9b0ccb285ba4681008f7b3#df5bec3d04d64d434f9b0ccb285ba4681008f7b3" +source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662e33e598c13ba41633#457d8a7eea9093af9440662e33e598c13ba41633" dependencies = [ "primitive-types", ] @@ -8747,7 +8759,7 @@ dependencies = [ "async-trait", "bincode", "circuit_definitions", - "const-decoder", + "const-decoder 0.3.0", "ctrlc", "futures 0.3.30", "jemallocator", diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 32c3185f64c3..e53efaae1968 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -28,6 +28,8 @@ debug-map-sorted = "0.1.1" dialoguer = "0.11" futures = "0.3" hex = "0.4" +humantime = "2.1" +humantime-serde = "1.1" indicatif = "0.16" itertools = "0.10.5" jemallocator = "0.5" @@ -47,12 +49,13 @@ rustls = { version = "0.23.12", features = ["ring"] } serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +serde_yaml = "0.9" sha3 = "0.10.8" sqlx = { version = "0.8.1", default-features = false } structopt = "0.3.26" strum = { version = "0.26" } +strum_macros = "0.26" tempfile = "3" -time = "0.3.36" tokio = "1" tokio-util = "0.7.11" toml_edit = "0.14.4" diff --git a/prover/crates/bin/prover_autoscaler/Cargo.toml b/prover/crates/bin/prover_autoscaler/Cargo.toml index fbf3ecae9098..4e66ecc2b0e3 100644 --- a/prover/crates/bin/prover_autoscaler/Cargo.toml +++ b/prover/crates/bin/prover_autoscaler/Cargo.toml @@ -10,22 +10,19 @@ keywords.workspace = true categories.workspace = true [dependencies] -zksync_core_leftovers.workspace = true zksync_vlog.workspace = true zksync_utils.workspace = true -zksync_types.workspace = true zksync_config = { workspace = true, features = ["observability_ext"] } zksync_prover_job_monitor.workspace = true -zksync_protobuf_config.workspace = true debug-map-sorted.workspace = true anyhow.workspace = true async-trait.workspace = true axum.workspace = true chrono.workspace = true -clap = { workspace = true, features = ["derive"] } ctrlc = { workspace = true, features = ["termination"] } futures.workspace = true +humantime-serde.workspace = true k8s-openapi = { workspace = true, features = ["v1_30"] } kube = { workspace = true, features = ["runtime", "derive"] } once_cell.workspace = true @@ -35,9 +32,10 @@ ring.workspace = true rustls = { workspace = true, features = ["ring"] } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true +serde_yaml.workspace = true structopt.workspace = true strum.workspace = true -time.workspace = true +strum_macros.workspace = true tokio = { workspace = true, features = ["time", "macros"] } tracing-subscriber = { workspace = true, features = ["env-filter"] } tracing.workspace = true diff --git a/core/lib/config/src/configs/prover_autoscaler.rs b/prover/crates/bin/prover_autoscaler/src/config.rs similarity index 85% rename from core/lib/config/src/configs/prover_autoscaler.rs rename to prover/crates/bin/prover_autoscaler/src/config.rs index 4191208b96e3..777ffe89fc91 100644 --- a/core/lib/config/src/configs/prover_autoscaler.rs +++ b/prover/crates/bin/prover_autoscaler/src/config.rs @@ -1,18 +1,20 @@ -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf, time::Duration}; +use anyhow::Context; use serde::Deserialize; use strum::Display; use strum_macros::EnumString; -use time::Duration; use vise::EncodeLabelValue; - -use crate::configs::ObservabilityConfig; +use zksync_config::configs::ObservabilityConfig; /// Config used for running ProverAutoscaler (both Scaler and Agent). -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub struct ProverAutoscalerConfig { /// Amount of time ProverJobMonitor will wait all it's tasks to finish. - // TODO: find a way to use #[serde(with = "humantime_serde")] with time::Duration. + #[serde( + with = "humantime_serde", + default = "ProverAutoscalerConfig::default_graceful_shutdown_timeout" + )] pub graceful_shutdown_timeout: Duration, pub agent_config: Option, pub scaler_config: Option, @@ -28,8 +30,6 @@ pub struct ProverAutoscalerAgentConfig { /// List of namespaces to watch. #[serde(default = "ProverAutoscalerAgentConfig::default_namespaces")] pub namespaces: Vec, - /// Watched cluster name. Also can be set via flag. - pub cluster_name: Option, /// If dry-run enabled don't do any k8s updates, just report success. #[serde(default = "ProverAutoscalerAgentConfig::default_dry_run")] pub dry_run: bool, @@ -40,7 +40,10 @@ pub struct ProverAutoscalerScalerConfig { /// Port for prometheus metrics connection. pub prometheus_port: u16, /// The interval between runs for global Scaler. - #[serde(default = "ProverAutoscalerScalerConfig::default_scaler_run_interval")] + #[serde( + with = "humantime_serde", + default = "ProverAutoscalerScalerConfig::default_scaler_run_interval" + )] pub scaler_run_interval: Duration, /// URL to get queue reports from. /// In production should be "http://prover-job-monitor.stage2.svc.cluster.local:3074/queue_report". @@ -59,7 +62,10 @@ pub struct ProverAutoscalerScalerConfig { /// Minimum number of provers per namespace. pub min_provers: HashMap, /// Duration after which pending pod considered long pending. - #[serde(default = "ProverAutoscalerScalerConfig::default_long_pending_duration")] + #[serde( + with = "humantime_serde", + default = "ProverAutoscalerScalerConfig::default_long_pending_duration" + )] pub long_pending_duration: Duration, /// List of simple autoscaler targets. pub scaler_targets: Vec, @@ -136,7 +142,7 @@ pub struct ScalerTarget { impl ProverAutoscalerConfig { /// Default graceful shutdown timeout -- 5 seconds pub fn default_graceful_shutdown_timeout() -> Duration { - Duration::seconds(5) + Duration::from_secs(5) } } @@ -153,7 +159,7 @@ impl ProverAutoscalerAgentConfig { impl ProverAutoscalerScalerConfig { /// Default scaler_run_interval -- 10s pub fn default_scaler_run_interval() -> Duration { - Duration::seconds(10) + Duration::from_secs(10) } /// Default prover_job_monitor_url -- cluster local URL @@ -163,7 +169,7 @@ impl ProverAutoscalerScalerConfig { /// Default long_pending_duration -- 10m pub fn default_long_pending_duration() -> Duration { - Duration::minutes(10) + Duration::from_secs(600) } } @@ -172,3 +178,9 @@ impl ScalerTarget { 1 } } + +pub fn config_from_yaml(path: &PathBuf) -> anyhow::Result { + let yaml = std::fs::read_to_string(path) + .with_context(|| format!("failed to read {}", path.display()))?; + Ok(serde_yaml::from_str(&yaml)?) +} diff --git a/prover/crates/bin/prover_autoscaler/src/global/queuer.rs b/prover/crates/bin/prover_autoscaler/src/global/queuer.rs index 7255f479647e..baeb5b70a4ef 100644 --- a/prover/crates/bin/prover_autoscaler/src/global/queuer.rs +++ b/prover/crates/bin/prover_autoscaler/src/global/queuer.rs @@ -2,11 +2,13 @@ use std::{collections::HashMap, ops::Deref}; use anyhow::{Context, Ok}; use reqwest::Method; -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}; +use crate::{ + config::QueueReportFields, + metrics::{AUTOSCALER_METRICS, DEFAULT_ERROR_CODE}, +}; const MAX_RETRIES: usize = 5; diff --git a/prover/crates/bin/prover_autoscaler/src/global/scaler.rs b/prover/crates/bin/prover_autoscaler/src/global/scaler.rs index dc652999da5f..829b95dd7514 100644 --- a/prover/crates/bin/prover_autoscaler/src/global/scaler.rs +++ b/prover/crates/bin/prover_autoscaler/src/global/scaler.rs @@ -4,14 +4,12 @@ use chrono::Utc; use debug_map_sorted::SortedOutputExt; use once_cell::sync::Lazy; use regex::Regex; -use zksync_config::configs::prover_autoscaler::{ - Gpu, ProverAutoscalerScalerConfig, QueueReportFields, ScalerTarget, -}; use super::{queuer, watcher}; use crate::{ agent::{ScaleDeploymentRequest, ScaleRequest}, cluster_types::{Cluster, Clusters, Pod, PodStatus}, + config::{Gpu, ProverAutoscalerScalerConfig, QueueReportFields, ScalerTarget}, metrics::AUTOSCALER_METRICS, task_wiring::Task, }; @@ -128,7 +126,7 @@ impl Scaler { simple_scalers.push(SimpleScaler::new( c, config.cluster_priorities.clone(), - chrono::Duration::seconds(config.long_pending_duration.whole_seconds()), + chrono::Duration::seconds(config.long_pending_duration.as_secs() as i64), )) } Self { @@ -150,7 +148,7 @@ impl GpuScaler { max_provers: config.max_provers, prover_speed: config.prover_speed, long_pending_duration: chrono::Duration::seconds( - config.long_pending_duration.whole_seconds(), + config.long_pending_duration.as_secs() as i64, ), } } diff --git a/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs b/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs index 707ff04f1836..b8476ab475ab 100644 --- a/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs +++ b/prover/crates/bin/prover_autoscaler/src/k8s/watcher.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, sync::Arc}; +use anyhow::Context; use chrono::{DateTime, Utc}; use futures::{stream, StreamExt, TryStreamExt}; use k8s_openapi::api; @@ -7,7 +8,12 @@ use kube::{ api::{Api, ResourceExt}, runtime::{watcher, WatchStreamExt}, }; +use reqwest::{ + header::{HeaderMap, HeaderValue}, + Method, +}; use tokio::sync::Mutex; +use zksync_utils::http_with_retries::send_request_with_retries; use crate::cluster_types::{Cluster, Deployment, Namespace, Pod, ScaleEvent}; @@ -17,13 +23,37 @@ pub struct Watcher { pub cluster: Arc>, } +async fn get_cluster_name() -> anyhow::Result { + let mut headers = HeaderMap::new(); + headers.insert("Metadata-Flavor", HeaderValue::from_static("Google")); + let url = "http://metadata.google.internal/computeMetadata/v1/instance/attributes/cluster-name"; + let response = send_request_with_retries(url, 5, Method::GET, Some(headers), None).await; + response + .map_err(|err| anyhow::anyhow!("Failed fetching response from url: {url}: {err:?}"))? + .text() + .await + .context("Failed to read response as text") +} + impl Watcher { - pub fn new(client: kube::Client, cluster_name: String, namespaces: Vec) -> Self { + pub async fn new( + client: kube::Client, + cluster_name: Option, + namespaces: Vec, + ) -> Self { let mut ns = HashMap::new(); namespaces.into_iter().for_each(|n| { ns.insert(n, Namespace::default()); }); + let cluster_name = match cluster_name { + Some(c) => c, + None => get_cluster_name() + .await + .expect("Load cluster_name from GCP"), + }; + tracing::info!("Agent cluster name is {cluster_name}"); + Self { client, cluster: Arc::new(Mutex::new(Cluster { diff --git a/prover/crates/bin/prover_autoscaler/src/lib.rs b/prover/crates/bin/prover_autoscaler/src/lib.rs index 0b0d704c9078..019fe2b7fb4d 100644 --- a/prover/crates/bin/prover_autoscaler/src/lib.rs +++ b/prover/crates/bin/prover_autoscaler/src/lib.rs @@ -1,5 +1,6 @@ pub mod agent; pub(crate) mod cluster_types; +pub mod config; pub mod global; pub mod k8s; pub(crate) mod metrics; diff --git a/prover/crates/bin/prover_autoscaler/src/main.rs b/prover/crates/bin/prover_autoscaler/src/main.rs index ac5121dccd9c..98ffdb49d824 100644 --- a/prover/crates/bin/prover_autoscaler/src/main.rs +++ b/prover/crates/bin/prover_autoscaler/src/main.rs @@ -6,10 +6,9 @@ use tokio::{ sync::{oneshot, watch}, task::JoinHandle, }; -use zksync_core_leftovers::temp_config_store::read_yaml_repr; -use zksync_protobuf_config::proto::prover_autoscaler; use zksync_prover_autoscaler::{ agent, + config::{config_from_yaml, ProverAutoscalerConfig}, global::{self}, k8s::{Scaler, Watcher}, task_wiring::TaskRunner, @@ -56,15 +55,11 @@ struct Opt { async fn main() -> anyhow::Result<()> { let opt = Opt::from_args(); let general_config = - read_yaml_repr::(&opt.config_path) - .context("general config")?; + config_from_yaml::(&opt.config_path).context("general config")?; let observability_config = general_config .observability .context("observability config")?; let _observability_guard = observability_config.install()?; - // That's unfortunate that there are at least 3 different Duration in rust and we use all 3 in this repo. - // TODO: Consider updating zksync_protobuf to support std::time::Duration. - let graceful_shutdown_timeout = general_config.graceful_shutdown_timeout.unsigned_abs(); let (stop_signal_sender, stop_signal_receiver) = oneshot::channel(); let mut stop_signal_sender = Some(stop_signal_sender); @@ -77,24 +72,20 @@ async fn main() -> anyhow::Result<()> { let (stop_sender, stop_receiver) = watch::channel(false); - let _ = rustls::crypto::ring::default_provider().install_default(); - let client = kube::Client::try_default().await?; - let mut tasks = vec![]; match opt.job { AutoscalerType::Agent => { - let cluster = opt - .cluster_name - .context("cluster_name is required for Agent")?; - tracing::info!("Starting ProverAutoscaler Agent for cluster {}", cluster); + tracing::info!("Starting ProverAutoscaler Agent"); let agent_config = general_config.agent_config.context("agent_config")?; let exporter_config = PrometheusExporterConfig::pull(agent_config.prometheus_port); tasks.push(tokio::spawn(exporter_config.run(stop_receiver.clone()))); - // TODO: maybe get cluster name from curl -H "Metadata-Flavor: Google" - // http://metadata.google.internal/computeMetadata/v1/instance/attributes/cluster-name - let watcher = Watcher::new(client.clone(), cluster, agent_config.namespaces); + let _ = rustls::crypto::ring::default_provider().install_default(); + let client = kube::Client::try_default().await?; + + let watcher = + Watcher::new(client.clone(), opt.cluster_name, agent_config.namespaces).await; let scaler = Scaler::new(client, agent_config.dry_run); tasks.push(tokio::spawn(watcher.clone().run())); tasks.push(tokio::spawn(agent::run_server( @@ -107,7 +98,7 @@ async fn main() -> anyhow::Result<()> { AutoscalerType::Scaler => { tracing::info!("Starting ProverAutoscaler Scaler"); let scaler_config = general_config.scaler_config.context("scaler_config")?; - let interval = scaler_config.scaler_run_interval.unsigned_abs(); + let interval = scaler_config.scaler_run_interval; let exporter_config = PrometheusExporterConfig::pull(scaler_config.prometheus_port); tasks.push(tokio::spawn(exporter_config.run(stop_receiver.clone()))); let watcher = @@ -127,7 +118,9 @@ async fn main() -> anyhow::Result<()> { } } stop_sender.send(true).ok(); - tasks.complete(graceful_shutdown_timeout).await; + tasks + .complete(general_config.graceful_shutdown_timeout) + .await; Ok(()) } diff --git a/prover/crates/bin/prover_autoscaler/src/metrics.rs b/prover/crates/bin/prover_autoscaler/src/metrics.rs index 39860a9e8f09..115ae3b74259 100644 --- a/prover/crates/bin/prover_autoscaler/src/metrics.rs +++ b/prover/crates/bin/prover_autoscaler/src/metrics.rs @@ -1,5 +1,6 @@ use vise::{Counter, Gauge, LabeledFamily, Metrics}; -use zksync_config::configs::prover_autoscaler::Gpu; + +use crate::config::Gpu; pub const DEFAULT_ERROR_CODE: u16 = 500; diff --git a/renovate.json b/renovate.json index eeccfee848dc..fd09d70ffe4b 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,13 @@ { - "extends": ["config:base", "schedule:earlyMondays","helpers:pinGitHubActionDigests"], + "extends": ["config:base", "helpers:pinGitHubActionDigests"], "enabledManagers": ["github-actions"], - "prCreation": "immediate" + "prCreation": "not-pending", + "groupName": "github actions monthly updates", + "schedule": ["monthly"], + "packageRules": [ + { + "managers": ["github-actions"], + "groupName": "all-github-actions-updates" + } + ] } diff --git a/resources/g1.point b/resources/g1.point new file mode 100644 index 000000000000..7e9cf49e5227 Binary files /dev/null and b/resources/g1.point differ diff --git a/resources/g2.point.powerOf2 b/resources/g2.point.powerOf2 new file mode 100644 index 000000000000..58e349b6e7d5 Binary files /dev/null and b/resources/g2.point.powerOf2 differ diff --git a/rust-toolchain b/rust-toolchain index 03c040b91f1f..bc5d1d6bbd8e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1,2 @@ -nightly-2024-08-01 +[toolchain] +channel = "nightly-2024-08-01" diff --git a/yarn.lock b/yarn.lock index 58511dd1b9ff..15fb8bb7d967 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1424,6 +1424,18 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -2303,6 +2315,11 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA== +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@pkgr/core@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" @@ -2633,6 +2650,16 @@ mkdirp "^2.1.6" path-browserify "^1.0.1" +"@ts-morph/common@~0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.23.0.tgz#bd4ddbd3f484f29476c8bd985491592ae5fc147e" + integrity sha512-m7Lllj9n/S6sOkCkRftpM7L24uvmfXQFedlW/4hENcuJH1HHm9u5EgxZb9uVjQSCGrbBWBkOGgcTxNg36r6ywA== + dependencies: + fast-glob "^3.3.2" + minimatch "^9.0.3" + mkdirp "^3.0.1" + path-browserify "^1.0.1" + "@tsconfig/node10@^1.0.7": version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" @@ -3305,6 +3332,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -3324,6 +3356,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + antlr4@^4.11.0: version "4.13.1" resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.1.tgz#1e0a1830a08faeb86217cb2e6c34716004e4253d" @@ -4183,6 +4220,11 @@ code-block-writer@^12.0.0: resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-12.0.0.tgz#4dd58946eb4234105aff7f0035977b2afdc2a770" integrity sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w== +code-block-writer@^13.0.1: + version "13.0.3" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-13.0.3.tgz#90f8a84763a5012da7af61319dd638655ae90b5b" + integrity sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg== + collect-v8-coverage@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" @@ -4435,7 +4477,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4738,6 +4780,11 @@ dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -4804,6 +4851,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encoding-down@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" @@ -5774,6 +5826,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -6062,6 +6122,18 @@ glob@8.1.0, glob@^8.0.3: minimatch "^5.0.1" once "^1.3.0" +glob@^10.4.1: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -6974,6 +7046,15 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -7877,6 +7958,11 @@ lowercase-keys@^3.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -8175,6 +8261,13 @@ minimatch@^7.4.3: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.3, minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimatch@~3.0.4: version "3.0.8" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" @@ -8187,6 +8280,11 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8, minimist@~1. resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -8209,6 +8307,11 @@ mkdirp@^2.1.6: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + mnemonist@^0.38.0: version "0.38.5" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" @@ -8664,6 +8767,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + package-json@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/package-json/-/package-json-8.1.1.tgz#3e9948e43df40d1e8e78a85485f1070bf8f03dc8" @@ -8739,6 +8847,14 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@^6.2.1: version "6.2.2" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36" @@ -9739,6 +9855,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + sinon-chai@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.7.0.tgz#cfb7dec1c50990ed18c153f1840721cf13139783" @@ -10070,6 +10191,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -10087,6 +10217,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2 is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.padend@^3.0.0: version "3.1.6" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz#ba79cf8992609a91c872daa47c6bb144ee7f62a5" @@ -10144,6 +10283,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" @@ -10165,6 +10311,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -10520,6 +10673,14 @@ ts-morph@^19.0.0: "@ts-morph/common" "~0.20.0" code-block-writer "^12.0.0" +ts-morph@^22.0.0: + version "22.0.0" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-22.0.0.tgz#5532c592fb6dddae08846f12c9ab0fc590b1d42e" + integrity sha512-M9MqFGZREyeb5fTl6gNHKZLqBQA0TjA1lea+CR48R8EBTDuWrNqW6ccC5QvjNR4s6wDumD3LTCjOFSp9iwlzaw== + dependencies: + "@ts-morph/common" "~0.23.0" + code-block-writer "^13.0.1" + ts-node@^10.1.0, ts-node@^10.7.0: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" @@ -11000,6 +11161,15 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -11009,6 +11179,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/zkstack_cli/Cargo.lock b/zkstack_cli/Cargo.lock index 7770d06a1977..a9089719714d 100644 --- a/zkstack_cli/Cargo.lock +++ b/zkstack_cli/Cargo.lock @@ -795,6 +795,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "const-decoder" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b381abde2cdc1bc3817e394b24e05667a2dc89f37570cbd34d9c397d99e56e3f" +dependencies = [ + "compile-fmt", +] + [[package]] name = "const-hex" version = "1.13.1" @@ -1088,7 +1097,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", - "serde", ] [[package]] @@ -6799,11 +6807,6 @@ dependencies = [ "rand", "secrecy", "serde", - "strum", - "strum_macros", - "time", - "url", - "vise", "zksync_basic_types", "zksync_concurrency", "zksync_consensus_utils", @@ -6953,7 +6956,6 @@ dependencies = [ "secrecy", "serde_json", "serde_yaml", - "time", "tracing", "zksync_basic_types", "zksync_config", @@ -7009,6 +7011,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bigdecimal", + "const-decoder", "futures", "hex", "num", diff --git a/zkstack_cli/crates/config/src/contracts.rs b/zkstack_cli/crates/config/src/contracts.rs index 6d336b5cfc17..79044a59f3af 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 29be89b91016..7b2b56c81548 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 f1cfc9946731..825fc967e6d7 100644 --- a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh +++ b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh @@ -1255,8 +1255,8 @@ esac ;; (lint) _arguments "${_arguments_options[@]}" : \ -'*-t+[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ -'*--targets=[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ +'*-t+[]:TARGETS:(md sol js ts rs contracts autocompletion rust-toolchain)' \ +'*--targets=[]:TARGETS:(md sol js ts rs contracts autocompletion rust-toolchain)' \ '--chain=[Chain to use]:CHAIN:_default' \ '-c[]' \ '--check[]' \ @@ -1309,8 +1309,8 @@ _arguments "${_arguments_options[@]}" : \ ;; (prettier) _arguments "${_arguments_options[@]}" : \ -'*-t+[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ -'*--targets=[]:TARGETS:(md sol js ts rs contracts autocompletion)' \ +'*-t+[]:TARGETS:(md sol js ts rs contracts autocompletion rust-toolchain)' \ +'*--targets=[]:TARGETS:(md sol js ts rs contracts autocompletion rust-toolchain)' \ '--chain=[Chain to use]:CHAIN:_default' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.fish b/zkstack_cli/crates/zkstack/completion/zkstack.fish index a1261082e6f0..7ad4e6959f90 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.fish +++ b/zkstack_cli/crates/zkstack/completion/zkstack.fish @@ -368,7 +368,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_sub complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from snapshot" -s h -l help -d 'Print help' complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from snapshot" -f -a "create" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from snapshot" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s t -l targets -r -f -a "{md\t'',sol\t'',js\t'',ts\t'',rs\t'',contracts\t'',autocompletion\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s t -l targets -r -f -a "{md\t'',sol\t'',js\t'',ts\t'',rs\t'',contracts\t'',autocompletion\t'',rust-toolchain\t''}" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s c -l check complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s v -l verbose -d 'Verbose mode' diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.sh b/zkstack_cli/crates/zkstack/completion/zkstack.sh index 7cdb20ae9aa7..ff351ebd79ed 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.sh +++ b/zkstack_cli/crates/zkstack/completion/zkstack.sh @@ -3104,11 +3104,11 @@ _zkstack() { fi case "${prev}" in --targets) - COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion" -- "${cur}")) + COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion rust-toolchain" -- "${cur}")) return 0 ;; -t) - COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion" -- "${cur}")) + COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion rust-toolchain" -- "${cur}")) return 0 ;; --chain) @@ -3768,11 +3768,11 @@ _zkstack() { fi case "${prev}" in --targets) - COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion" -- "${cur}")) + COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion rust-toolchain" -- "${cur}")) return 0 ;; -t) - COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion" -- "${cur}")) + COMPREPLY=($(compgen -W "md sol js ts rs contracts autocompletion rust-toolchain" -- "${cur}")) return 0 ;; --chain) 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 8dbd5c371c88..091bef86d26d 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(()) }, ) diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs index 6c3c3fa3d756..04955726706f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs @@ -41,6 +41,7 @@ pub fn run(shell: &Shell, args: LintArgs) -> anyhow::Result<()> { Target::Ts, Target::Contracts, Target::Autocompletion, + Target::RustToolchain, ] } else { args.targets.clone() @@ -55,6 +56,7 @@ pub fn run(shell: &Shell, args: LintArgs) -> anyhow::Result<()> { Target::Rs => lint_rs(shell, &ecosystem, args.check)?, Target::Contracts => lint_contracts(shell, &ecosystem, args.check)?, Target::Autocompletion => lint_autocompletion_files(shell, args.check)?, + Target::RustToolchain => check_rust_toolchain(shell)?, ext => lint(shell, &ecosystem, &ext, args.check)?, } } @@ -70,13 +72,18 @@ fn lint_rs(shell: &Shell, ecosystem: &EcosystemConfig, check: bool) -> anyhow::R let link_to_code = &ecosystem.link_to_code; let lint_to_prover = &ecosystem.link_to_code.join("prover"); let link_to_zkstack = &ecosystem.link_to_code.join("zkstack_cli"); - let paths = vec![link_to_code, lint_to_prover, link_to_zkstack]; spinner.freeze(); - for path in paths { + for path in [link_to_code, lint_to_prover, link_to_zkstack] { let _dir_guard = shell.push_dir(path); let mut cmd = cmd!(shell, "cargo clippy"); - let common_args = &["--locked", "--", "-D", "warnings"]; + let mut common_args = vec!["--locked", "--", "-D", "warnings"]; + + if !path.ends_with("prover") { + common_args.push("-D"); + common_args.push("unstable-features"); + } + if !check { cmd = cmd.args(&["--fix", "--allow-dirty"]); } @@ -87,6 +94,34 @@ fn lint_rs(shell: &Shell, ecosystem: &EcosystemConfig, check: bool) -> anyhow::R Ok(()) } +fn check_rust_toolchain(shell: &Shell) -> anyhow::Result<()> { + // deserialize /zkstack_cli/rust-toolchain as TOML + let path = Path::new("zkstack_cli/rust-toolchain"); + if !path.exists() { + logger::info("WARNING: Please run this command from the project's root folder"); + return Ok(()); + } + let contents = shell.read_file(path)?; + let zkstack_cli_toolchain: toml::Value = toml::from_str(&contents)?; + + // deserialize /rust-toolchain as TOML + let path = Path::new("rust-toolchain"); + let contents = shell.read_file(path)?; + let zksync_era_toolchain: toml::Value = toml::from_str(&contents)?; + + // check if the toolchains are the same + if zksync_era_toolchain["toolchain"]["channel"] != zkstack_cli_toolchain["toolchain"]["channel"] + { + bail!( + "The Rust toolchains are not the same: ZKsync Era: {} - ZK Stack CLI: {}", + zksync_era_toolchain["toolchain"]["channel"], + zkstack_cli_toolchain["toolchain"]["channel"] + ); + } + + Ok(()) +} + fn get_linter(target: &Target) -> Vec { match target { Target::Rs => vec!["cargo".to_string(), "clippy".to_string()], @@ -96,6 +131,7 @@ fn get_linter(target: &Target) -> Vec { Target::Ts => vec!["eslint".to_string(), "--ext".to_string(), "ts".to_string()], Target::Contracts => vec![], Target::Autocompletion => vec![], + Target::RustToolchain => vec![], } } @@ -183,7 +219,7 @@ fn lint_autocompletion_files(_shell: &Shell, check: bool) -> anyhow::Result<()> let mut autocomplete_file = File::create(path).context("Failed to create file")?; autocomplete_file.write_all(new.as_bytes())?; } else { - bail!("Autocompletion files need to be regenerated. Run `zkstack dev lint -t autocompletion` to fix this issue.") + bail!("Autocompletion files need to be regenerated. To fix this issue, follow these steps: 1) Build an updated ZK Stack CLI using `zkstackup --local`, 2) Run `zkstack dev lint -t autocompletion` to generate the updated files, and 3) Commit the newly generated files.") } } } diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint_utils.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint_utils.rs index 11a325047104..93184de76561 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint_utils.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint_utils.rs @@ -15,6 +15,7 @@ pub enum Target { Rs, Contracts, Autocompletion, + RustToolchain, } #[derive(Deserialize, Serialize, Debug)] diff --git a/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs b/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs index 5c8e10ba2d81..096c45da5d8f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs @@ -108,14 +108,15 @@ fn build_explorer_chain_config( // Get L2 RPC URL from general config let l2_rpc_url = general_config.get_l2_rpc_url()?; // Get Verification API URL from general config - let verification_api_url = general_config + let verification_api_port = general_config .contract_verifier .as_ref() - .map(|verifier| &verifier.url) - .context("verification_url")?; + .map(|verifier| verifier.port) + .context("verifier.port")?; + let verification_api_url = format!("http://127.0.0.1:{verification_api_port}"); // Build API URL let api_port = backend_config.ports.api_http_port; - let api_url = format!("http://127.0.0.1:{}", api_port); + let api_url = format!("http://127.0.0.1:{api_port}"); // Build explorer chain config Ok(ExplorerChainConfig { diff --git a/zkstack_cli/rust-toolchain b/zkstack_cli/rust-toolchain index 03c040b91f1f..bc5d1d6bbd8e 100644 --- a/zkstack_cli/rust-toolchain +++ b/zkstack_cli/rust-toolchain @@ -1 +1,2 @@ -nightly-2024-08-01 +[toolchain] +channel = "nightly-2024-08-01"