diff --git a/.github/labeler.yml b/.github/labeler.yml index 4805743d5..d705bad8a 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -3,6 +3,11 @@ - changed-files: - any-glob-to-any-file: mpt_trie/** +# Add 'crate: smt_trie' label to any changes within 'smt_trie' folder. +'crate: smt_trie': +- changed-files: + - any-glob-to-any-file: smt_trie/** + # Add 'crate: evm_arithmetization' label to any changes within 'evm_arithmetization' folder. 'crate: evm_arithmetization': - changed-files: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a786cf3e1..332f1c881 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,31 @@ jobs: CARGO_INCREMENTAL: 1 RUST_BACKTRACE: 1 + test_smt_trie: + name: Test smt_trie + runs-on: ubuntu-latest + timeout-minutes: 30 + if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Set up rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Test in smt_trie subdirectory + run: cargo test --manifest-path smt_trie/Cargo.toml + env: + RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 + RUST_LOG: 1 + CARGO_INCREMENTAL: 1 + RUST_BACKTRACE: 1 + test_trace_decoder: name: Test trace_decoder runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 894e1ee61..ba4b79bdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,9 +74,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.1.23" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1752d7d62e2665da650a36d84abbf239f812534475d51f072a49a533513b7cdd" +checksum = "cd47e5f8545bdf53beb545d3c039b4afa16040bdf69c50100581579b08776afd" dependencies = [ "num_enum", "strum", @@ -97,9 +97,9 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +checksum = "5af3faff14c12c8b11037e0a093dd157c3702becb8435577a2408534d0758315" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +checksum = "f783611babedbbe90db3478c120fb5f5daacceffc210b39adc0af4fe0da70bad" dependencies = [ "alloy-rlp", "bytes", @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.7" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43b18702501396fa9bcdeecd533bc85fac75150d308fc0f6800a01e6234a003" +checksum = "b155716bab55763c95ba212806cf43d05bcc70e5f35b02bad20cf5ec7fe11fed" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -217,13 +217,13 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.7" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83524c1f6162fcb5b0decf775498a125066c86dda6066ed609531b0e912f85a" +checksum = "8037e03c7f462a063f28daec9fda285a9a89da003c552f8637a80b9c8fd96241" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -310,23 +310,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +checksum = "4bad41a7c19498e3f6079f7744656328699f8ea3e783bdd10d85788cd439f572" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +checksum = "fd9899da7d011b4fe4c406a524ed3e3f963797dbc93b45479d60341d3a27b252" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -335,31 +335,31 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +checksum = "d32d595768fdc61331a132b6f65db41afae41b9b97d36c21eb1b955c422a7e60" dependencies = [ "const-hex", "dunce", "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", "syn-solidity", ] [[package]] name = "alloy-sol-types" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +checksum = "a49042c6d3b66a9fe6b2b5a8bf0d39fc2ae1ee0310a2a26ffedd79fb097878dd" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -663,7 +663,7 @@ checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", "synstructure", ] @@ -675,7 +675,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -819,7 +819,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -830,13 +830,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -862,7 +862,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -873,9 +873,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-lc-rs" -version = "1.8.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a47f2fb521b70c11ce7369a6c5fa4bd6af7e5d62ec06303875bafe7c6ba245" +checksum = "bf7d844e282b4b56750b2d4e893b2205581ded8709fddd2b6aa5418c150ca877" dependencies = [ "aws-lc-sys", "mirai-annotations", @@ -885,9 +885,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.19.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2927c7af777b460b7ccd95f8b67acd7b4c04ec8896bf0c8e80ba30523cffc057" +checksum = "c3a2c29203f6bf296d01141cc8bb9dbd5ecd4c27843f2ee0767bcd5985a927da" dependencies = [ "bindgen", "cc", @@ -1006,7 +1006,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -1019,7 +1019,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.70", + "syn 2.0.66", "which", ] @@ -1046,9 +1046,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" @@ -1195,9 +1195,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.0" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", @@ -1269,9 +1269,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.9" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -1279,9 +1279,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.9" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -1291,14 +1291,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.8" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -1629,7 +1629,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -1662,7 +1662,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.0", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -1697,13 +1697,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -1740,9 +1740,9 @@ dependencies = [ [[package]] name = "either" -version = "1.13.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "elliptic-curve" @@ -1778,7 +1778,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -1916,7 +1916,7 @@ dependencies = [ "itertools 0.13.0", "keccak-hash 0.10.0", "log", - "mpt_trie", + "mpt_trie 0.4.1", "num", "num-bigint", "once_cell", @@ -1933,10 +1933,47 @@ dependencies = [ "serde", "serde_json", "sha2", + "smt_trie 0.1.1", "starky", "static_assertions", "tiny-keccak", - "zk_evm_proc_macro", + "zk_evm_proc_macro 0.1.0", +] + +[[package]] +name = "evm_arithmetization" +version = "0.4.0" +source = "git+https://github.com/0xPolygonZero/zk_evm?branch=develop#33360556b0945272883c3f742c3fb404cd4c7fb0" +dependencies = [ + "anyhow", + "bytes", + "env_logger 0.11.3", + "ethereum-types", + "hashbrown", + "hex-literal", + "itertools 0.13.0", + "keccak-hash 0.10.0", + "log", + "mpt_trie 0.4.1 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", + "num", + "num-bigint", + "once_cell", + "pest", + "pest_derive", + "plonky2", + "plonky2_maybe_rayon", + "plonky2_util", + "rand", + "rand_chacha", + "rlp", + "rlp-derive", + "serde", + "serde_json", + "sha2", + "starky", + "static_assertions", + "tiny-keccak", + "zk_evm_proc_macro 0.1.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", ] [[package]] @@ -2148,7 +2185,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -2390,9 +2427,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", @@ -2426,9 +2463,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes", "futures-channel", @@ -2722,9 +2759,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" @@ -2745,7 +2782,7 @@ dependencies = [ "futures", "ops", "paladin-core", - "proof_gen", + "proof_gen 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "prover", "rpc", "serde", @@ -2766,12 +2803,12 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -2797,7 +2834,7 @@ checksum = "f8dccda732e04fa3baf2e17cf835bfe2601c7c2edafd64417c627dabae3a8cda" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -2825,9 +2862,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru" @@ -2924,6 +2961,29 @@ dependencies = [ "uint", ] +[[package]] +name = "mpt_trie" +version = "0.4.1" +source = "git+https://github.com/0xPolygonZero/zk_evm?branch=develop#33360556b0945272883c3f742c3fb404cd4c7fb0" +dependencies = [ + "bytes", + "enum-as-inner", + "ethereum-types", + "hex", + "impl-codec", + "impl-num-traits", + "impl-rlp", + "impl-serde", + "keccak-hash 0.10.0", + "log", + "num-traits", + "parking_lot", + "rlp", + "serde", + "thiserror", + "uint", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -2977,9 +3037,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ "num-integer", "num-traits", @@ -3070,7 +3130,7 @@ checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -3084,9 +3144,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -3108,9 +3168,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" -version = "11.1.4" +version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" @@ -3118,7 +3178,7 @@ version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -3135,7 +3195,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -3160,10 +3220,10 @@ dependencies = [ name = "ops" version = "0.1.0" dependencies = [ - "evm_arithmetization", + "evm_arithmetization 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "keccak-hash 0.10.0", "paladin-core", - "proof_gen", + "proof_gen 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "serde", "tracing", "zero_bin_common", @@ -3237,7 +3297,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af25dcb10b7c0ce99abee8694e2e79e4787d7f778b9339dc5a50ba6fc45e5cc9" dependencies = [ "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -3292,7 +3352,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -3328,9 +3388,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.11" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -3339,9 +3399,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -3349,22 +3409,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", @@ -3388,7 +3448,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -3628,7 +3688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -3689,9 +3749,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -3700,7 +3760,19 @@ dependencies = [ name = "proof_gen" version = "0.4.0" dependencies = [ - "evm_arithmetization", + "evm_arithmetization 0.4.0", + "log", + "paste", + "plonky2", + "serde", +] + +[[package]] +name = "proof_gen" +version = "0.4.0" +source = "git+https://github.com/0xPolygonZero/zk_evm?branch=develop#33360556b0945272883c3f742c3fb404cd4c7fb0" +dependencies = [ + "evm_arithmetization 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "log", "paste", "plonky2", @@ -3709,13 +3781,13 @@ dependencies = [ [[package]] name = "proptest" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand", @@ -3737,12 +3809,12 @@ dependencies = [ "num-traits", "ops", "paladin-core", - "proof_gen", + "proof_gen 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "ruint", "serde", "serde_json", "tokio", - "trace_decoder", + "trace_decoder 0.6.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "tracing", "zero_bin_common", ] @@ -3853,7 +3925,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", ] [[package]] @@ -4002,18 +4074,18 @@ dependencies = [ "anyhow", "clap", "compat", - "evm_arithmetization", + "evm_arithmetization 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "futures", "hex", "lru", - "mpt_trie", + "mpt_trie 0.4.1 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "primitive-types 0.12.2", "prover", "serde", "serde_json", "tokio", "tower", - "trace_decoder", + "trace_decoder 0.6.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "tracing-subscriber", "url", "zero_bin_common", @@ -4114,7 +4186,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys 0.4.14", @@ -4123,9 +4195,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.11" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ "aws-lc-rs", "log", @@ -4151,9 +4223,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.7.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -4180,9 +4252,9 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.5" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "aws-lc-rs", "ring", @@ -4278,7 +4350,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -4324,22 +4396,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.204" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -4476,6 +4548,18 @@ dependencies = [ "serde", ] +[[package]] +name = "smt_trie" +version = "0.1.1" +source = "git+https://github.com/0xPolygonZero/zk_evm?branch=develop#33360556b0945272883c3f742c3fb404cd4c7fb0" +dependencies = [ + "ethereum-types", + "hex-literal", + "plonky2", + "rand", + "serde", +] + [[package]] name = "socket2" version = "0.4.10" @@ -4552,9 +4636,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ "strum_macros", ] @@ -4569,14 +4653,14 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] name = "subtle" -version = "2.6.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -4591,9 +4675,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.70" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -4602,14 +4686,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +checksum = "8d71e19bca02c807c9faa67b5a47673ff231b6e7449b251695188522f1dc44b2" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -4632,7 +4716,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -4691,7 +4775,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -4816,7 +4900,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -4877,7 +4961,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.15", + "toml_edit 0.22.14", ] [[package]] @@ -4902,9 +4986,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.15" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -4946,7 +5030,7 @@ name = "trace_decoder" version = "0.6.0" dependencies = [ "anyhow", - "bitflags 2.6.0", + "bitflags 2.5.0", "bitvec", "bytes", "ciborium", @@ -4955,20 +5039,51 @@ dependencies = [ "either", "enum-as-inner", "ethereum-types", - "evm_arithmetization", + "evm_arithmetization 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "hex", "hex-literal", "itertools 0.13.0", "keccak-hash 0.10.0", "log", - "mpt_trie", + "mpt_trie 0.4.1 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "nunny", "plonky2", "pretty_env_logger", "rlp", "serde", "serde_json", - "smt_trie", + "smt_trie 0.1.1", + "thiserror", + "u4", + "winnow 0.6.13", +] + +[[package]] +name = "trace_decoder" +version = "0.6.0" +source = "git+https://github.com/0xPolygonZero/zk_evm?branch=develop#33360556b0945272883c3f742c3fb404cd4c7fb0" +dependencies = [ + "anyhow", + "bitflags 2.5.0", + "bitvec", + "bytes", + "ciborium", + "ciborium-io", + "either", + "enum-as-inner", + "ethereum-types", + "evm_arithmetization 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", + "hex", + "hex-literal", + "itertools 0.13.0", + "keccak-hash 0.10.0", + "log", + "mpt_trie 0.4.1 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", + "nunny", + "plonky2", + "rlp", + "serde", + "smt_trie 0.1.1 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "thiserror", "u4", "winnow 0.6.13", @@ -4994,7 +5109,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -5152,9 +5267,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", "rand", @@ -5180,7 +5295,7 @@ dependencies = [ "anyhow", "clap", "dotenvy", - "proof_gen", + "proof_gen 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "serde_json", "serde_path_to_error", "tracing", @@ -5255,7 +5370,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -5289,7 +5404,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5378,7 +5493,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.52.5", ] [[package]] @@ -5398,18 +5513,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -5420,9 +5535,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -5432,9 +5547,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -5444,15 +5559,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -5462,9 +5577,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -5474,9 +5589,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -5486,9 +5601,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -5498,9 +5613,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -5590,10 +5705,10 @@ dependencies = [ "anyhow", "async-stream", "clap", - "evm_arithmetization", + "evm_arithmetization 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "futures", "plonky2", - "proof_gen", + "proof_gen 0.4.0 (git+https://github.com/0xPolygonZero/zk_evm?branch=develop)", "serde", "serde_json", "thiserror", @@ -5603,22 +5718,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -5638,7 +5753,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", ] [[package]] @@ -5647,6 +5762,16 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.66", "trybuild", ] + +[[package]] +name = "zk_evm_proc_macro" +version = "0.1.0" +source = "git+https://github.com/0xPolygonZero/zk_evm?branch=develop#33360556b0945272883c3f742c3fb404cd4c7fb0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] diff --git a/evm_arithmetization/Cargo.toml b/evm_arithmetization/Cargo.toml index 45e9ccb8f..a70e1d5cb 100644 --- a/evm_arithmetization/Cargo.toml +++ b/evm_arithmetization/Cargo.toml @@ -46,6 +46,7 @@ serde_json = { workspace = true } # Local dependencies mpt_trie = { workspace = true } +smt_trie = { workspace = true } zk_evm_proc_macro = { workspace = true } [dev-dependencies] @@ -56,6 +57,7 @@ ripemd = { workspace = true } [features] default = ["parallel"] asmtools = ["hex"] +disable_jemalloc = [] parallel = [ "plonky2/parallel", "plonky2_maybe_rayon/parallel", diff --git a/evm_arithmetization/benches/fibonacci_25m_gas.rs b/evm_arithmetization/benches/fibonacci_25m_gas.rs index 9959acbc1..fe0c74ddf 100644 --- a/evm_arithmetization/benches/fibonacci_25m_gas.rs +++ b/evm_arithmetization/benches/fibonacci_25m_gas.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use ethereum_types::{Address, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use evm_arithmetization::cpu::kernel::aggregator::KERNEL; use evm_arithmetization::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; @@ -17,16 +17,18 @@ use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::testing::simulate_execution; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, - preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, - GLOBAL_EXIT_ROOT_ACCOUNT, + preinitialized_state, preinitialized_state_with_updated_storage, }; use evm_arithmetization::Node; use hex_literal::hex; -use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::db::Db; +use smt_trie::keys::{key_balance, key_code, key_code_length, key_nonce, key_storage}; +use smt_trie::smt::Smt; +use smt_trie::utils::hashout2u; type F = GoldilocksField; @@ -55,12 +57,6 @@ fn prepare_setup() -> anyhow::Result { let sender = hex!("8943545177806ED17B9F23F0a21ee5948eCaa776"); let to = hex!("159271B89fea49aF29DFaf8b4eCE7D042D5d6f07"); - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - let push1 = get_push_opcode(1); let push4 = get_push_opcode(4); let add = get_opcode("ADD"); @@ -71,36 +67,39 @@ fn prepare_setup() -> anyhow::Result { let code = [ push1, 1, push1, 1, jumpdest, dup2, add, swap1, push4, 0, 0, 0, 4, jump, ]; - let code_hash = keccak(code); - - let empty_trie_root = HashedPartialTrie::from(Node::Empty).hash(); + let code_hash = hash_bytecode_u256(code.to_vec()); let sender_account_before = AccountRlp { nonce: 169.into(), balance: U256::from_dec_str("999999999998417410153631615")?, - storage_root: empty_trie_root, - code_hash: keccak(vec![]), + code_hash: hash_bytecode_u256(vec![]), + code_length: 0.into(), }; let to_account_before = AccountRlp { nonce: 1.into(), balance: 0.into(), - storage_root: empty_trie_root, code_hash, + code_length: code.len().into(), }; - let (mut state_trie_before, mut storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec())?; - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec())?; - - storage_tries.push((sender_state_key, Node::Empty.into())); - storage_tries.push((to_state_key, Node::Empty.into())); + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries, }; let gas_used = U256::from(0x17d7840_u32); @@ -123,7 +122,7 @@ fn prepare_setup() -> anyhow::Result { }; let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); contract_code.insert(code_hash, code.to_vec()); let sender_account_after = AccountRlp { @@ -133,25 +132,20 @@ fn prepare_setup() -> anyhow::Result { }; let to_account_after = to_account_before; - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec())?; - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec())?; - - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = beacon_roots_contract_from_storage(&beacon_roots_account_storage); - expected_state_trie_after.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - expected_state_trie_after.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; + let mut expected_state_smt_after = + preinitialized_state_with_updated_storage(&block_metadata, &[]); + set_account( + &mut expected_state_smt_after, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account( + &mut expected_state_smt_after, + H160(to), + &to_account_after, + &HashMap::new(), + ); let receipt_0 = LegacyReceiptRlp { status: false, @@ -171,7 +165,7 @@ fn prepare_setup() -> anyhow::Result { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -197,6 +191,21 @@ fn prepare_setup() -> anyhow::Result { }) } +fn set_account( + smt: &mut Smt, + addr: Address, + account: &AccountRlp, + storage: &HashMap, +) { + smt.set(key_balance(addr), account.balance); + smt.set(key_nonce(addr), account.nonce); + smt.set(key_code(addr), account.code_hash); + smt.set(key_code_length(addr), account.code_length); + for (&k, &v) in storage { + smt.set(key_storage(addr, k), v); + } +} + fn init_logger() { let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); } diff --git a/evm_arithmetization/src/all_stark.rs b/evm_arithmetization/src/all_stark.rs index f8422b300..12e4aa8c4 100644 --- a/evm_arithmetization/src/all_stark.rs +++ b/evm_arithmetization/src/all_stark.rs @@ -23,6 +23,9 @@ use crate::logic; use crate::logic::LogicStark; use crate::memory::memory_stark; use crate::memory::memory_stark::MemoryStark; +use crate::poseidon::columns::POSEIDON_SPONGE_RATE; +use crate::poseidon::poseidon_stark::PoseidonStark; +use crate::poseidon::poseidon_stark::{self, FELT_MAX_BYTES}; /// Structure containing all STARKs and the cross-table lookups. #[derive(Clone)] @@ -34,6 +37,7 @@ pub struct AllStark, const D: usize> { pub(crate) keccak_sponge_stark: KeccakSpongeStark, pub(crate) logic_stark: LogicStark, pub(crate) memory_stark: MemoryStark, + pub(crate) poseidon_stark: PoseidonStark, pub(crate) cross_table_lookups: Vec>, } @@ -49,6 +53,7 @@ impl, const D: usize> Default for AllStark { keccak_sponge_stark: KeccakSpongeStark::default(), logic_stark: LogicStark::default(), memory_stark: MemoryStark::default(), + poseidon_stark: PoseidonStark::default(), cross_table_lookups: all_cross_table_lookups(), } } @@ -64,6 +69,7 @@ impl, const D: usize> AllStark { self.keccak_sponge_stark.num_lookup_helper_columns(config), self.logic_stark.num_lookup_helper_columns(config), self.memory_stark.num_lookup_helper_columns(config), + 0, ] } } @@ -80,6 +86,7 @@ pub enum Table { KeccakSponge = 4, Logic = 5, Memory = 6, + Poseidon = 7, } impl Deref for Table { @@ -88,12 +95,12 @@ impl Deref for Table { fn deref(&self) -> &Self::Target { // Hacky way to implement `Deref` for `Table` so that we don't have to // call `Table::Foo as usize`, but perhaps too ugly to be worth it. - [&0, &1, &2, &3, &4, &5, &6][*self as TableIdx] + [&0, &1, &2, &3, &4, &5, &6, &7][*self as TableIdx] } } /// Number of STARK tables. -pub(crate) const NUM_TABLES: usize = Table::Memory as usize + 1; +pub(crate) const NUM_TABLES: usize = Table::Poseidon as usize + 1; impl Table { /// Returns all STARK table indices. @@ -106,6 +113,7 @@ impl Table { Self::KeccakSponge, Self::Logic, Self::Memory, + Self::Poseidon, ] } } @@ -120,6 +128,9 @@ pub(crate) fn all_cross_table_lookups() -> Vec> { ctl_keccak_outputs(), ctl_logic(), ctl_memory(), + ctl_poseidon_simple(), + ctl_poseidon_general_input(), + ctl_poseidon_general_output(), ] } @@ -287,6 +298,14 @@ fn ctl_memory() -> CrossTableLookup { byte_packing_stark::ctl_looking_memory_filter(i), ) }); + + let poseidon_general_reads = (0..FELT_MAX_BYTES * POSEIDON_SPONGE_RATE).map(|i| { + TableWithColumns::new( + *Table::Poseidon, + poseidon_stark::ctl_looking_memory(i), + poseidon_stark::ctl_looking_memory_filter(), + ) + }); let all_lookers = vec![ cpu_memory_code_read, cpu_push_write_ops, @@ -297,6 +316,7 @@ fn ctl_memory() -> CrossTableLookup { .chain(cpu_memory_gp_ops) .chain(keccak_sponge_reads) .chain(byte_packing_ops) + .chain(poseidon_general_reads) .collect(); let memory_looked = TableWithColumns::new( *Table::Memory, @@ -305,3 +325,24 @@ fn ctl_memory() -> CrossTableLookup { ); CrossTableLookup::new(all_lookers, memory_looked) } + +fn ctl_poseidon_simple() -> CrossTableLookup { + CrossTableLookup::new( + vec![cpu_stark::ctl_poseidon_simple_op()], + poseidon_stark::ctl_looked_simple_op(), + ) +} + +fn ctl_poseidon_general_input() -> CrossTableLookup { + CrossTableLookup::new( + vec![cpu_stark::ctl_poseidon_general_input()], + poseidon_stark::ctl_looked_general_input(), + ) +} + +fn ctl_poseidon_general_output() -> CrossTableLookup { + CrossTableLookup::new( + vec![cpu_stark::ctl_poseidon_general_output()], + poseidon_stark::ctl_looked_general_output(), + ) +} diff --git a/evm_arithmetization/src/cpu/columns/ops.rs b/evm_arithmetization/src/cpu/columns/ops.rs index c3d1281a6..adc6b9a63 100644 --- a/evm_arithmetization/src/cpu/columns/ops.rs +++ b/evm_arithmetization/src/cpu/columns/ops.rs @@ -20,6 +20,8 @@ pub(crate) struct OpsColumnsView { pub shift: T, /// Combines JUMPDEST and KECCAK_GENERAL flags. pub jumpdest_keccak_general: T, + /// Combines POSEIDON and POSEIDON_GENERAL flags. + pub poseidon: T, /// Combines JUMP and JUMPI flags. pub jumps: T, /// Combines PUSH and PROVER_INPUT flags. diff --git a/evm_arithmetization/src/cpu/contextops.rs b/evm_arithmetization/src/cpu/contextops.rs index 6a7abed89..c3d4640af 100644 --- a/evm_arithmetization/src/cpu/contextops.rs +++ b/evm_arithmetization/src/cpu/contextops.rs @@ -23,6 +23,7 @@ const KEEPS_CONTEXT: OpsColumnsView = OpsColumnsView { not_pop: true, shift: true, jumpdest_keccak_general: true, + poseidon: true, push_prover_input: true, jumps: true, pc_push0: true, diff --git a/evm_arithmetization/src/cpu/control_flow.rs b/evm_arithmetization/src/cpu/control_flow.rs index ba4e71890..aa0d42d2e 100644 --- a/evm_arithmetization/src/cpu/control_flow.rs +++ b/evm_arithmetization/src/cpu/control_flow.rs @@ -8,7 +8,7 @@ use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsume use crate::cpu::columns::{CpuColumnsView, COL_MAP}; use crate::cpu::kernel::aggregator::KERNEL; -const NATIVE_INSTRUCTIONS: [usize; 12] = [ +const NATIVE_INSTRUCTIONS: [usize; 13] = [ COL_MAP.op.binary_op, COL_MAP.op.ternary_op, COL_MAP.op.fp254_op, @@ -17,6 +17,7 @@ const NATIVE_INSTRUCTIONS: [usize; 12] = [ COL_MAP.op.not_pop, COL_MAP.op.shift, COL_MAP.op.jumpdest_keccak_general, + COL_MAP.op.poseidon, // Not PROVER_INPUT: it is dealt with manually below. // not JUMPS (possible need to jump) COL_MAP.op.pc_push0, diff --git a/evm_arithmetization/src/cpu/cpu_stark.rs b/evm_arithmetization/src/cpu/cpu_stark.rs index 21c61f1e6..590c85236 100644 --- a/evm_arithmetization/src/cpu/cpu_stark.rs +++ b/evm_arithmetization/src/cpu/cpu_stark.rs @@ -428,6 +428,73 @@ pub(crate) fn ctl_filter_set_context() -> Filter { ) } +/// Returns the `TableWithColumns` for the CPU rows calling POSEIDON. +pub(crate) fn ctl_poseidon_simple_op() -> TableWithColumns { + let mut columns = Vec::new(); + for channel in 0..3 { + for i in 0..VALUE_LIMBS / 2 { + columns.push(Column::linear_combination([ + (COL_MAP.mem_channels[channel].value[2 * i], F::ONE), + ( + COL_MAP.mem_channels[channel].value[2 * i + 1], + F::from_canonical_u64(1 << 32), + ), + ])); + } + } + columns.extend(Column::singles_next_row(COL_MAP.mem_channels[0].value)); + TableWithColumns::new(*Table::Cpu, columns, ctl_poseidon_simple_filter()) +} + +pub(crate) fn ctl_poseidon_general_input() -> TableWithColumns { + // When executing POSEIDON_GENERAL, the GP memory channels are used as follows: + // GP channel 0: stack[-1] = addr (context, segment, virt) + // GP channel 1: stack[-2] = len + let (context, segment, virt) = get_addr(&COL_MAP, 0); + let context = Column::single(context); + let segment: Column = Column::single(segment); + let virt = Column::single(virt); + let len = Column::single(COL_MAP.mem_channels[1].value[0]); + + let num_channels = F::from_canonical_usize(NUM_CHANNELS); + let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); + + TableWithColumns::new( + *Table::Cpu, + vec![context, segment, virt, len, timestamp], + ctl_poseidon_general_filter(), + ) +} + +pub(crate) fn ctl_poseidon_simple_filter() -> Filter { + Filter::new( + vec![( + Column::single(COL_MAP.op.poseidon), + Column::linear_combination_with_constant([(COL_MAP.opcode_bits[0], -F::ONE)], F::ONE), + )], + vec![], + ) +} + +pub(crate) fn ctl_poseidon_general_filter() -> Filter { + Filter::new( + vec![( + Column::single(COL_MAP.op.poseidon), + Column::single(COL_MAP.opcode_bits[0]), + )], + vec![], + ) +} + +/// Returns the `TableWithColumns` for the CPU rows calling POSEIDON_GENERAL. +pub(crate) fn ctl_poseidon_general_output() -> TableWithColumns { + let mut columns = Vec::new(); + columns.extend(Column::singles_next_row(COL_MAP.mem_channels[0].value)); + let num_channels = F::from_canonical_usize(NUM_CHANNELS); + columns.push(Column::linear_combination([(COL_MAP.clock, num_channels)])); + TableWithColumns::new(*Table::Cpu, columns, ctl_poseidon_general_filter()) +} + /// Disable the specified memory channels. /// Since channel 0 contains the top of the stack and is handled specially, /// channels to disable are 1, 2 or both. All cases can be expressed as a vec. diff --git a/evm_arithmetization/src/cpu/decode.rs b/evm_arithmetization/src/cpu/decode.rs index 0dfb65be5..f8c9dac61 100644 --- a/evm_arithmetization/src/cpu/decode.rs +++ b/evm_arithmetization/src/cpu/decode.rs @@ -29,13 +29,14 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP}; /// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to /// decode to `is_invalid`. The kernel then verifies that the opcode was /// _actually_ invalid. -const OPCODES: [(u8, usize, bool, usize); 5] = [ +const OPCODES: [(u8, usize, bool, usize); 6] = [ // (start index of block, number of top bits to check (log2), kernel-only, flag column) // ADD, MUL, SUB, DIV, MOD, LT, GT and BYTE flags are handled partly manually here, and partly // through the Arithmetic table CTL. ADDMOD, MULMOD and SUBMOD flags are handled partly // manually here, and partly through the Arithmetic table CTL. FP254 operation flags are // handled partly manually here, and partly through the Arithmetic table CTL. (0x14, 1, false, COL_MAP.op.eq_iszero), + (0x22, 1, true, COL_MAP.op.poseidon), // AND, OR and XOR flags are handled partly manually here, and partly through the Logic table // CTL. NOT and POP are handled manually here. // SHL and SHR flags are handled partly manually here, and partly through the Logic table CTL. diff --git a/evm_arithmetization/src/cpu/gas.rs b/evm_arithmetization/src/cpu/gas.rs index c3ec89089..614cca8e5 100644 --- a/evm_arithmetization/src/cpu/gas.rs +++ b/evm_arithmetization/src/cpu/gas.rs @@ -27,8 +27,9 @@ const SIMPLE_OPCODES: OpsColumnsView> = OpsColumnsView { not_pop: None, // This is handled manually below shift: G_VERYLOW, jumpdest_keccak_general: None, // This is handled manually below. - push_prover_input: None, // This is handled manually below. - jumps: None, // Combined flag handled separately. + poseidon: KERNEL_ONLY_INSTR, + push_prover_input: None, // This is handled manually below. + jumps: None, // Combined flag handled separately. pc_push0: G_BASE, dup_swap: G_VERYLOW, context_op: KERNEL_ONLY_INSTR, diff --git a/evm_arithmetization/src/cpu/kernel/aggregator.rs b/evm_arithmetization/src/cpu/kernel/aggregator.rs index 746181dfe..6c742bd4f 100644 --- a/evm_arithmetization/src/cpu/kernel/aggregator.rs +++ b/evm_arithmetization/src/cpu/kernel/aggregator.rs @@ -1,5 +1,7 @@ //! Loads each kernel assembly file and concatenates them. +use std::collections::HashSet; + use itertools::Itertools; use once_cell::sync::Lazy; @@ -7,7 +9,7 @@ use super::assembler::{assemble, Kernel}; use crate::cpu::kernel::constants::evm_constants; use crate::cpu::kernel::parser::parse; -pub const NUMBER_KERNEL_FILES: usize = 156; +pub const NUMBER_KERNEL_FILES: usize = 162; pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ "global jumped_to_0: PANIC", @@ -131,6 +133,12 @@ pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ include_str!("asm/mpt/storage/storage_read.asm"), include_str!("asm/mpt/storage/storage_write.asm"), include_str!("asm/mpt/util.asm"), + include_str!("asm/smt/delete.asm"), + include_str!("asm/smt/hash.asm"), + include_str!("asm/smt/insert.asm"), + include_str!("asm/smt/keys.asm"), + include_str!("asm/smt/read.asm"), + include_str!("asm/smt/utils.asm"), include_str!("asm/rlp/decode.asm"), include_str!("asm/rlp/encode.asm"), include_str!("asm/rlp/encode_rlp_scalar.asm"), @@ -173,7 +181,7 @@ pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ pub static KERNEL: Lazy = Lazy::new(combined_kernel); pub(crate) fn combined_kernel_from_files(files: [&str; N]) -> Kernel { - let parsed_files = files.iter().map(|f| parse(f)).collect_vec(); + let parsed_files = files.iter().map(|f| parse(f, HashSet::new())).collect_vec(); assemble(parsed_files, evm_constants(), true) } diff --git a/evm_arithmetization/src/cpu/kernel/asm/account_code.asm b/evm_arithmetization/src/cpu/kernel/asm/account_code.asm index 2654bedc7..bc8abc5f8 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/account_code.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/account_code.asm @@ -24,17 +24,9 @@ extcodehash_dead: global extcodehash: // stack: address, retdest - %mpt_read_state_trie - // stack: account_ptr, retdest - DUP1 ISZERO %jumpi(retzero) - %add_const(3) - // stack: codehash_ptr, retdest - %mload_trie_data + %key_code %smt_read_state %mload_trie_data // stack: codehash, retdest SWAP1 JUMP -retzero: - %stack (account_ptr, retdest) -> (retdest, 0) - JUMP %macro extcodehash %stack (address) -> (address, %%after) @@ -44,7 +36,7 @@ retzero: %macro ext_code_empty %extcodehash - %eq_const(@EMPTY_STRING_HASH) + %eq_const(@EMPTY_STRING_POSEIDON_HASH) %endmacro %macro extcodesize @@ -76,11 +68,9 @@ global sys_extcodesize: global extcodesize: // stack: address, retdest - %next_context_id - // stack: codesize_ctx, address, retdest - SWAP1 - // stack: address, codesize_ctx, retdest - %jump(load_code) + %key_code_length %smt_read_state %mload_trie_data + // stack: codesize, retdest + SWAP1 JUMP // Loads the code at `address` into memory, in the code segment of the given context, starting at offset 0. // Checks that the hash of the loaded code corresponds to the `codehash` in the state trie. @@ -96,14 +86,8 @@ load_code_ctd: DUP1 ISZERO %jumpi(load_code_non_existent_account) // Load the code non-deterministically in memory and return the length. PROVER_INPUT(account_code) - %stack (code_size, codehash, ctx, retdest) -> (ctx, code_size, codehash, retdest, code_size) - // Check that the hash of the loaded code equals `codehash`. - // ctx == DST, as SEGMENT_CODE == offset == 0. - KECCAK_GENERAL - // stack: shouldbecodehash, codehash, retdest, code_size - %assert_eq - // stack: retdest, code_size - JUMP + // stack: padded_code_size, codehash, ctx, retdest + %jump(poseidon_hash_code) load_code_non_existent_account: // Write 0 at address 0 for soundness: SEGMENT_CODE == 0, hence ctx == addr. @@ -134,3 +118,87 @@ load_code_padded_ctd: MSTORE_GENERAL // stack: retdest, code_size JUMP +global poseidon_hash_code: + // stack: padded_code_size, codehash, ctx, retdest + // %stack (padded_code_size, codehash, ctx) -> (0, 0, padded_code_size, ctx, codehash) + %stack (padded_code_size, codehash, ctx) -> (ctx, padded_code_size, codehash, padded_code_size, ctx) + POSEIDON_GENERAL + %assert_eq + // stack: padded_code_size, ctx, retdest + %decrement +remove_padding_loop: + // stack: offset, ctx, retdest + DUP2 DUP2 ADD DUP1 MLOAD_GENERAL + // stack: code[offset], offset+ctx, offset, ctx, retdest + SWAP1 PUSH 0 MSTORE_GENERAL + // stack: code[offset], offset, ctx, retdest + %and_const(1) %jumpi(remove_padding_after) + // stack: offset, ctx, retdest + %decrement %jump(remove_padding_loop) + +remove_padding_after: + %stack (offset, ctx, retdest) -> (retdest, offset) + JUMP + +// Convenience macro to call poseidon_hash_code_unpadded and return where we left off. +%macro poseidon_hash_code_unpadded + %stack (addr, len) -> (addr, len, %%after) + %jump(poseidon_hash_code_unpadded) +%%after: +%endmacro + +/// Applies the padding rule to the code located at the provided address before hashing it. +/// Memory cells after the last code byte will be overwritten. +global poseidon_hash_code_unpadded: + // stack: addr, len, retdest + DUP2 ISZERO %jumpi(poseidon_empty_code) + DUP2 DUP2 ADD + // stack: padding_addr, addr, len, retdest + + // write 1 after the last code byte + DUP1 PUSH 1 MSTORE_GENERAL + // stack: padding_addr, addr, len, retdest + %increment + // stack: padding_addr, addr, len, retdest + + // Pad with 0s until the length is a multiple of 56 + PUSH 56 + DUP4 %increment + // stack: curr_len, 56, padding_addr, addr, len, retdest + PUSH 56 SUB + // stack: 56 - curr_len, 56, padding_addr, addr, len, retdest + MOD + // stack: padding_len, padding_addr, addr, len, retdest + SWAP3 DUP4 + // stack: padding_len, len, padding_addr, addr, padding_len, retdest + ADD + // stack: last_byte_offset, padding_addr, addr, padding_len, retdest + %stack (last_byte_offset, padding_addr, addr, padding_len) + -> (padding_addr, padding_len, after_padding, addr, last_byte_offset) + %jump(memset) +after_padding: + // stack: addr, last_byte_offset, retdest + + // Xor the last element with 0x80 + PUSH 1 DUP3 ADD + // stack: total_code_len, addr, last_byte_offset, retdest + SWAP2 + // stack: last_byte_offset, addr, total_code_len, retdest + DUP2 ADD + // stack: last_byte_addr, addr, total_code_len, retdest + DUP1 MLOAD_GENERAL + // stack: last_byte, last_byte_addr, addr, total_code_len, retdest + PUSH 0x80 ADD + // stack: last_byte_updated, last_byte_addr, addr, total_code_len, retdest + MSTORE_GENERAL + // stack: addr, total_code_len, retdest + + POSEIDON_GENERAL + // stack: codehash, retdest + SWAP1 + JUMP + +global poseidon_empty_code: + // stack: addr, len, retdest + %stack (addr, len, retdest) -> (retdest, @EMPTY_STRING_POSEIDON_HASH) + JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/balance.asm b/evm_arithmetization/src/cpu/kernel/asm/balance.asm index d39f66063..daf2ff855 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/balance.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/balance.asm @@ -27,12 +27,7 @@ global sys_balance: global balance: // stack: address, retdest - %mpt_read_state_trie - // stack: account_ptr, retdest - DUP1 ISZERO %jumpi(retzero) // If the account pointer is null, return 0. - %add_const(1) - // stack: balance_ptr, retdest - %mload_trie_data + %key_balance %smt_read_state %mload_trie_data // stack: balance, retdest SWAP1 JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm b/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm index 125c9d58b..05c4046f0 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm @@ -12,84 +12,37 @@ global set_beacon_root: // stack: timestamp, 8191, timestamp, retdest MOD // stack: timestamp_idx, timestamp, retdest + PUSH @BEACON_ROOTS_CONTRACT_STATE_KEY + // stack: addr, timestamp_idx, timestamp, retdest + PUSH write_beacon_roots_to_storage %parent_beacon_block_root - // stack: calldata, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - DUP3 + // stack: calldata, write_beacon_roots_to_storage, addr, timestamp_idx, timestamp, retdest + DUP4 %add_const(@HISTORY_BUFFER_LENGTH) - // stack: root_idx, calldata, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest + // stack: root_idx, calldata, write_beacon_roots_to_storage, addr, timestamp_idx, timestamp, retdest + DUP4 + // stack: addr, root_idx, calldata, write_beacon_roots_to_storage, addr, timestamp_idx, timestamp, retdest // If the calldata is zero, delete the slot from the storage trie. - DUP2 ISZERO %jumpi(delete_root_idx_slot) + DUP3 ISZERO %jumpi(delete_root_idx_slot) write_beacon_roots_to_storage: - // stack: slot, value, retdest - // First we write the value to MPT data, and get a pointer to it. - %get_trie_data_size - // stack: value_ptr, slot, value, retdest - SWAP2 - // stack: value, slot, value_ptr, retdest - %append_to_trie_data - // stack: slot, value_ptr, retdest - - // Next, call mpt_insert on the current account's storage root. - %stack (slot, value_ptr) -> (slot, value_ptr, after_beacon_roots_storage_insert) - %slot_to_storage_key - // stack: storage_key, value_ptr, after_beacon_roots_storage_insert, retdest - PUSH 64 // storage_key has 64 nibbles - %get_storage_trie(@BEACON_ROOTS_CONTRACT_STATE_KEY) - // stack: storage_root_ptr, 64, storage_key, value_ptr, after_beacon_roots_storage_insert, retdest - %jump(mpt_insert) - -after_beacon_roots_storage_insert: - // stack: new_storage_root_ptr, retdest - %get_account_data(@BEACON_ROOTS_CONTRACT_STATE_KEY) - // stack: account_ptr, new_storage_root_ptr, retdest - - // Update the copied account with our new storage root pointer. - %add_const(2) - // stack: account_storage_root_ptr_ptr, new_storage_root_ptr, retdest - %mstore_trie_data + // stack: addr, slot, value, retdest + %key_storage + // stack: storage_key, value, retdest + %smt_insert_state JUMP delete_root_idx_slot: - // stack: root_idx, 0, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - PUSH after_root_idx_slot_delete - SWAP2 POP - // stack: root_idx, after_root_idx_slot_delete, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - %slot_to_storage_key - // stack: storage_key, after_root_idx_slot_delete, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - PUSH 64 // storage_key has 64 nibbles - %get_storage_trie(@BEACON_ROOTS_CONTRACT_STATE_KEY) - // stack: storage_root_ptr, 64, storage_key, after_root_idx_slot_delete, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - - // If the slot is empty (i.e. ptr defaulting to 0), skip the deletion. - DUP1 ISZERO %jumpi(skip_empty_slot) - - // stack: storage_root_ptr, 64, storage_key, after_root_idx_slot_delete, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - %stack (storage_root_ptr, nibbles, storage_key) -> (storage_root_ptr, nibbles, storage_key, checkpoint_delete_root_idx, storage_root_ptr, nibbles, storage_key) - %jump(mpt_read) -checkpoint_delete_root_idx: - // stack: value_ptr, storage_root_ptr, 64, storage_key, after_root_idx_slot_delete, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - // If the the storage key is not found (i.e. ptr defaulting to 0), skip the deletion. - ISZERO %jumpi(skip_empty_slot) - - // stack: storage_root_ptr, 64, storage_key, after_root_idx_slot_delete, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - %jump(mpt_delete) - -after_root_idx_slot_delete: - // stack: new_storage_root_ptr, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - %get_account_data(@BEACON_ROOTS_CONTRACT_STATE_KEY) - // stack: account_ptr, new_storage_root_ptr, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - - // Update the copied account with our new storage root pointer. - %add_const(2) - // stack: account_storage_root_ptr_ptr, new_storage_root_ptr, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - %mstore_trie_data - // stack: write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - JUMP - -skip_empty_slot: - // stack: 0, 64, storage_key, after_root_idx_slot_delete, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest - %pop4 - JUMP + // stack: addr, root_idx, 0, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest + %key_storage + // stack: key, 0, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest + DUP1 %smt_read_state %mload_trie_data %jumpi(delete) + // stack: key, 0, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest + %pop2 JUMP +delete: + // stack: key, 0, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest + %smt_delete_state + // stack: 0, write_beacon_roots_to_storage, timestamp_idx, timestamp, retdest + POP JUMP \ No newline at end of file diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/create.asm b/evm_arithmetization/src/cpu/kernel/asm/core/create.asm index 80f8f4618..99644f2fa 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/create.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/create.asm @@ -83,13 +83,10 @@ global create_common: DUP1 %nonce %eq_const(@MAX_NONCE) %jumpi(nonce_overflow) // EIP-2681 %increment_nonce // stack: address, value, code_offset, code_len, kexit_info - %checkpoint - // stack: address, value, code_offset, code_len, kexit_info DUP2 DUP2 %address %transfer_eth %jumpi(panic) // We checked the balance above, so this should never happen. DUP2 DUP2 %address %journal_add_balance_transfer // Add journal entry for the balance transfer. - %create_context // stack: new_ctx, address, value, code_offset, code_len, kexit_info GET_CONTEXT @@ -171,8 +168,8 @@ after_constructor: // Store the code hash of the new contract. %returndatasize PUSH @SEGMENT_RETURNDATA GET_CONTEXT %build_address_no_offset - // stack: addr, len - KECCAK_GENERAL + // stack: addr, len, leftover_gas, success, address, kexit_info + %poseidon_hash_code_unpadded // stack: codehash, leftover_gas, success, address, kexit_info %observe_new_contract DUP4 @@ -251,17 +248,16 @@ create_too_deep: global set_codehash: // stack: addr, codehash, retdest DUP1 %insert_touched_addresses - DUP1 %mpt_read_state_trie - // stack: account_ptr, addr, codehash, retdest - %add_const(3) - // stack: codehash_ptr, addr, codehash, retdest - DUP1 %mload_trie_data - // stack: prev_codehash, codehash_ptr, addr, codehash, retdest - DUP3 %journal_add_code_change // Add the code change to the journal. - %stack (codehash_ptr, addr, codehash) -> (codehash_ptr, codehash) - %mstore_trie_data - // stack: retdest - JUMP + DUP1 %key_code %smt_read_state %mload_trie_data + // stack: prev_codehash, addr, codehash, retdest + DUP2 %key_code_length %smt_read_state %mload_trie_data + %stack (prev_code_length, prev_codehash, addr) -> (addr, prev_codehash, prev_code_length, addr) + %journal_add_code_change // Add the code change to the journal. + // stack: addr, codehash, retdest + DUP2 DUP2 %key_code %smt_insert_state + %returndatasize DUP2 %key_code_length %smt_insert_state + // stack: addr, codehash, retdest + %pop2 JUMP // Check and charge gas cost for initcode size. See EIP-3860. // Pre stack: code_size, kexit_info diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/create_contract_account.asm b/evm_arithmetization/src/cpu/kernel/asm/core/create_contract_account.asm index a614f9fa8..de019a8e2 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/create_contract_account.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/create_contract_account.asm @@ -4,53 +4,45 @@ %macro create_contract_account // stack: address DUP1 %insert_touched_addresses - DUP1 %append_created_contracts - DUP1 %mpt_read_state_trie - // stack: existing_account_ptr, address + // stack: address // If the account doesn't exist, there's no need to check its balance or nonce, // so we can skip ahead, setting existing_balance = existing_account_ptr = 0. - DUP1 ISZERO %jumpi(%%add_account) + DUP1 %key_code %smt_read_state ISZERO %jumpi(%%add_account) // Check that the nonce is 0. - // stack: existing_account_ptr, address - DUP1 %mload_trie_data // nonce = account[0] - // stack: nonce, existing_account_ptr, address + // stack: address + DUP1 %nonce + // stack: nonce, address %jumpi(%%error_collision) - // stack: existing_account_ptr, address + // stack: address // Check that the code is empty. - %add_const(3) - // stack: existing_codehash_ptr, address - DUP1 %mload_trie_data // codehash = account[3] - %eq_const(@EMPTY_STRING_HASH) ISZERO %jumpi(%%error_collision) - // stack: existing_codehash_ptr, address - %sub_const(2) %mload_trie_data // balance = account[1] + DUP1 %extcodehash + %eq_const(@EMPTY_STRING_POSEIDON_HASH) ISZERO %jumpi(%%error_collision) + DUP1 %balance %jump(%%do_insert) %%add_account: - // stack: existing_balance, address - DUP2 PUSH 1 - // stack: is_contract, address, existing_balance, address + // stack: address + DUP1 PUSH 1 + // stack: is_contract, address, address %journal_add_account_created + // stack: address + DUP1 + %append_created_contracts + // stack: address + PUSH 0 %%do_insert: // stack: new_acct_value=existing_balance, address // Write the new account's data to MPT data, and get a pointer to it. - %get_trie_data_size - // stack: account_ptr, new_acct_value, address - PUSH 0 DUP4 %journal_add_nonce_change - PUSH 1 %append_to_trie_data // nonce = 1 - // stack: account_ptr, new_acct_value, address - SWAP1 %append_to_trie_data // balance = new_acct_value - // stack: account_ptr, address - PUSH 0 %append_to_trie_data // storage_root = nil - // stack: account_ptr, address - PUSH @EMPTY_STRING_HASH %append_to_trie_data // code_hash = keccak('') - // stack: account_ptr, address - SWAP1 - // stack: address, account_ptr - %addr_to_state_key - // stack: state_key, account_ptr - %mpt_insert_state_trie - // stack: (empty) + // stack: new_acct_value, address + PUSH 0 DUP3 %journal_add_nonce_change + %stack (new_acct_value, address) -> (address, 1, new_acct_value, address) + %key_nonce %smt_insert_state // nonce = 1 + // stack: new_acct_value, address + DUP2 %key_balance %smt_insert_state // balance = new_acct_value + %stack (address) -> (address, @EMPTY_STRING_POSEIDON_HASH) + %key_code %smt_insert_state + // stack: empty PUSH 0 // success %jump(%%end) @@ -58,7 +50,7 @@ // (This should be impossible with contract creation transactions or CREATE, but possible with CREATE2.) // So we return 1 to indicate an error. %%error_collision: - %stack (existing_account_ptr, address) -> (1) + %stack (address) -> (1) %%end: // stack: status diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/nonce.asm b/evm_arithmetization/src/cpu/kernel/asm/core/nonce.asm index 48486be9e..fe955b927 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/nonce.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/nonce.asm @@ -3,12 +3,8 @@ // Post stack: (empty) global nonce: // stack: address, retdest - %mpt_read_state_trie - // stack: account_ptr, retdest - // The nonce is the first account field, so we deref the account pointer itself. - // Note: We don't need to handle account_ptr=0, as trie_data[0] = 0, - // so the deref will give 0 (the default nonce) as desired. - %mload_trie_data + %key_nonce + %smt_read_state %mload_trie_data // stack: nonce, retdest SWAP1 JUMP @@ -23,9 +19,9 @@ global nonce: global increment_nonce: // stack: address, retdest DUP1 - %mpt_read_state_trie - // stack: account_ptr, address, retdest - DUP1 ISZERO %jumpi(increment_nonce_no_such_account) + %key_nonce %smt_read_state + // stack: nonce_ptr, address, retdest + DUP1 ISZERO %jumpi(create_nonce) // stack: nonce_ptr, address, retdest DUP1 %mload_trie_data // stack: nonce, nonce_ptr, address, retdest @@ -38,8 +34,16 @@ global increment_nonce: // stack: address, retdest POP JUMP -global increment_nonce_no_such_account: - PANIC + +create_nonce: + // stack: nonce_ptr, address, retdest + POP + // stack: address, retdest + PUSH 0 DUP2 %journal_add_nonce_change + // stack: address, retdest + %key_nonce + %stack (key_nonce) -> (key_nonce, 1) + %jump(smt_insert_state) // Convenience macro to call increment_nonce and return where we left off. %macro increment_nonce diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/process_txn.asm b/evm_arithmetization/src/cpu/kernel/asm/core/process_txn.asm index c6d10eb40..242a266f8 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/process_txn.asm @@ -203,8 +203,8 @@ global process_contract_creation_txn_after_constructor: PUSH @SEGMENT_RETURNDATA GET_CONTEXT %build_address_no_offset - // stack: addr, len - KECCAK_GENERAL + // stack: addr, len, leftover_gas, new_ctx, address, retdest, success + %poseidon_hash_code_unpadded // stack: codehash, leftover_gas, new_ctx, address, retdest, success %observe_new_contract DUP4 diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm b/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm index d1a366ede..9cf0c9f86 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/terminate.asm @@ -87,14 +87,16 @@ global sys_selfdestruct: // Set the balance of the address to 0. // stack: balance, address, recipient, kexit_info - PUSH 0 - // stack: 0, balance, address, recipient, kexit_info - DUP3 %mpt_read_state_trie - // stack: account_ptr, 0, balance, address, recipient, kexit_info - %add_const(1) - // stack: balance_ptr, 0, balance, address, recipient, kexit_info - %mstore_trie_data - + DUP1 ISZERO %jumpi(selfdestruct_balance_is_zero) + DUP2 %key_balance %smt_delete_state + // stack: balance, address, recipient, kexit_info +selfdestruct_balance_is_zero: + // EIP-6780: insert address into the selfdestruct set only if contract has been created + // during the current transaction. + // stack: balance, address, recipient, kexit_info + DUP2 %contract_just_created + // stack: is_just_created, balance, address, recipient, kexit_info + %jumpi(sys_selfdestruct_just_created) // EIP-6780: insert address into the selfdestruct set only if contract has been created // during the current transaction. @@ -265,9 +267,6 @@ global terminate_common: // stack: parent_pc, success, leftover_gas JUMP - - - // Returns 1 if the address is present in SEGMENT_CREATED_CONTRACTS, meaning that it has been // created during this transaction. Returns 0 otherwise. // Pre stack: addr diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/transfer.asm b/evm_arithmetization/src/cpu/kernel/asm/core/transfer.asm index b21828f5e..247bd3dc3 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/transfer.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/transfer.asm @@ -29,28 +29,35 @@ global transfer_eth_failure: global deduct_eth: // stack: addr, amount, retdest DUP1 %insert_touched_addresses - %mpt_read_state_trie - // stack: account_ptr, amount, retdest - DUP1 ISZERO %jumpi(deduct_eth_no_such_account) // If the account pointer is null, return 1. - %add_const(1) - // stack: balance_ptr, amount, retdest + DUP2 ISZERO %jumpi(deduct_eth_noop) + DUP1 %key_balance %smt_read_state + // stack: balance_ptr, addr, amount, retdest DUP1 %mload_trie_data - // stack: balance, balance_ptr, amount, retdest - DUP1 DUP4 GT - // stack: amount > balance, balance, balance_ptr, amount, retdest + // stack: balance, balance_ptr, addr, amount, retdest + DUP1 DUP5 GT + // stack: amount > balance, balance, balance_ptr, addr, amount, retdest %jumpi(deduct_eth_insufficient_balance) - %stack (balance, balance_ptr, amount, retdest) -> (balance, amount, balance_ptr, retdest, 0) + // stack: balance, balance_ptr, addr, amount, retdest + DUP1 DUP5 EQ + // stack: amount == balance, balance, balance_ptr, addr, amount, retdest + %jumpi(deduct_eth_delete_balance) + %stack (balance, balance_ptr, addr, amount, retdest) -> (balance, amount, balance_ptr, retdest, 0) SUB SWAP1 // stack: balance_ptr, balance - amount, retdest, 0 %mstore_trie_data // stack: retdest, 0 JUMP -global deduct_eth_no_such_account: - %stack (account_ptr, amount, retdest) -> (retdest, 1) +deduct_eth_insufficient_balance: + %stack (balance, balance_ptr, addr, amount, retdest) -> (retdest, 1) + JUMP +deduct_eth_delete_balance: + %stack (balance, balance_ptr, addr, amount, retdest) -> (addr, retdest, 0) + %key_balance %smt_delete_state + // stack: retdest, 0 JUMP -global deduct_eth_insufficient_balance: - %stack (balance, balance_ptr, amount, retdest) -> (retdest, 1) +deduct_eth_noop: + %stack (addr, amount, retdest) -> (retdest, 0) JUMP // Convenience macro to call deduct_eth and return where we left off. @@ -65,46 +72,44 @@ global deduct_eth_insufficient_balance: global add_eth: // stack: addr, amount, retdest DUP1 %insert_touched_addresses + DUP2 ISZERO %jumpi(add_eth_noop) // stack: addr, amount, retdest - DUP2 ISZERO %jumpi(add_eth_zero_amount) + DUP1 %key_code %smt_read_state %mload_trie_data + // stack: codehash, addr, amount, retdest + ISZERO %jumpi(add_eth_new_account) // If the account is empty, we need to create the account. // stack: addr, amount, retdest - DUP1 %mpt_read_state_trie - // stack: account_ptr, addr, amount, retdest - DUP1 ISZERO %jumpi(add_eth_new_account) // If the account pointer is null, we need to create the account. - %add_const(1) - // stack: balance_ptr, addr, amount, retdest - DUP1 %mload_trie_data - // stack: balance, balance_ptr, addr, amount, retdest - %stack (balance, balance_ptr, addr, amount) -> (amount, balance, balance_ptr) - ADD - // stack: new_balance, balance_ptr, retdest - SWAP1 - // stack: balance_ptr, new_balance, retdest - %mstore_trie_data + %key_balance DUP1 %smt_read_state + DUP1 ISZERO %jumpi(add_eth_zero_balance) + %stack (balance_ptr, key_balance, amount) -> (balance_ptr, amount, balance_ptr) + // stack: balance_ptr, amount, balance_ptr, retdest + %mload_trie_data ADD + // stack: balance+amount, balance_ptr, retdest + SWAP1 %mstore_trie_data + JUMP +add_eth_zero_balance: + // stack: balance_ptr, key_balance, amount, retdest + POP + // stack: key_balance, amount, retdest + %smt_insert_state // stack: retdest JUMP + global add_eth_new_account: - // stack: null_account_ptr, addr, amount, retdest - POP // stack: addr, amount, retdest DUP1 PUSH 0 - // stack: is_eoa, addr, addr, amount, retdest + // stack: is_eoa, addr, amount, retdest %journal_add_account_created - %get_trie_data_size // pointer to new account we're about to create - // stack: new_account_ptr, addr, amount, retdest - SWAP2 - // stack: amount, addr, new_account_ptr, retdest - PUSH 0 %append_to_trie_data // nonce - %append_to_trie_data // balance - // stack: addr, new_account_ptr, retdest - PUSH 0 %append_to_trie_data // storage root pointer - PUSH @EMPTY_STRING_HASH %append_to_trie_data // code hash - // stack: addr, new_account_ptr, retdest - %addr_to_state_key - // stack: key, new_account_ptr, retdest - %jump(mpt_insert_state_trie) + // stack: addr, amount, retdest + DUP1 %key_code + %stack (key_code) -> (key_code, @EMPTY_STRING_POSEIDON_HASH) + %smt_insert_state + // stack: addr, amount, retdest + %key_balance + // stack: key_balance, amount, retdest + %smt_insert_state + JUMP -add_eth_zero_amount: +add_eth_noop: // stack: addr, amount, retdest %pop2 JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/util.asm b/evm_arithmetization/src/cpu/kernel/asm/core/util.asm index 6b48428ef..1241c95cf 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/util.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/util.asm @@ -34,29 +34,26 @@ // Returns 1 if the account is non-existent, 0 otherwise. %macro is_non_existent // stack: addr - %mpt_read_state_trie ISZERO + %key_code %smt_read_state ISZERO %endmacro // Returns 1 if the account is empty, 0 otherwise. %macro is_empty // stack: addr - %mpt_read_state_trie - // stack: account_ptr - DUP1 ISZERO %jumpi(%%false) - // stack: account_ptr - DUP1 %mload_trie_data - // stack: nonce, account_ptr + DUP1 %key_nonce %smt_read_state %mload_trie_data + // stack: nonce, addr ISZERO %not_bit %jumpi(%%false) - %increment DUP1 %mload_trie_data - // stack: balance, balance_ptr + // stack: addr + DUP1 %key_balance %smt_read_state %mload_trie_data + // stack: balance, addr ISZERO %not_bit %jumpi(%%false) - %add_const(2) %mload_trie_data - // stack: code_hash - PUSH @EMPTY_STRING_HASH - EQ + // stack: addr + %key_code %smt_read_state %mload_trie_data + // stack: codehash + %eq_const(@EMPTY_STRING_POSEIDON_HASH) %jump(%%after) %%false: - // stack: account_ptr + // stack: addr POP PUSH 0 %%after: diff --git a/evm_arithmetization/src/cpu/kernel/asm/global_exit_root.asm b/evm_arithmetization/src/cpu/kernel/asm/global_exit_root.asm index ffcc377a5..eab9e3940 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/global_exit_root.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/global_exit_root.asm @@ -9,75 +9,54 @@ global set_global_exit_roots: // stack: (empty) PUSH start_txn // stack: retdest + PUSH @GLOBAL_EXIT_ROOT_MANAGER_L2_STATE_KEY + // stack: addr, retdest PROVER_INPUT(ger) - // stack: num_ger, retdest + // stack: num_ger, addr, retdest PUSH 0 ger_loop: - // stack: i, num_ger, retdest + // stack: i, num_ger, addr, DUP2 DUP2 EQ %jumpi(ger_loop_end) PROVER_INPUT(ger) - // stack: timestamp, i, num_ger, retdest + // stack: timestamp, i, num_ger, addr, retdest PUSH @GLOBAL_EXIT_ROOT_STORAGE_POS PROVER_INPUT(ger) - // stack: root, GLOBAL_EXIT_ROOT_STORAGE_POS, timestamp, i, num_ger, retdest + // stack: root, GLOBAL_EXIT_ROOT_STORAGE_POS, timestamp, i, num_ger, addr, retdest PUSH @SEGMENT_KERNEL_GENERAL - // stack: addr, root, GLOBAL_EXIT_ROOT_STORAGE_POS, timestamp, i, num_ger, retdest + // stack: addr, root, GLOBAL_EXIT_ROOT_STORAGE_POS, timestamp, i, num_ger, addr, retdest MSTORE_32BYTES_32 - // stack: addr, GLOBAL_EXIT_ROOT_STORAGE_POS, timestamp, i, num_ger, retdest + // stack: addr, GLOBAL_EXIT_ROOT_STORAGE_POS, timestamp, i, num_ger, addr, retdest MSTORE_32BYTES_32 - // stack: addr, timestamp, i, num_ger, retdest + // stack: addr, timestamp, i, num_ger, addr, retdest POP - // stack: timestamp, i, num_ger, retdest + // stack: timestamp, i, num_ger, addr, retdest PUSH 64 PUSH @SEGMENT_KERNEL_GENERAL - // stack: addr, len, timestamp, i, num_ger, retdest + // stack: addr, len, timestamp, i, num_ger, addr, retdest KECCAK_GENERAL - // stack: slot, timestamp, i, num_ger, retdest + // stack: slot, timestamp, i, num_ger, addr, retdest write_timestamp_to_storage: - // stack: slot, timestamp, i, num_ger, retdest - // First we write the value to MPT data, and get a pointer to it. - %get_trie_data_size - // stack: value_ptr, slot, timestamp, i, num_ger, retdest - SWAP2 - // stack: timestamp, slot, value_ptr, i, num_ger, retdest - %append_to_trie_data - // stack: slot, value_ptr, i, num_ger, retdest - - // Next, call mpt_insert on the current account's storage root. - %stack (slot, value_ptr) -> (slot, value_ptr, after_timestamp_storage_insert) - %slot_to_storage_key - // stack: storage_key, value_ptr, after_timestamp_storage_insert - PUSH 64 // storage_key has 64 nibbles - %get_storage_trie(@GLOBAL_EXIT_ROOT_MANAGER_L2_STATE_KEY) - // stack: storage_root_ptr, 64, storage_key, value_ptr, after_timestamp_storage_insert - %stack (storage_root_ptr, num_nibbles, storage_key) -> (storage_root_ptr, num_nibbles, storage_key, after_read, storage_root_ptr, num_nibbles, storage_key) - %jump(mpt_read) -after_read: + // stack: slot, timestamp, i, num_ger, addr, retdest + DUP5 + // stack: addr, slot, timestamp, i, num_ger, addr, retdest + %key_storage + // stack: storage_key, timestamp, i, num_ger, addr, retdest // If the current value is non-zero, do nothing. - // stack: current_value_ptr, storage_root_ptr, 64, storage_key, value_ptr, after_timestamp_storage_insert - %mload_trie_data %jumpi(do_nothing) - // stack: storage_root_ptr, 64, storage_key, value_ptr, after_timestamp_storage_insert - %jump(mpt_insert) - -after_timestamp_storage_insert: - // stack: new_storage_root_ptr, i, num_ger, retdest - %get_account_data(@GLOBAL_EXIT_ROOT_MANAGER_L2_STATE_KEY) - // stack: account_ptr, new_storage_root_ptr - // Update the copied account with our new storage root pointer. - %add_const(2) - // stack: account_storage_root_ptr_ptr, new_storage_root_ptr - %mstore_trie_data + DUP1 %smt_read_state %mload_trie_data %jumpi(do_nothing) - // stack: i, num_ger, retdest + // stack: storage_key, timestamp, i, num_ger, addr, retdest + %smt_insert_state + // stack: i, num_ger, addr, retdest %increment %jump(ger_loop) ger_loop_end: - // stack: i, num_ger, retdest - %pop2 JUMP + // stack: i, num_ger, addr, retdest + %pop3 JUMP do_nothing: - // stack: storage_root_ptr, 64, storage_key, value_ptr, after_timestamp_storage_insert, i, num_ger, retdest - %pop7 - // stack: retdest - JUMP + // stack: storage_key, timestamp, i, num_ger, addr, retdest + %pop2 + // stack: i, num_ger, addr, retdest + %increment + %jump(ger_loop) diff --git a/evm_arithmetization/src/cpu/kernel/asm/journal/account_destroyed.asm b/evm_arithmetization/src/cpu/kernel/asm/journal/account_destroyed.asm index 3806a891d..5986161a5 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/journal/account_destroyed.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/journal/account_destroyed.asm @@ -16,17 +16,13 @@ revert_account_destroyed_contd: SWAP1 // Remove `prev_balance` from `target`'s balance. // stack: target, address, prev_balance, retdest - %mpt_read_state_trie - %add_const(1) - // stack: target_balance_ptr, address, prev_balance, retdest - DUP3 - DUP2 %mload_trie_data - // stack: target_balance, prev_balance, target_balance_ptr, address, prev_balance, retdest - SUB SWAP1 %mstore_trie_data + %key_balance DUP1 %smt_read_state %mload_trie_data + // stack: target_balance, target_balance_key, address, prev_balance, retdest + %stack (target_balance, target_balance_key, address, prev_balance) -> (target_balance, prev_balance, target_balance_key, address, prev_balance) + // stack: target_balance, prev_balance, target_balance_key, address, prev_balance, retdest + SUB SWAP1 %smt_insert_state // Set `address`'s balance to `prev_balance`. // stack: address, prev_balance, retdest - %mpt_read_state_trie - %add_const(1) - %mstore_trie_data + %key_balance %smt_insert_state + // stack: retdest JUMP - diff --git a/evm_arithmetization/src/cpu/kernel/asm/journal/code_change.asm b/evm_arithmetization/src/cpu/kernel/asm/journal/code_change.asm index 5bb637c72..61e5d5718 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/journal/code_change.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/journal/code_change.asm @@ -1,18 +1,15 @@ -// struct CodeChange { address, prev_codehash } +// struct CodeChange { address, prev_codehash, prev_code_length } %macro journal_add_code_change - %journal_add_2(@JOURNAL_ENTRY_CODE_CHANGE) + %journal_add_3(@JOURNAL_ENTRY_CODE_CHANGE) %endmacro global revert_code_change: // stack: entry_ptr, ptr, retdest POP - %journal_load_2 - // stack: address, prev_codehash, retdest - %mpt_read_state_trie - // stack: account_ptr, prev_codehash, retdest - %add_const(3) - // stack: codehash_ptr, prev_codehash, retdest - %mstore_trie_data + %journal_load_3 + %stack (address, prev_codehash, prev_code_length) -> (address, prev_codehash, address, prev_code_length) + %key_code %smt_insert_state + %key_code_length %smt_insert_state // stack: retdest JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/journal/nonce_change.asm b/evm_arithmetization/src/cpu/kernel/asm/journal/nonce_change.asm index 3ab8f1367..99d6c6554 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/journal/nonce_change.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/journal/nonce_change.asm @@ -9,9 +9,6 @@ global revert_nonce_change: POP %journal_load_2 // stack: address, prev_nonce, retdest - %mpt_read_state_trie - // stack: nonce_ptr, prev_nonce retdest - %mstore_trie_data + %key_nonce %smt_insert_state // stack: retdest JUMP - diff --git a/evm_arithmetization/src/cpu/kernel/asm/journal/storage_change.asm b/evm_arithmetization/src/cpu/kernel/asm/journal/storage_change.asm index 752674d1e..5ff87cd1d 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/journal/storage_change.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/journal/storage_change.asm @@ -11,47 +11,12 @@ global revert_storage_change: // stack: address, slot, prev_value, retdest DUP3 ISZERO %jumpi(delete) // stack: address, slot, prev_value, retdest - SWAP1 %slot_to_storage_key - // stack: storage_key, address, prev_value, retdest - PUSH 64 // storage_key has 64 nibbles - // stack: 64, storage_key, address, prev_value, retdest - DUP3 %mpt_read_state_trie - DUP1 ISZERO %jumpi(panic) - // stack: account_ptr, 64, storage_key, address, prev_value, retdest - %add_const(2) - // stack: storage_root_ptr_ptr, 64, storage_key, address, prev_value, retdest - %mload_trie_data - %get_trie_data_size - DUP6 %append_to_trie_data - %stack (prev_value_ptr, storage_root_ptr, num_nibbles, storage_key, address, prev_value, retdest) -> - (storage_root_ptr, num_nibbles, storage_key, prev_value_ptr, new_storage_root, address, retdest) - %jump(mpt_insert) + %key_storage %smt_insert_state + // stack: retdest + JUMP delete: // stack: address, slot, prev_value, retdest - SWAP2 POP - %stack (slot, address, retdest) -> (slot, new_storage_root, address, retdest) - %slot_to_storage_key - // stack: storage_key, new_storage_root, address, retdest - PUSH 64 // storage_key has 64 nibbles - // stack: 64, storage_key, new_storage_root, address, retdest - DUP4 %mpt_read_state_trie - DUP1 ISZERO %jumpi(panic) - // stack: account_ptr, 64, storage_key, new_storage_root, address, retdest - %add_const(2) - // stack: storage_root_ptr_ptr, 64, storage_key, new_storage_root, address, retdest - %mload_trie_data - // stack: storage_root_ptr, 64, storage_key, new_storage_root, address, retdest - %jump(mpt_delete) - -new_storage_root: - // stack: new_storage_root_ptr, address, retdest - DUP2 %mpt_read_state_trie - // stack: account_ptr, new_storage_root_ptr, address, retdest - - // Update account with our new storage root pointer. - %add_const(2) - // stack: account_storage_root_ptr_ptr, new_storage_root_ptr, address, retdest - %mstore_trie_data - // stack: address, retdest + %key_storage %smt_delete_state + // stack: prev_value, retdest POP JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/main.asm b/evm_arithmetization/src/cpu/kernel/asm/main.asm index 5d6d96799..668a5f196 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/main.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/main.asm @@ -20,7 +20,11 @@ global main: // Initialize the RLP DATA pointer to its initial position, // skipping over the preinitialized empty node. + // Because hashing with the SMT doesn't require RLP encoding, + // we shift the initial pointer by MAX_RLP_BLOB_SIZE to not + // overwrite any transaction field. PUSH @INITIAL_TXN_RLP_ADDR + %add_const(@MAX_RLP_BLOB_SIZE) %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) // Encode constant nodes @@ -37,10 +41,10 @@ global main: global hash_initial_tries: // We compute the length of the trie data segment in `mpt_hash` so that we // can check the value provided by the prover. - // We initialize the segment length with 1 because the segment contains + // We initialize the segment length with 2 because the segment contains // the null pointer `0` when the tries are empty. - PUSH 1 - %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE) %assert_eq + PUSH 2 + %smt_hash_state %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE) %assert_eq // stack: trie_data_len %mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE) %assert_eq // stack: trie_data_len @@ -99,7 +103,7 @@ global perform_final_checks: %pop3 PUSH 1 // initial trie data length global check_state_trie: - %mpt_hash_state_trie %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER) %assert_eq + %smt_hash_state %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER) %assert_eq global check_txn_trie: %mpt_hash_txn_trie %mload_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER) %assert_eq global check_receipt_trie: diff --git a/evm_arithmetization/src/cpu/kernel/asm/mpt/accounts.asm b/evm_arithmetization/src/cpu/kernel/asm/mpt/accounts.asm index 1f60a3f75..0ee987b4c 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/mpt/accounts.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/mpt/accounts.asm @@ -19,25 +19,3 @@ %mload_trie_data // stack: storage_root_ptr %endmacro - -// Return a pointer to the provided account's data in the state trie. -%macro get_account_data(addr) - PUSH $addr %mpt_read_state_trie - // stack: account_ptr - // account_ptr should be non-null as long as the prover provided the proper - // Merkle data. But a bad prover may not have, and we don't want return a - // null pointer for security reasons. - DUP1 ISZERO %jumpi(panic) - // stack: account_ptr -%endmacro - -// Returns a pointer to the root of the storage trie associated with the provided account. -%macro get_storage_trie(key) - // stack: (empty) - %get_account_data($key) - // stack: account_ptr - %add_const(2) - // stack: storage_root_ptr_ptr - %mload_trie_data - // stack: storage_root_ptr -%endmacro \ No newline at end of file diff --git a/evm_arithmetization/src/cpu/kernel/asm/mpt/delete/delete.asm b/evm_arithmetization/src/cpu/kernel/asm/mpt/delete/delete.asm index 913ba1fcf..5df1e283b 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/mpt/delete/delete.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/mpt/delete/delete.asm @@ -22,24 +22,3 @@ mpt_delete_leaf: %pop4 PUSH 0 // empty node ptr SWAP1 JUMP - -global delete_account: - %stack (address, retdest) -> (address, delete_account_save, retdest) - %addr_to_state_key - // stack: key, delete_account_save, retdest - PUSH 64 - // stack: 64, key, delete_account_save, retdest - %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - // stack: state_root_prt, 64, key, delete_account_save, retdest - %jump(mpt_delete) -delete_account_save: - // stack: updated_state_root_ptr, retdest - %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) - JUMP - -%macro delete_account - %stack (address) -> (address, %%after) - %jump(delete_account) -%%after: - // stack: (empty) -%endmacro \ No newline at end of file diff --git a/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_read.asm b/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_read.asm index db9fe4222..9025842e9 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_read.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_read.asm @@ -5,35 +5,18 @@ %endmacro global sload_current: - %stack (slot) -> (slot, after_storage_read) - %slot_to_storage_key - // stack: storage_key, after_storage_read - PUSH 64 // storage_key has 64 nibbles - %current_storage_trie - // stack: storage_root_ptr, 64, storage_key, after_storage_read - %jump(mpt_read) - -global after_storage_read: - // stack: value_ptr, retdest - DUP1 %jumpi(storage_key_exists) - - // Storage key not found. Return default value_ptr = 0, - // which derefs to 0 since @SEGMENT_TRIE_DATA[0] = 0. - %stack (value_ptr, retdest) -> (retdest, 0) - JUMP - -global storage_key_exists: - // stack: value_ptr, retdest + // stack: slot, retdest + %address + // stack: addr, slot, retdest + %key_storage %smt_read_state %mload_trie_data // stack: value, retdest - SWAP1 - JUMP + SWAP1 JUMP // Read a word from the current account's storage trie. // // Pre stack: kexit_info, slot // Post stack: value - global sys_sload: // stack: kexit_info, slot SWAP1 diff --git a/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_write.asm b/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_write.asm index 22c5d29de..bb2796675 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_write.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/mpt/storage/storage_write.asm @@ -103,6 +103,13 @@ sstore_after_refund: %stack (kexit_info, current_value, slot, value) -> (value, current_value, current_value, slot, value, kexit_info) EQ %jumpi(sstore_noop) + // stack: current_value, slot, value, kexit_info + DUP1 ISZERO + // stack: current_value==0, current_value, slot, value, kexit_info + DUP4 MUL + // stack: value & current_value==0, current_value, slot, value, kexit_info + %jumpi(new_storage_slot) +global not_new_storage_slot: // stack: current_value, slot, value, kexit_info DUP2 %address %journal_add_storage_change // stack: slot, value, kexit_info @@ -111,32 +118,12 @@ sstore_after_refund: // stack: slot, value, kexit_info DUP2 ISZERO %jumpi(sstore_delete) - // First we write the value to MPT data, and get a pointer to it. - %get_trie_data_size - // stack: value_ptr, slot, value, kexit_info - SWAP2 - // stack: value, slot, value_ptr, kexit_info - %append_to_trie_data - // stack: slot, value_ptr, kexit_info - - // Next, call mpt_insert on the current account's storage root. - %stack (slot, value_ptr) -> (slot, value_ptr, after_storage_insert) - %slot_to_storage_key - // stack: storage_key, value_ptr, after_storage_insert, kexit_info - PUSH 64 // storage_key has 64 nibbles - %current_storage_trie - // stack: storage_root_ptr, 64, storage_key, value_ptr, after_storage_insert, kexit_info - %jump(mpt_insert) - -after_storage_insert: - // stack: new_storage_root_ptr, kexit_info - %current_account_data - // stack: account_ptr, new_storage_root_ptr, kexit_info - - // Update the copied account with our new storage root pointer. - %add_const(2) - // stack: account_storage_root_ptr_ptr, new_storage_root_ptr, kexit_info - %mstore_trie_data + // stack: slot, value, kexit_info + %address + // stack: addr, slot, value, kexit_info + %key_storage + // stack: storage_key, value, kexit_info + %smt_insert_state // stack: kexit_info EXIT_KERNEL @@ -148,12 +135,41 @@ sstore_noop: // Delete the slot from the storage trie. sstore_delete: // stack: slot, value, kexit_info - SWAP1 POP - PUSH after_storage_insert SWAP1 - // stack: slot, after_storage_insert, kexit_info - %slot_to_storage_key - // stack: storage_key, after_storage_insert, kexit_info - PUSH 64 // storage_key has 64 nibbles - %current_storage_trie - // stack: storage_root_ptr, 64, storage_key, after_storage_insert, kexit_info - %jump(mpt_delete) + %address + // stack: address, slot, value, kexit_info + %key_storage + // stack: key_storage, value, kexit_info + %smt_delete_state + // stack: value, kexit_info + POP EXIT_KERNEL + +%macro insert_new_storage_slot + // stack: address, slot + %mload_global_metadata(@GLOBAL_METADATA_NEW_STORAGE_SLOTS_LEN) + // stack: list_len, address, slot + DUP1 %add_const(@SEGMENT_NEW_STORAGE_SLOTS) + // stack: index, list_len, address, slot + DUP1 %add_const(1) + %stack (index_plus_1, index, list_len, address, slot) -> (address, index, slot, index_plus_1, list_len) + MSTORE_GENERAL MSTORE_GENERAL + // stack: list_len + %add_const(2) + // stack: list_len+2 + %mstore_global_metadata(@GLOBAL_METADATA_NEW_STORAGE_SLOTS_LEN) + // stack: (empty) +%endmacro + +new_storage_slot: + // stack: current_value, slot, value, kexit_info + %address DUP1 %contract_just_created + // stack: contract_just_created, address, current_value, slot, value, kexit_info + %jumpi(new_storage_slot_new_contract) + // stack: address, current_value, slot, value, kexit_info + POP %jump(not_new_storage_slot) +new_storage_slot_new_contract: + // stack: address, current_value, slot, value, kexit_info + DUP3 SWAP1 + // stack: address, slot, current_value, slot, value, kexit_info + %insert_new_storage_slot + // stack: current_value, slot, value, kexit_info + %jump(not_new_storage_slot) diff --git a/evm_arithmetization/src/cpu/kernel/asm/smt/delete.asm b/evm_arithmetization/src/cpu/kernel/asm/smt/delete.asm new file mode 100644 index 000000000..701bd4193 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/smt/delete.asm @@ -0,0 +1,293 @@ +%macro smt_delete_state + %stack (key) -> (key, %%after) + %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) // node_ptr + // stack: node_ptr, key, retdest + %jump(smt_delete) +%%after: + // stack: new_node_ptr + %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) + // stack: (emtpy) +%endmacro + +// Return a copy of the given node with the given key deleted. +// Assumes that the key is in the SMT. +// +// Pre stack: node_ptr, key, retdest +// Post stack: updated_node_ptr +global smt_delete: + // stack: node_ptr, key, retdest + SWAP1 %split_key + %stack (k0, k1, k2, k3, node_ptr) -> (node_ptr, 0, k0, k1, k2, k3) +smt_delete_with_keys: + // stack: node_ptr, level, ks, retdest + DUP1 %mload_trie_data + // stack: node_type, node_ptr, level, ks, retdest + // Increment node_ptr, so it points to the node payload instead of its type. + SWAP1 %increment SWAP1 + // stack: node_type, node_payload_ptr, level, ks, retdest + + DUP1 %eq_const(@SMT_NODE_INTERNAL) %jumpi(smt_delete_internal) + %eq_const(@SMT_NODE_LEAF) %jumpi(smt_delete_leaf) + PANIC // Should never happen. + +smt_delete_leaf: + // stack: node_payload_ptr, level, ks, retdest + %pop6 + PUSH 0 // empty node ptr + SWAP1 JUMP + +smt_delete_internal: + // stack: node_type, node_payload_ptr, level, ks, retdest + POP + // stack: node_payload_ptr, level, ks, retdest + DUP2 %and_const(3) // level mod 4 + // stack: level%4, node_payload_ptr, level, ks, retdest + DUP1 %eq_const(0) %jumpi(smt_delete_internal_0) + DUP1 %eq_const(1) %jumpi(smt_delete_internal_1) + DUP1 %eq_const(2) %jumpi(smt_delete_internal_2) + %eq_const(3) %jumpi(smt_delete_internal_3) + PANIC +smt_delete_internal_0: + // stack: level%4, node_payload_ptr, level, ks, retdest + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k0, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk0, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, newk0, k1, k2, k3 ) + %jump(smt_delete_internal_contd) +smt_delete_internal_1: + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k1, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk1, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, newk1, k2, k3 ) + %jump(smt_delete_internal_contd) +smt_delete_internal_2: + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k2, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk2, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, k1, newk2, k3 ) + %jump(smt_delete_internal_contd) +smt_delete_internal_3: + %stack (node_payload_ptr, level, k0, k1, k2, k3 ) -> (k3, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk3, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, k1, k2, newk3 ) +smt_delete_internal_contd: + //stack: bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + PUSH internal_update + //stack: internal_update, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %rep 7 + DUP8 + %endrep + //stack: bit, node_payload_ptr, level, k0, k1, k2, k3, internal_update, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + ADD + //stack: child_ptr_ptr, level, k0, k1, k2, k3, internal_update, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %mload_trie_data + //stack: child_ptr, level, k0, k1, k2, k3, internal_update, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + SWAP1 %increment SWAP1 + //stack: child_ptr, level+1, k0, k1, k2, k3, internal_update, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %jump(smt_delete_with_keys) + +// Update the internal node, possibly deleting it, or returning a leaf node. +internal_update: + // Update the child first. + //stack: deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + DUP2 PUSH 1 SUB + //stack: 1-bit, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + DUP4 ADD + //stack: sibling_ptr_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %mload_trie_data DUP1 %mload_trie_data + //stack: sibling_node_type, sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + DUP1 %eq_const(@SMT_NODE_HASH) %jumpi(sibling_is_hash) + DUP1 %eq_const(@SMT_NODE_LEAF) %jumpi(sibling_is_leaf) + %eq_const(@SMT_NODE_INTERNAL) %jumpi(sibling_is_internal) + PANIC // Should never happen. +sibling_is_internal: + //stack: sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + POP +insert_child: + //stack: deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %stack (deleted_child_ptr, bit, node_payload_ptr) -> (node_payload_ptr, bit, deleted_child_ptr, node_payload_ptr) + ADD %mstore_trie_data + // stack: node_payload_ptr, level, ks, retdest + %decrement + %stack (node_ptr, level, k0, k1, k2, k3, retdest) -> (retdest, node_ptr) + JUMP + +sibling_is_hash: + // stack: sibling_node_type, sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + POP + //stack: sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %increment %mload_trie_data + // stack: hash, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %jumpi(insert_child) // Sibling is non-empty hash node. +sibling_is_empty: + // stack: deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + DUP1 %mload_trie_data + // stack: deleted_child_node_type, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + DUP1 %eq_const(@SMT_NODE_HASH) %jumpi(sibling_is_empty_child_is_hash) + DUP1 %eq_const(@SMT_NODE_LEAF) %jumpi(sibling_is_empty_child_is_leaf) +sibling_is_empty_child_is_internal: + // stack: deleted_child_node_type, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + POP + // stack: deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %jump(insert_child) + +sibling_is_empty_child_is_hash: + // stack: deleted_child_node_type, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + POP + // stack: deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + DUP1 %increment %mload_trie_data + // stack: hash, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %jumpi(insert_child) +sibling_is_empty_child_is_empty: + // We can just delete this node. + // stack: deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %pop8 + SWAP1 PUSH 0 + // stack: retdest, 0 + JUMP + +sibling_is_empty_child_is_leaf: + // stack: deleted_child_node_type, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + POP + // stack: deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %increment + // stack: deleted_child_key_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP4 + // stack: level, deleted_child_key_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP3 + // stack: bit, level, deleted_child_key_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP3 %mload_trie_data + // stack: child_key, bit, level, deleted_child_key_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %recombine_key + // stack: new_child_key, deleted_child_key_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP2 %mstore_trie_data + // stack: deleted_child_key_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %decrement + // stack: deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + SWAP7 + // stack: k3, bit, node_payload_ptr, level, k0, k1, k2, deleted_child_ptr, retdest + %pop7 + // stack: deleted_child_ptr, retdest + SWAP1 JUMP + +sibling_is_leaf: + // stack: sibling_node_type, sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + POP + // stack: sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + DUP2 %is_non_empty_node + // stack: child_is_non_empty, sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %jumpi(sibling_is_leaf_child_is_non_empty) +sibling_is_leaf_child_is_empty: + // stack: sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + %increment + // stack: sibling_key_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP5 + // stack: level, sibling_key_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP4 + // stack: bit, level, sibling_key_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + PUSH 1 SUB + // stack: obit, level, sibling_key_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP3 %mload_trie_data + // stack: sibling_key, obit, level, sibling_key_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %recombine_key + // stack: new_key, sibling_key_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + DUP2 %mstore_trie_data + // stack: sibling_key_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + %decrement + // stack: sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + SWAP8 + // stack: k3, deleted_child_ptr, bit, node_payload_ptr, level, k0, k1, k2, sibling_ptr, retdest + %pop8 + // stack: sibling_ptr, retdest + SWAP1 JUMP + +sibling_is_leaf_child_is_non_empty: + // stack: sibling_ptr, deleted_child_ptr, bit, node_payload_ptr, level, ks, retdest + POP + // stack: deleted_child_ptr, node_payload_ptr, bit, retdest + %jump(insert_child) + + +global delete_account: + %stack (address, retdest) -> (address, retdest) + DUP1 %key_nonce + // stack: key_nonce, address, retdest + DUP1 %smt_read_state ISZERO %jumpi(zero_nonce) + // stack: key_nonce, address, retdest + DUP1 %smt_delete_state + // stack: key_nonce, address, retdest +zero_nonce: + // stack: key_nonce, address, retdest + POP + // stack: address, retdest + DUP1 %key_balance + // stack: key_balance, address, retdest + DUP1 %smt_read_state ISZERO %jumpi(zero_balance) + // stack: key_balance, address, retdest + DUP1 %smt_delete_state + // stack: key_balance, address, retdest +zero_balance: + // stack: key_balance, address, retdest + POP + // stack: address, retdest + DUP1 %key_code + // stack: key_code, address, retdest + DUP1 %smt_read_state ISZERO %jumpi(zero_code) + // stack: key_code, address, retdest + DUP1 %smt_delete_state + // stack: key_code, address, retdest +zero_code: + // stack: key_code, address, retdest + POP + // stack: address, retdest + DUP1 %key_code_length + // stack: key_code_length, address, retdest + DUP1 %smt_read_state ISZERO %jumpi(zero_code_length) + // stack: key_code_length, address, retdest + DUP1 %smt_delete_state +zero_code_length: + // N.B.: We don't delete the storage, since there's no way of knowing keys used. + // stack: key_code_length, address, retdest + POP + // stack: address, retdest + %mload_global_metadata(@GLOBAL_METADATA_NEW_STORAGE_SLOTS_LEN) + // stack: slots_len, address, retdest + PUSH 0 + // stack: i, slots_len, address, retdest +delete_storage_slots_loop: + // stack: i, slots_len, address, retdest + DUP2 DUP2 EQ %jumpi(delete_storage_slots_loop_end) + // stack: i, slots_len, address, retdest + DUP1 %add_const(@SEGMENT_NEW_STORAGE_SLOTS) + // stack: addr_index, i, slots_len, address, retdest + MLOAD_GENERAL + // stack: slot_addr, i, slots_len, address, retdest + DUP4 EQ + // stack: address==slot_addr, i, slots_len, address, retdest + %jumpi(delete_storage_slot) + // stack: i, slots_len, address, retdest + %add_const(2) %jump(delete_storage_slots_loop) +delete_storage_slot: + // stack: i, slots_len, address, retdest + DUP1 %increment %add_const(@SEGMENT_NEW_STORAGE_SLOTS) + // stack: slot_index, i, slots_len, address, retdest + MLOAD_GENERAL + // stack: slot, i, slots_len, address, retdest + DUP4 %key_storage + // stack: key_storage, i, slots_len, address, retdest + DUP1 %smt_read_state ISZERO %jumpi(zero_slot) + // stack: key_storage, i, slots_len, address, retdest + DUP1 %smt_delete_state +zero_slot: + // stack: key_storage, i, slots_len, address, retdest + POP + // stack: i, slots_len, address, retdest + %add_const(2) %jump(delete_storage_slots_loop) + +delete_storage_slots_loop_end: + // stack: i, slots_len, address, retdest + %pop3 JUMP + +%macro delete_account + %stack (address) -> (address, %%after) + %jump(delete_account) +%%after: + // stack: (empty) +%endmacro diff --git a/evm_arithmetization/src/cpu/kernel/asm/smt/hash.asm b/evm_arithmetization/src/cpu/kernel/asm/smt/hash.asm new file mode 100644 index 000000000..08dbfd1a1 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/smt/hash.asm @@ -0,0 +1,85 @@ +%macro smt_hash_state + %stack (cur_len) -> (cur_len, %%after) + %jump(smt_hash_state) +%%after: +%endmacro + +// Root hash of the state SMT. +global smt_hash_state: + // stack: cur_len, retdest + %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) + +// Root hash of SMT stored at `trie_data[ptr]`. +// Pseudocode: +// ``` +// hash( HashNode { h } ) = h +// hash( InternalNode { left, right } ) = Poseidon(hash(left) || hash(right) || [0,0,0,0]) +// hash( Leaf { rem_key, val_hash } ) = Poseidon(rem_key || val_hash || [1,0,0,0]) +// ``` +global smt_hash: + // stack: ptr, cur_len, retdest + DUP1 + %mload_trie_data + // stack: node, node_ptr, cur_len, retdest + DUP1 %eq_const(@SMT_NODE_HASH) %jumpi(smt_hash_hash) + DUP1 %eq_const(@SMT_NODE_INTERNAL) %jumpi(smt_hash_internal) + %eq_const(@SMT_NODE_LEAF) %jumpi(smt_hash_leaf) +smt_hash_unknown_node_type: + PANIC + +smt_hash_hash: + // stack: node, node_ptr, cur_len, retdest + POP + // stack: node_ptr, cur_len, retdest + SWAP1 %add_const(2) SWAP1 + // stack: node_ptr, cur_len, retdest + %increment + // stack: node_ptr+1, cur_len, retdest + %mload_trie_data + %stack (hash, cur_len, retdest) -> (retdest, hash, cur_len) + JUMP + +smt_hash_internal: + // stack: node, node_ptr, cur_len, retdest + POP + // stack: node_ptr, cur_len, retdest + SWAP1 %add_const(3) SWAP1 + %increment + // stack: node_ptr+1, cur_len, retdest + DUP1 + %mload_trie_data + %stack (left_child_ptr, node_ptr_plus_1, cur_len, retdest) -> (left_child_ptr, cur_len, smt_hash_internal_after_left, node_ptr_plus_1, retdest) + %jump(smt_hash) +smt_hash_internal_after_left: + %stack (left_hash, cur_len, node_ptr_plus_1, retdest) -> (node_ptr_plus_1, left_hash, cur_len, retdest) + %increment + // stack: node_ptr+2, left_hash, cur_len, retdest + %mload_trie_data + %stack (right_child_ptr, left_hash, cur_len, retdest) -> (right_child_ptr, cur_len, smt_hash_internal_after_right, left_hash, retdest) + %jump(smt_hash) +smt_hash_internal_after_right: + %stack (right_hash, cur_len, left_hash) -> (left_hash, right_hash, 0, cur_len) + POSEIDON + %stack (hash, cur_len, retdest) -> (retdest, hash, cur_len) + JUMP + +smt_hash_leaf: + // stack: node_ptr, cur_len, retdest + SWAP1 %add_const(3) SWAP1 + // stack: node_ptr, cur_len, retdest + %increment + // stack: node_ptr+1, cur_len, retdest + DUP1 %increment + // stack: node_ptr+2, node_ptr+1, cur_len, retdest + %mload_trie_data + // stack: value, node_ptr+1, cur_len, retdest + SWAP1 + // stack: node_ptr+1, value, cur_len, retdest + %mload_trie_data + %stack (rem_key, value) -> (value, smt_hash_leaf_contd, rem_key) + %jump(hash_limbs) +smt_hash_leaf_contd: + %stack (value_hash, rem_key) -> (rem_key, value_hash, 1) + POSEIDON + %stack (hash, cur_len, retdest) -> (retdest, hash, cur_len) + JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/smt/insert.asm b/evm_arithmetization/src/cpu/kernel/asm/smt/insert.asm new file mode 100644 index 000000000..20589b083 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/smt/insert.asm @@ -0,0 +1,175 @@ +// Insert a key-value pair in the state SMT. +global smt_insert_state: + // stack: key, value, retdest + DUP2 ISZERO %jumpi(insert_zero) + // stack: key, value, retdest + %stack (key, value) -> (key, value, smt_insert_state_after) + %split_key + // stack: k0, k1, k2, k3, value, smt_insert_state_after, retdest + PUSH 0 + // stack: level, k0, k1, k2, k3, value, smt_insert_state_after, retdest + %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) // node_ptr + // stack: node_ptr, level, k0, k1, k2, k3, value, smt_insert_state_after, retdest + %jump(smt_insert) + +smt_insert_state_after: + // stack: root_ptr, retdest + %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) + // stack: retdest + JUMP + +%macro smt_insert_state + %stack (key, value_ptr) -> (key, value_ptr, %%after) + %jump(smt_insert_state) +%%after: +%endmacro + +// Insert a key-value pair in the SMT at `trie_data[node_ptr]`. +// Pseudocode: +// ``` +// insert( HashNode { h }, key, value ) = if h == 0 then Leaf { key, value } else PANIC +// insert( InternalNode { left, right }, key, value ) = if key&1 { insert( right, key>>1, value ) } else { insert( left, key>>1, value ) } +// insert( Leaf { key', value' }, key, value ) = { +// let internal = new InternalNode; +// insert(internal, key', value'); +// insert(internal, key, value); +// return internal;} +// ``` +global smt_insert: + // stack: node_ptr, level, ks, value, retdest + DUP1 %mload_trie_data + // stack: node_type, node_ptr, level, ks, value, retdest + // Increment node_ptr, so it points to the node payload instead of its type. + SWAP1 %increment SWAP1 + // stack: node_type, node_payload_ptr, level, ks, value, retdest + + DUP1 %eq_const(@SMT_NODE_HASH) %jumpi(smt_insert_hash) + DUP1 %eq_const(@SMT_NODE_INTERNAL) %jumpi(smt_insert_internal) + DUP1 %eq_const(@SMT_NODE_LEAF) %jumpi(smt_insert_leaf) + PANIC + +smt_insert_hash: + // stack: node_type, node_payload_ptr, level, ks, value, retdest + POP + // stack: node_payload_ptr, level, ks, value, retdest + %mload_trie_data + // stack: hash, level, ks, value, retdest + ISZERO %jumpi(smt_insert_empty) + PANIC // Trying to insert in a non-empty hash node. +smt_insert_empty: + // stack: level, ks, value, retdest + POP + // stack: ks, value, retdest + %combine_key + // stack: key, value, retdest + %get_trie_data_size + // stack: index, key, value, retdest + PUSH @SMT_NODE_LEAF %append_to_trie_data + %stack (index, key, value) -> (key, value, index) + %append_to_trie_data // key + %append_to_trie_data // value + // stack: index, retdest + SWAP1 JUMP + +smt_insert_internal: + // stack: node_type, node_payload_ptr, level, ks, value, retdest + POP + // stack: node_payload_ptr, level, ks, value, retdest + DUP2 %and_const(3) // level mod 4 + // stack: level%4, node_payload_ptr, level, ks, value, retdest + DUP1 %eq_const(0) %jumpi(smt_insert_internal_0) + DUP1 %eq_const(1) %jumpi(smt_insert_internal_1) + DUP1 %eq_const(2) %jumpi(smt_insert_internal_2) + %eq_const(3) %jumpi(smt_insert_internal_3) + PANIC +smt_insert_internal_0: + // stack: level%4, node_payload_ptr, level, ks, value, retdest + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k0, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk0, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, newk0, k1, k2, k3 ) + %jump(smt_insert_internal_contd) +smt_insert_internal_1: + // stack: level%4, node_payload_ptr, level, ks, value, retdest + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k1, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk1, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, newk1, k2, k3 ) + %jump(smt_insert_internal_contd) +smt_insert_internal_2: + // stack: level%4, node_payload_ptr, level, ks, value, retdest + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k2, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk2, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, k1, newk2, k3 ) + %jump(smt_insert_internal_contd) +smt_insert_internal_3: + // stack: level%4, node_payload_ptr, level, ks, value, retdest + %stack (node_payload_ptr, level, k0, k1, k2, k3 ) -> (k3, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk3, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, k1, k2, newk3 ) + %jump(smt_insert_internal_contd) +smt_insert_internal_contd: + // stack: bit, node_payload_ptr, level, ks, value, retdest + DUP2 ADD + // stack: child_ptr_ptr, node_payload_ptr, level, ks, value, retdest + DUP1 %mload_trie_data + // stack: child_ptr, child_ptr_ptr, node_payload_ptr, level, ks, value, retdest + SWAP3 %increment SWAP3 + %stack (child_ptr, child_ptr_ptr, node_payload_ptr, level_plus_1, k0, k1, k2, k3, value, retdest) -> + (child_ptr, level_plus_1, k0, k1, k2, k3, value, smt_insert_internal_after, child_ptr_ptr, node_payload_ptr, retdest) + %jump(smt_insert) + +smt_insert_internal_after: + // stack: new_node_ptr, child_ptr_ptr, node_payload_ptr, retdest + SWAP1 %mstore_trie_data + // stack: node_payload_ptr, retdest + %decrement + SWAP1 JUMP + +smt_insert_leaf: + // stack: node_type, node_payload_ptr, level, ks, value, retdest + POP + %stack (node_payload_ptr, level, k0, k1, k2, k3, value) -> (k0, k1, k2, k3, node_payload_ptr, level, k0, k1, k2, k3, value) + %combine_key + // stack: rem_key, node_payload_ptr, level, ks, value, retdest + DUP2 %mload_trie_data + // stack: existing_key, rem_key, node_payload_ptr, level, ks, value, retdest + DUP2 DUP2 EQ %jumpi(smt_insert_leaf_same_key) + // stack: existing_key, rem_key, node_payload_ptr, level, ks, value, retdest + // We create an internal node with two empty children, and then we insert the two leaves. + %get_trie_data_size + // stack: index, existing_key, rem_key, node_payload_ptr, level, ks, value, retdest + PUSH @SMT_NODE_INTERNAL %append_to_trie_data + PUSH 0 %append_to_trie_data // Empty hash node + PUSH 0 %append_to_trie_data // Empty hash node + // stack: index, existing_key, rem_key, node_payload_ptr, level, ks, value, retdest + SWAP1 %split_key + // stack: existing_k0, existing_k1, existing_k2, existing_k3, index, rem_key, node_payload_ptr, level, ks, value, retdest + DUP7 %increment %mload_trie_data + // stack: existing_value, existing_k0, existing_k1, existing_k2, existing_k3, index, rem_key, node_payload_ptr, level, ks, value, retdest + DUP9 + %stack (level, existing_value, existing_k0, existing_k1, existing_k2, existing_k3, index) -> (index, level, existing_k0, existing_k1, existing_k2, existing_k3, existing_value, after_first_leaf) + %jump(smt_insert) +after_first_leaf: + // stack: internal_ptr, rem_key, node_payload_ptr, level, ks, value, retdest + %stack (internal_ptr, rem_key, node_payload_ptr, level, k0, k1, k2, k3, value) -> (internal_ptr, level, k0, k1, k2, k3, value) + %jump(smt_insert) + +smt_insert_leaf_same_key: + // stack: existing_key, rem_key, node_payload_ptr, level, ks, value, retdest + %pop2 + %stack (node_payload_ptr, level, k0, k1, k2, k3, value) -> (node_payload_ptr, value, node_payload_ptr) + %increment %mstore_trie_data + // stack: node_payload_ptr, retdest + %decrement + // stack: node_ptr, retdest + SWAP1 JUMP + +insert_zero: + // stack: key, value, retdest + DUP1 %smt_read_state %mload_trie_data %jumpi(delete) + // stack: key, value, retdest + %pop2 JUMP +delete: + // stack: key, value, retdest + %smt_delete_state + // stack: value, retdest + POP JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/smt/keys.asm b/evm_arithmetization/src/cpu/kernel/asm/smt/keys.asm new file mode 100644 index 000000000..0d8b09013 --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/smt/keys.asm @@ -0,0 +1,131 @@ +/// See `smt_trie::keys.rs` for documentation. + +// addr = sum_{0<=i<5} a_i << (32i) +%macro key_balance + // stack: addr + PUSH 0x100000000 + // stack: u32max, addr + DUP1 DUP3 MOD + // stack: a_0, u32max, addr + DUP2 DUP4 %shr_const(32) MOD %shl_const(64) ADD + // stack: a_0 + a_1<<64, u32max, addr + DUP2 DUP4 %shr_const(64) MOD %shl_const(128) ADD + // stack: a_0 + a_1<<64 + a_2<<128, u32max, addr + DUP2 DUP4 %shr_const(96) MOD %shl_const(192) ADD + // stack: a_0 + a_1<<64 + a_2<<128 + a_3<<192, u32max, addr + SWAP2 %shr_const(128) + // stack: a_4, u32max, a_0 + a_1<<64 + a_2<<128 + a_3<<192 + %stack (y, u32max, x) -> (x, y, @POSEIDON_HASH_ZEROS) + POSEIDON +%endmacro + +// addr = sum_{0<=i<5} a_i << (32i) +%macro key_nonce + // stack: addr + PUSH 0x100000000 + // stack: u32max, addr + DUP1 DUP3 MOD + // stack: a_0, u32max, addr + DUP2 DUP4 %shr_const(32) MOD %shl_const(64) ADD + // stack: a_0 + a_1<<64, u32max, addr + DUP2 DUP4 %shr_const(64) MOD %shl_const(128) ADD + // stack: a_0 + a_1<<64 + a_2<<128, u32max, addr + DUP2 DUP4 %shr_const(96) MOD %shl_const(192) ADD + // stack: a_0 + a_1<<64 + a_2<<128 + a_3<<192, u32max, addr + SWAP2 %shr_const(128) + // stack: a_4, u32max, a_0 + a_1<<64 + a_2<<128 + a_3<<192 + %add_const(0x100000000000000000000000000000000) // SMT_KEY_NONCE (=1) << 128 + %stack (y, u32max, x) -> (x, y, @POSEIDON_HASH_ZEROS) + POSEIDON +%endmacro + +// addr = sum_{0<=i<5} a_i << (32i) +%macro key_code + // stack: addr + PUSH 0x100000000 + // stack: u32max, addr + DUP1 DUP3 MOD + // stack: a_0, u32max, addr + DUP2 DUP4 %shr_const(32) MOD %shl_const(64) ADD + // stack: a_0 + a_1<<64, u32max, addr + DUP2 DUP4 %shr_const(64) MOD %shl_const(128) ADD + // stack: a_0 + a_1<<64 + a_2<<128, u32max, addr + DUP2 DUP4 %shr_const(96) MOD %shl_const(192) ADD + // stack: a_0 + a_1<<64 + a_2<<128 + a_3<<192, u32max, addr + SWAP2 %shr_const(128) + // stack: a_4, u32max, a_0 + a_1<<64 + a_2<<128 + a_3<<192 + %add_const(0x200000000000000000000000000000000) // SMT_KEY_CODE (=2) << 128 + %stack (y, u32max, x) -> (x, y, @POSEIDON_HASH_ZEROS) + POSEIDON +%endmacro + +// addr = sum_{0<=i<5} a_i << (32i) +%macro key_code_length + // stack: addr + PUSH 0x100000000 + // stack: u32max, addr + DUP1 DUP3 MOD + // stack: a_0, u32max, addr + DUP2 DUP4 %shr_const(32) MOD %shl_const(64) ADD + // stack: a_0 + a_1<<64, u32max, addr + DUP2 DUP4 %shr_const(64) MOD %shl_const(128) ADD + // stack: a_0 + a_1<<64 + a_2<<128, u32max, addr + DUP2 DUP4 %shr_const(96) MOD %shl_const(192) ADD + // stack: a_0 + a_1<<64 + a_2<<128 + a_3<<192, u32max, addr + SWAP2 %shr_const(128) + // stack: a_4, u32max, a_0 + a_1<<64 + a_2<<128 + a_3<<192 + %add_const(0x400000000000000000000000000000000) // SMT_KEY_CODE_LENGTH (=4) << 128 + %stack (y, u32max, x) -> (x, y, @POSEIDON_HASH_ZEROS) + POSEIDON +%endmacro + +// addr = sum_{0<=i<5} a_i << (32i) +%macro key_storage + %stack (addr, slot) -> (slot, %%after, addr) + %jump(hash_limbs) +%%after: + // stack: capacity, addr + SWAP1 + // stack: addr, capacity + PUSH 0x100000000 + // stack: u32max, addr, capacity + DUP1 DUP3 MOD + // stack: a_0, u32max, addr + DUP2 DUP4 %shr_const(32) MOD %shl_const(64) ADD + // stack: a_0 + a_1<<64, u32max, addr + DUP2 DUP4 %shr_const(64) MOD %shl_const(128) ADD + // stack: a_0 + a_1<<64 + a_2<<128, u32max, addr + DUP2 DUP4 %shr_const(96) MOD %shl_const(192) ADD + // stack: a_0 + a_1<<64 + a_2<<128 + a_3<<192, u32max, addr + SWAP2 %shr_const(128) + // stack: a_4, u32max, a_0 + a_1<<64 + a_2<<128 + a_3<<192 + %add_const(0x300000000000000000000000000000000) // SMT_KEY_STORAGE (=3) << 128 + %stack (y, u32max, x, capacity) -> (x, y, capacity) + POSEIDON +%endmacro + +// slot = sum_{0<=i<8} s_i << (32i) +global hash_limbs: + // stack: slot, retdest + PUSH 0x100000000 + // stack: u32max, slot, retdest + DUP1 DUP3 MOD + // stack: s_0, u32max, slot + DUP2 DUP4 %shr_const(32) MOD %shl_const(64) ADD + // stack: s_0 + s_1<<64, u32max, slot + DUP2 DUP4 %shr_const(64) MOD %shl_const(128) ADD + // stack: s_0 + s_1<<64 + s_2<<128, u32max, slot + DUP2 DUP4 %shr_const(96) MOD %shl_const(192) ADD + // stack: s_0 + s_1<<64 + s_2<<128 + s_3<<192, u32max, slot + DUP2 DUP4 %shr_const(128) MOD + // stack: s_4, s_0 + s_1<<64 + s_2<<128 + s_3<<192, u32max, slot + DUP3 DUP5 %shr_const(160) MOD %shl_const(64) ADD + // stack: s_4 + s_5<<64, s_0 + s_1<<64 + s_2<<128 + s_3<<192, u32max, slot + DUP3 DUP5 %shr_const(192) MOD %shl_const(128) ADD + // stack: s_4 + s_5<<64 + s_6<<128, s_0 + s_1<<64 + s_2<<128 + s_3<<192, u32max, slot + DUP3 DUP5 %shr_const(224) MOD %shl_const(192) ADD + // stack: s_4 + s_5<<64 + s_6<<128 + s_7<<192, s_0 + s_1<<64 + s_2<<128 + s_3<<192, u32max, slot + %stack (b, a, u32max, slot) -> (a, b, 0) + POSEIDON + // stack: hash, retdest + SWAP1 JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/smt/read.asm b/evm_arithmetization/src/cpu/kernel/asm/smt/read.asm new file mode 100644 index 000000000..cd4bce33d --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/smt/read.asm @@ -0,0 +1,110 @@ +// Given a key, return a pointer to the associated SMT entry. +// Returns 0 if the key is not in the SMT. +global smt_read_state: + // stack: key, retdest + %split_key + // stack: k0, k1, k2, k3, retdest + PUSH 0 + // stack: level, k0, k1, k2, k3, retdest + %mload_global_metadata(@GLOBAL_METADATA_STATE_TRIE_ROOT) // node_ptr + // stack: node_ptr, level, k0, k1, k2, k3, retdest + %jump(smt_read) + +// Convenience macro to call smt_read_state and return where we left off. +%macro smt_read_state + %stack (key) -> (key, %%after) + %jump(smt_read_state) +%%after: +%endmacro + +// Return a pointer to the data at the given key in the SMT at `trie_data[node_ptr]`. +// Pseudocode: +// ``` +// read( HashNode { h }, key ) = if h == 0 then 0 else PANIC +// read( InternalNode { left, right }, key ) = if key&1 { read( right, key>>1 ) } else { read( left, key>>1 ) } +// read( Leaf { rem_key', value }, key ) = if rem_key == rem_key' then &value else 0 +// ``` +global smt_read: + // stack: node_ptr, level, ks, retdest + DUP1 %mload_trie_data + // stack: node_type, node_ptr, level, ks, retdest + // Increment node_ptr, so it points to the node payload instead of its type. + SWAP1 %increment SWAP1 + // stack: node_type, node_payload_ptr, level, ks, retdest + + DUP1 %eq_const(@SMT_NODE_HASH) %jumpi(smt_read_hash) + DUP1 %eq_const(@SMT_NODE_INTERNAL) %jumpi(smt_read_internal) + %eq_const(@SMT_NODE_LEAF) %jumpi(smt_read_leaf) + PANIC + +smt_read_hash: + // stack: node_type, node_payload_ptr, level, ks, retdest + POP + // stack: node_payload_ptr, level, ks, retdest + %mload_trie_data + // stack: hash, level, ks, retdest + ISZERO %jumpi(smt_read_empty) + PANIC // Trying to read a non-empty hash node. Should never happen. + +smt_read_empty: + %stack (level, k0, k1, k2, k3, retdest) -> (retdest, 0) + JUMP + +smt_read_internal: + // stack: node_type, node_payload_ptr, level, ks, retdest + POP + // stack: node_payload_ptr, level, ks, retdest + DUP2 %and_const(3) // level mod 4 + // stack: level%4, node_payload_ptr, level, ks, retdest + DUP1 %eq_const(0) %jumpi(smt_read_internal_0) + DUP1 %eq_const(1) %jumpi(smt_read_internal_1) + DUP1 %eq_const(2) %jumpi(smt_read_internal_2) + DUP1 %eq_const(3) %jumpi(smt_read_internal_3) + PANIC +smt_read_internal_0: + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k0, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk0, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, newk0, k1, k2, k3 ) + %jump(smt_read_internal_contd) +smt_read_internal_1: + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k1, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk1, node_payload_ptr, level , k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, newk1, k2, k3 ) + %jump(smt_read_internal_contd) +smt_read_internal_2: + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k2, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk2, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, k1, newk2, k3 ) + %jump(smt_read_internal_contd) +smt_read_internal_3: + %stack (level_mod_4, node_payload_ptr, level, k0, k1, k2, k3 ) -> (k3, node_payload_ptr, level, k0, k1, k2, k3 ) + %pop_bit + %stack (bit, newk3, node_payload_ptr, level, k0, k1, k2, k3 ) -> (bit, node_payload_ptr, level, k0, k1, k2, newk3 ) +smt_read_internal_contd: + // stack: bit, node_payload_ptr, level, k0, k1, k2, k3, retdest + ADD + // stack: child_ptr_ptr, level, k0, k1, k2, k3, retdest + %mload_trie_data + // stack: child_ptr, level, k0, k1, k2, k3, retdest + SWAP1 %increment SWAP1 + // stack: child_ptr, level+1, k0, k1, k2, k3, retdest + %jump(smt_read) + +smt_read_leaf: + // stack: node_payload_ptr, level, ks, retdest + DUP1 %mload_trie_data + // stack: rem_key, node_payload_ptr, level, ks, retdest + SWAP1 + // stack: node_payload_ptr, rem_key, level, ks, retdest + %increment + %stack (value_ptr, rem_key, level, k0, k1, k2, k3) -> (k0, k1, k2, k3, rem_key, value_ptr) + %combine_key + // stack: this_rem_key, rem_key, value_ptr, retdest + EQ %jumpi(smt_read_existing_leaf) +smt_read_non_existing_leaf: + %stack (value_ptr, retdest) -> (retdest, 0) + JUMP + +smt_read_existing_leaf: + // stack: value_ptr, retdest + SWAP1 JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/smt/utils.asm b/evm_arithmetization/src/cpu/kernel/asm/smt/utils.asm new file mode 100644 index 000000000..4f8c2832e --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/asm/smt/utils.asm @@ -0,0 +1,129 @@ +// Input: x +// Output: (x&1, x>>1) +%macro pop_bit + // stack: key + DUP1 %shr_const(1) + // stack: key>>1, key + SWAP1 %mod_const(2) + // stack: key&1, key>>1 +%endmacro + +// Returns a non-zero value if the node is non-empty. +%macro is_non_empty_node + // stack: node_ptr + DUP1 %mload_trie_data %jumpi(%%end) // If the node is not a hash node, node_ptr is non-zero. + // The node is a hash node + // stack: node_ptr + %increment %mload_trie_data + // stack: hash +%%end: +%endmacro + +// Input: key = k0 + k1.2^64 + k2.2^128 + k3.2^192, with 0<=ki<2^64. +// Output: (k0, k1, k2, k3) +%macro split_key + // stack: key + DUP1 %shr_const(128) %mod_const(0x10000000000000000) + // stack: k2, key + DUP2 %shr_const(64) %mod_const(0x10000000000000000) + // stack: k1, k2, key + DUP3 %shr_const(192) + // stack: k3, k1, k2, key + SWAP3 %mod_const(0x10000000000000000) + // stack: k0, k1, k2, k3 +%endmacro + +// Input: (k0, k1, k2, k3) +// Output: k0 + k1.2^64 + k2.2^128 + k3.2^192 +%macro combine_key + // stack: k0, k1, k2, k3 + SWAP1 %shl_const(64) ADD + // stack: k0 + k1<<64, k2, k3 + SWAP1 %shl_const(128) ADD + // stack: k0 + k1<<64 + k2<<128, k3 + SWAP1 %shl_const(192) ADD + // stack: k0 + k1<<64 + k2<<128 + k3<<192 +%endmacro + + +// Pseudocode: +// ``` +// def recombine_key(key, bit, level): +// k0, k1, k2, k3 = [(key>>(64*i))&(2**64-1) for i in range(4)] +// match level%4: +// 0 => k0 = 2*k0 + bit +// 1 => k1 = 2*k1 + bit +// 2 => k2 = 2*k2 + bit +// 3 => k3 = 2*k3 + bit +// return k0 + (k1<<64) + (k2<<128) + (k3<<192) +// ``` +%macro recombine_key + // stack: key, bit, level + SWAP1 + // stack: bit, key, level + SWAP2 + // stack: level, key, bit + %mod_const(4) + // stack: level%4, key, bit + DUP1 %eq_const(0) %jumpi(%%recombine_key_0) + DUP1 %eq_const(1) %jumpi(%%recombine_key_1) + DUP1 %eq_const(2) %jumpi(%%recombine_key_2) + DUP1 %eq_const(3) %jumpi(%%recombine_key_3) + PANIC +%%recombine_key_0: + // stack: level%4, key, bit + POP + // stack: key, bit + %split_key + // stack: k0, k1, k2, k3, bit + %shl_const(1) + // stack: k0<<1, k1, k2, k3, bit + DUP5 ADD + // stack: k0<<1 + bit, k1, k2, k3, bit + %combine_key + %stack (newkey, bit) -> (newkey) + %jump(%%after) +%%recombine_key_1: + // stack: level%4, key, bit + POP + // stack: key, bit + %split_key + // stack: k0, k1, k2, k3, bit + DUP2 %shl_const(1) + // stack: k1<<1, k0, k1, k2, k3, bit + DUP6 ADD + // stack: k1<<1 + bit, k0, k1, k2, k3, bit + SWAP2 POP + %combine_key + %stack (newkey, bit) -> (newkey) + %jump(%%after) +%%recombine_key_2: + // stack: key, bit + POP + // stack: key, bit + %split_key + // stack: k0, k1, k2, k3, bit + DUP3 %shl_const(1) + // stack: k2<<1, k0, k1, k2, k3, bit + DUP6 ADD + // stack: k2<<1 + bit, k0, k1, k2, k3, bit + SWAP3 POP + %combine_key + %stack (newkey, bit) -> (newkey) + %jump(%%after) +%%recombine_key_3: + // stack: key, bit + POP + // stack: key, bit + %split_key + // stack: k0, k1, k2, k3, bit + DUP4 %shl_const(1) + // stack: k3<<1, k0, k1, k2, k3, bit + DUP6 ADD + // stack: k3<<1 + bit, k0, k1, k2, k3, bit + SWAP4 POP + %combine_key + %stack (newkey, bit) -> (newkey) +%%after: + // stack: newkey +%endmacro diff --git a/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm b/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm index 613131c3e..7487c3b20 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm @@ -292,9 +292,8 @@ decode_and_store_access_list_finish: %endmacro insert_accessed_storage_keys_with_original_value: - %stack (addr, key, retdest) -> (key, addr, after_read, addr, key, retdest) - %jump(sload_with_addr) -after_read: + %stack (addr, key, retdest) -> (addr, key, addr, key, retdest) + %key_storage %smt_read_state %mload_trie_data %stack (value, addr, key, retdest) -> ( addr, key, value, retdest) %insert_accessed_storage_keys // stack: cold_access, value_ptr, value, retdest @@ -304,25 +303,3 @@ after_read: // stack: cold_access, retdest POP JUMP - - -sload_with_addr: - %stack (slot, addr) -> (slot, addr, after_storage_read) - %slot_to_storage_key - // stack: storage_key, addr, after_storage_read - PUSH 64 // storage_key has 64 nibbles - %stack (n64, storage_key, addr, after_storage_read) -> (addr, n64, storage_key, after_storage_read) - %mpt_read_state_trie - // stack: account_ptr, 64, storage_key, after_storage_read - DUP1 ISZERO %jumpi(ret_zero) // TODO: Fix this. This should never happen. - // stack: account_ptr, 64, storage_key, after_storage_read - %add_const(2) - // stack: storage_root_ptr_ptr - %mload_trie_data - // stack: storage_root_ptr, 64, storage_key, after_storage_read - %jump(mpt_read) - -ret_zero: - // stack: account_ptr, 64, storage_key, after_storage_read, retdest - %pop4 - PUSH 0 SWAP1 JUMP diff --git a/evm_arithmetization/src/cpu/kernel/assembler.rs b/evm_arithmetization/src/cpu/kernel/assembler.rs index 7dfba09fc..1bf97e604 100644 --- a/evm_arithmetization/src/cpu/kernel/assembler.rs +++ b/evm_arithmetization/src/cpu/kernel/assembler.rs @@ -129,6 +129,7 @@ pub(crate) fn assemble( for file in files { let start = Instant::now(); let mut file = file.body; + file = expand_conditional_blocks(file); file = expand_macros(file, ¯os, &mut macro_counter); file = inline_constants(file, &constants); file = expand_stack_manipulation(file); @@ -159,22 +160,44 @@ pub(crate) fn assemble( fn find_macros(files: &[File]) -> HashMap { let mut macros = HashMap::new(); for file in files { - for item in &file.body { - if let Item::MacroDef(name, params, items) = item { - let signature = MacroSignature { - name: name.clone(), - num_params: params.len(), - }; - let macro_ = Macro { - params: params.clone(), - items: items.clone(), - }; - let old = macros.insert(signature.clone(), macro_); - assert!(old.is_none(), "Duplicate macro signature: {signature:?}"); + find_macros_internal(&file.body, &mut macros); + } + macros +} + +fn find_macros_internal(items: &[Item], macros: &mut HashMap) { + for item in items { + if let Item::ConditionalBlock(_, local_items) = item { + find_macros_internal(local_items, macros); + } + if let Item::MacroDef(name, params, local_items) = item { + let signature = MacroSignature { + name: name.clone(), + num_params: params.len(), + }; + let macro_ = Macro { + params: params.clone(), + items: local_items.clone(), + }; + let old = macros.insert(signature.clone(), macro_); + assert!(old.is_none(), "Duplicate macro signature: {signature:?}"); + } + } +} + +fn expand_conditional_blocks(body: Vec) -> Vec { + let mut expanded = vec![]; + for item in body { + match item { + Item::ConditionalBlock(_, items) => { + expanded.extend(items); + } + _ => { + expanded.push(item); } } } - macros + expanded } fn expand_macros( @@ -196,6 +219,9 @@ fn expand_macros( expanded.extend(expand_macros(body.clone(), macros, macro_counter)); } } + Item::ConditionalBlock(_, items) => { + expanded.extend(expand_macros(items.clone(), macros, macro_counter)); + } item => { expanded.push(item); } @@ -325,7 +351,8 @@ fn find_labels( let mut local_labels = HashMap::::new(); for item in body { match item { - Item::MacroDef(_, _, _) + Item::ConditionalBlock(_, _) + | Item::MacroDef(_, _, _) | Item::MacroCall(_, _) | Item::Repeat(_, _) | Item::StackManipulation(_, _) @@ -379,7 +406,8 @@ fn assemble_file( // Assemble the file. for item in body { match item { - Item::MacroDef(_, _, _) + Item::ConditionalBlock(_, _) + | Item::MacroDef(_, _, _) | Item::MacroCall(_, _) | Item::Repeat(_, _) | Item::StackManipulation(_, _) @@ -437,6 +465,8 @@ fn push_target_size(target: &PushTarget) -> u8 { #[cfg(test)] mod tests { + use std::collections::HashSet; + use super::*; use crate::cpu::kernel::parser::parse; @@ -734,7 +764,7 @@ mod tests { constants: HashMap, optimize: bool, ) -> Kernel { - let parsed_files = files.iter().map(|f| parse(f)).collect_vec(); + let parsed_files = files.iter().map(|f| parse(f, HashSet::new())).collect_vec(); assemble(parsed_files, constants, optimize) } } diff --git a/evm_arithmetization/src/cpu/kernel/ast.rs b/evm_arithmetization/src/cpu/kernel/ast.rs index 0af3bdabe..fa38166ba 100644 --- a/evm_arithmetization/src/cpu/kernel/ast.rs +++ b/evm_arithmetization/src/cpu/kernel/ast.rs @@ -9,6 +9,8 @@ pub(crate) struct File { #[derive(Eq, PartialEq, Clone, Debug)] pub(crate) enum Item { + /// Defines a conditional, feature-gated, block of items. + ConditionalBlock(String, Vec), /// Defines a new macro: name, params, body. MacroDef(String, Vec, Vec), /// Calls a macro: name, args. diff --git a/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs b/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs index 8f18ec60a..f225d6d70 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/global_metadata.rs @@ -89,7 +89,7 @@ pub(crate) enum GlobalMetadata { ContractCreation, IsPrecompileFromEoa, CallStackDepth, - /// Transaction logs list length + /// Transaction logs list length. LogsLen, LogsDataLen, LogsPayloadLen, @@ -111,10 +111,13 @@ pub(crate) enum GlobalMetadata { BlobVersionedHashesRlpLen, // Number of blob versioned hashes contained in the current type-3 transaction. BlobVersionedHashesLen, + + /// Number of used storage slots in newly created contracts. + NewStorageSlotsLen, } impl GlobalMetadata { - pub(crate) const COUNT: usize = 55; + pub(crate) const COUNT: usize = 56; /// Unscales this virtual offset by their respective `Segment` value. pub(crate) const fn unscale(&self) -> usize { @@ -178,6 +181,7 @@ impl GlobalMetadata { Self::BlobVersionedHashesRlpStart, Self::BlobVersionedHashesRlpLen, Self::BlobVersionedHashesLen, + Self::NewStorageSlotsLen, ] } @@ -239,6 +243,7 @@ impl GlobalMetadata { Self::BlobVersionedHashesRlpStart => "GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_START", Self::BlobVersionedHashesRlpLen => "GLOBAL_METADATA_BLOB_VERSIONED_HASHES_RLP_LEN", Self::BlobVersionedHashesLen => "GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN", + Self::NewStorageSlotsLen => "GLOBAL_METADATA_NEW_STORAGE_SLOTS_LEN", } } } diff --git a/evm_arithmetization/src/cpu/kernel/constants/mod.rs b/evm_arithmetization/src/cpu/kernel/constants/mod.rs index 3bd99f610..aeaf300d3 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/mod.rs @@ -1,11 +1,13 @@ use std::collections::HashMap; -use ethereum_types::{H256, U256}; +use ethereum_types::U256; use hex_literal::hex; +use smt_trie::code::hash_bytecode_u256; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::constants::journal_entry::JournalEntry; +use crate::cpu::kernel::constants::smt_type::PartialSmtType; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::cpu::kernel::constants::txn_fields::NormalizedTxnField; use crate::generation::mpt::AccountRlp; @@ -15,6 +17,7 @@ pub(crate) mod context_metadata; mod exc_bitfields; pub(crate) mod global_metadata; pub(crate) mod journal_entry; +pub(crate) mod smt_type; pub(crate) mod trie_type; pub(crate) mod txn_fields; @@ -57,10 +60,17 @@ pub(crate) fn evm_constants() -> HashMap { c.insert(MAX_NONCE.0.into(), U256::from(MAX_NONCE.1)); c.insert(CALL_STACK_LIMIT.0.into(), U256::from(CALL_STACK_LIMIT.1)); + c.insert(POSEIDON_HASH_ZEROS.0.into(), POSEIDON_HASH_ZEROS.1); c.insert( cancun_constants::BEACON_ROOTS_CONTRACT_STATE_KEY.0.into(), U256::from_big_endian(&cancun_constants::BEACON_ROOTS_CONTRACT_STATE_KEY.1), ); + c.insert( + cancun_constants::BEACON_ROOTS_CONTRACT_ADDRESS_HASHED + .0 + .into(), + U256::from_big_endian(&cancun_constants::BEACON_ROOTS_CONTRACT_ADDRESS_HASHED.1), + ); c.insert( cancun_constants::HISTORY_BUFFER_LENGTH.0.into(), cancun_constants::HISTORY_BUFFER_LENGTH.1.into(), @@ -95,6 +105,9 @@ pub(crate) fn evm_constants() -> HashMap { for trie_type in PartialTrieType::all() { c.insert(trie_type.var_name().into(), (trie_type as u32).into()); } + for trie_type in PartialSmtType::all() { + c.insert(trie_type.var_name().into(), (trie_type as u32).into()); + } for entry in JournalEntry::all() { c.insert(entry.var_name().into(), (entry as u32).into()); } @@ -142,7 +155,7 @@ const MISC_CONSTANTS: [(&str, [u8; 32]); 5] = [ ), ]; -const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ +const HASH_CONSTANTS: [(&str, [u8; 32]); 3] = [ // Hash of an empty string: keccak(b'').hex() ( "EMPTY_STRING_HASH", @@ -153,6 +166,10 @@ const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ "EMPTY_NODE_HASH", hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), ), + ( + "EMPTY_STRING_POSEIDON_HASH", + hex!("3baed9289a384f6c1c05d92b56c801c2d2e2a7050d6c16538b814fa186835c79"), + ), ]; const EC_CONSTANTS: [(&str, [u8; 32]); 25] = [ @@ -345,6 +362,16 @@ const CODE_SIZE_LIMIT: [(&str, u64); 3] = [ const MAX_NONCE: (&str, u64) = ("MAX_NONCE", 0xffffffffffffffff); const CALL_STACK_LIMIT: (&str, u64) = ("CALL_STACK_LIMIT", 1024); +const POSEIDON_HASH_ZEROS: (&str, U256) = ( + "POSEIDON_HASH_ZEROS", + U256([ + 4330397376401421145, + 14124799381142128323, + 8742572140681234676, + 14345658006221440202, + ]), +); + /// Cancun-related constants /// See and /// . @@ -383,21 +410,23 @@ pub mod cancun_constants { pub const BEACON_ROOTS_CONTRACT_CODE_HASH: [u8; 32] = hex!("f57acd40259872606d76197ef052f3d35588dadf919ee1f0e3cb9b62d3f4b02c"); - pub const BEACON_ROOTS_CONTRACT_ADDRESS_HASHED: [u8; 32] = - hex!("37d65eaa92c6bc4c13a5ec45527f0c18ea8932588728769ec7aecfe6d9f32e42"); - - pub const BEACON_ROOTS_ACCOUNT: AccountRlp = AccountRlp { - nonce: U256::zero(), - balance: U256::zero(), - // Storage root for this account at genesis. - storage_root: H256(hex!( - "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - )), - code_hash: H256(BEACON_ROOTS_CONTRACT_CODE_HASH), - }; + pub const BEACON_ROOTS_CONTRACT_ADDRESS_HASHED: (&str, [u8; 32]) = ( + "BEACON_ROOTS_CONTRACT_ADDRESS_HASHED", + hex!("37d65eaa92c6bc4c13a5ec45527f0c18ea8932588728769ec7aecfe6d9f32e42"), + ); + + pub fn beacon_roots_account() -> AccountRlp { + AccountRlp { + nonce: U256::zero(), + balance: U256::zero(), + code_hash: hash_bytecode_u256(BEACON_ROOTS_CONTRACT_CODE.to_vec()), + code_length: U256([BEACON_ROOTS_CONTRACT_CODE.len() as u64, 0, 0, 0]), + } + } } pub mod global_exit_root { + use super::*; /// Taken from https://github.com/0xPolygonHermez/cdk-erigon/blob/61f0b6912055c73f6879ea7e9b5bac22ea5fc85c/zk/utils/global_exit_root.go#L16. @@ -415,13 +444,12 @@ pub mod global_exit_root { pub const GLOBAL_EXIT_ROOT_ADDRESS_HASHED: [u8; 32] = hex!("1d5e9c22b4b1a781d0ef63e9c1293c2a45fee966809019aa9804b5e7148b0ca9"); - pub const GLOBAL_EXIT_ROOT_ACCOUNT: AccountRlp = AccountRlp { - nonce: U256::zero(), - balance: U256::zero(), - // Empty storage root - storage_root: H256(hex!( - "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - )), - code_hash: H256(GLOBAL_EXIT_ROOT_CONTRACT_CODE_HASH), - }; + pub fn global_exit_root_account() -> AccountRlp { + AccountRlp { + nonce: U256::zero(), + balance: U256::zero(), + code_hash: hash_bytecode_u256(GLOBAL_EXIT_ROOT_CONTRACT_CODE.to_vec()), + code_length: U256([GLOBAL_EXIT_ROOT_CONTRACT_CODE.len() as u64, 0, 0, 0]), + } + } } diff --git a/evm_arithmetization/src/cpu/kernel/constants/smt_type.rs b/evm_arithmetization/src/cpu/kernel/constants/smt_type.rs new file mode 100644 index 000000000..b134598bc --- /dev/null +++ b/evm_arithmetization/src/cpu/kernel/constants/smt_type.rs @@ -0,0 +1,23 @@ +#[derive(Copy, Clone, Debug)] +pub(crate) enum PartialSmtType { + Hash = 0, + Internal = 1, + Leaf = 2, +} + +impl PartialSmtType { + pub(crate) const COUNT: usize = 3; + + pub(crate) fn all() -> [Self; Self::COUNT] { + [Self::Hash, Self::Internal, Self::Leaf] + } + + /// The variable name that gets passed into kernel assembly code. + pub(crate) fn var_name(&self) -> &'static str { + match self { + Self::Hash => "SMT_NODE_HASH", + Self::Internal => "SMT_NODE_INTERNAL", + Self::Leaf => "SMT_NODE_LEAF", + } + } +} diff --git a/evm_arithmetization/src/cpu/kernel/evm_asm.pest b/evm_arithmetization/src/cpu/kernel/evm_asm.pest index 40dec03b3..7c2d645ae 100644 --- a/evm_arithmetization/src/cpu/kernel/evm_asm.pest +++ b/evm_arithmetization/src/cpu/kernel/evm_asm.pest @@ -15,7 +15,7 @@ literal = { literal_hex | literal_decimal } variable = ${ "$" ~ identifier } constant = ${ "@" ~ identifier } -item = { macro_def | macro_call | repeat | stack | global_label_decl | local_label_decl | macro_label_decl | bytes_item | jumptable_item | push_instruction | prover_input_instruction | nullary_instruction } +item = { conditional_block | macro_def | macro_call | repeat | stack | global_label_decl | local_label_decl | macro_label_decl | bytes_item | jumptable_item | push_instruction | prover_input_instruction | nullary_instruction } macro_def = { ^"%macro" ~ identifier ~ paramlist? ~ item* ~ ^"%endmacro" } macro_call = ${ "%" ~ !((^"macro" | ^"endmacro" | ^"rep" | ^"endrep" | ^"stack") ~ !identifier_char) ~ identifier ~ macro_arglist? } repeat = { ^"%rep" ~ literal ~ item* ~ ^"%endrep" } @@ -43,5 +43,7 @@ prover_input_instruction = { ^"PROVER_INPUT" ~ "(" ~ prover_input_fn ~ ")" } prover_input_fn = { identifier ~ ("::" ~ identifier)*} nullary_instruction = { identifier } +conditional_block = { ^"#" ~ "[" ~ "cfg" ~ "(" ~ "feature" ~ "=" ~ identifier ~ ")" ~ "]" ~ "{" ~ item* ~ ^"}"} + file = { SOI ~ item* ~ silent_eoi } silent_eoi = _{ !ANY } diff --git a/evm_arithmetization/src/cpu/kernel/interpreter.rs b/evm_arithmetization/src/cpu/kernel/interpreter.rs index 38fbe2320..3ef63d61f 100644 --- a/evm_arithmetization/src/cpu/kernel/interpreter.rs +++ b/evm_arithmetization/src/cpu/kernel/interpreter.rs @@ -12,6 +12,8 @@ use ethereum_types::{BigEndianHash, U256}; use log::Level; use mpt_trie::partial_trie::PartialTrie; use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; +use smt_trie::smt::hash_serialize_u256; use crate::byte_packing::byte_packing_stark::BytePackingOp; use crate::cpu::columns::CpuColumnsView; @@ -40,7 +42,7 @@ use crate::{arithmetic, keccak, logic}; /// Halt interpreter execution whenever a jump to this offset is done. const DEFAULT_HALT_OFFSET: usize = 0xdeadbeef; -pub(crate) struct Interpreter { +pub(crate) struct Interpreter { /// The interpreter holds a `GenerationState` to keep track of the memory /// and registers. pub(crate) generation_state: GenerationState, @@ -62,7 +64,7 @@ pub(crate) struct Interpreter { /// Simulates the CPU execution from `state` until the program counter reaches /// `final_label` in the current context. -pub(crate) fn simulate_cpu_and_get_user_jumps( +pub(crate) fn simulate_cpu_and_get_user_jumps( final_label: &str, state: &GenerationState, ) -> Option>> { @@ -90,7 +92,7 @@ pub(crate) fn simulate_cpu_and_get_user_jumps( } } -impl Interpreter { +impl Interpreter { /// Returns an instance of `Interpreter` given `GenerationInputs`, and /// assuming we are initializing with the `KERNEL` code. pub(crate) fn new_with_generation_inputs( @@ -220,7 +222,7 @@ impl Interpreter { ), ( GlobalMetadata::StateTrieRootDigestBefore, - h2u(tries.state_trie.hash()), + hash_serialize_u256(&tries.state_smt), ), ( GlobalMetadata::TransactionTrieRootDigestBefore, @@ -405,7 +407,7 @@ impl Interpreter { } } -impl State for Interpreter { +impl State for Interpreter { //// Returns a `GenerationStateCheckpoint` to save the current registers and /// reset memory operations to the empty vector. fn checkpoint(&mut self) -> GenerationStateCheckpoint { @@ -548,7 +550,7 @@ impl State for Interpreter { } } -impl Transition for Interpreter { +impl Transition for Interpreter { fn generate_jumpdest_analysis(&mut self, dst: usize) -> bool { if self.is_jumpdest_analysis && !self.generation_state.registers.is_kernel { self.add_jumpdest_offset(dst); @@ -625,6 +627,7 @@ fn get_mnemonic(opcode: u8) -> &'static str { 0x1d => "SAR", 0x20 => "KECCAK256", 0x21 => "KECCAK_GENERAL", + 0x22 => "POSEIDON", 0x30 => "ADDRESS", 0x31 => "BALANCE", 0x32 => "ORIGIN", diff --git a/evm_arithmetization/src/cpu/kernel/mod.rs b/evm_arithmetization/src/cpu/kernel/mod.rs index 8c9d1bf88..7b8a58566 100644 --- a/evm_arithmetization/src/cpu/kernel/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/mod.rs @@ -12,6 +12,8 @@ mod utils; pub(crate) mod interpreter; +use std::collections::HashSet; + pub use constants::cancun_constants; pub use constants::global_exit_root; @@ -26,7 +28,7 @@ use crate::cpu::kernel::constants::evm_constants; /// Assemble files, outputting bytes. /// This is for debugging the kernel only. pub fn assemble_to_bytes(files: &[String]) -> Vec { - let parsed_files: Vec<_> = files.iter().map(|f| parse(f)).collect(); + let parsed_files: Vec<_> = files.iter().map(|f| parse(f, HashSet::new())).collect(); let kernel = assemble(parsed_files, evm_constants(), true); kernel.code } diff --git a/evm_arithmetization/src/cpu/kernel/opcodes.rs b/evm_arithmetization/src/cpu/kernel/opcodes.rs index 6491003f1..fde72e247 100644 --- a/evm_arithmetization/src/cpu/kernel/opcodes.rs +++ b/evm_arithmetization/src/cpu/kernel/opcodes.rs @@ -39,6 +39,8 @@ pub fn get_opcode(mnemonic: &str) -> u8 { "SAR" => 0x1d, "KECCAK256" => 0x20, "KECCAK_GENERAL" => 0x21, + "POSEIDON" => 0x22, + "POSEIDON_GENERAL" => 0x23, "ADDRESS" => 0x30, "BALANCE" => 0x31, "ORIGIN" => 0x32, diff --git a/evm_arithmetization/src/cpu/kernel/parser.rs b/evm_arithmetization/src/cpu/kernel/parser.rs index 7864acfe0..c661dabd2 100644 --- a/evm_arithmetization/src/cpu/kernel/parser.rs +++ b/evm_arithmetization/src/cpu/kernel/parser.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{collections::HashSet, str::FromStr}; use ethereum_types::U256; use pest::iterators::Pair; @@ -12,22 +12,26 @@ use crate::cpu::kernel::ast::{File, Item, PushTarget, StackReplacement}; #[grammar = "cpu/kernel/evm_asm.pest"] struct AsmParser; -pub(crate) fn parse(s: &str) -> File { +pub(crate) fn parse(s: &str, active_features: HashSet<&str>) -> File { let file = AsmParser::parse(Rule::file, s) .expect("Parsing failed") .next() .unwrap(); - let body = file.into_inner().map(parse_item).collect(); + let body = file + .into_inner() + .map(|i| parse_item(i, &active_features)) + .collect(); File { body } } -fn parse_item(item: Pair) -> Item { +fn parse_item(item: Pair, active_features: &HashSet<&str>) -> Item { assert_eq!(item.as_rule(), Rule::item); let item = item.into_inner().next().unwrap(); match item.as_rule() { - Rule::macro_def => parse_macro_def(item), + Rule::conditional_block => parse_conditional_block(item, active_features), + Rule::macro_def => parse_macro_def(item, active_features), Rule::macro_call => parse_macro_call(item), - Rule::repeat => parse_repeat(item), + Rule::repeat => parse_repeat(item, active_features), Rule::stack => parse_stack(item), Rule::global_label_decl => { Item::GlobalLabelDeclaration(item.into_inner().next().unwrap().as_str().into()) @@ -57,7 +61,23 @@ fn parse_item(item: Pair) -> Item { } } -fn parse_macro_def(item: Pair) -> Item { +fn parse_conditional_block(item: Pair, active_features: &HashSet<&str>) -> Item { + assert_eq!(item.as_rule(), Rule::conditional_block); + let mut inner = item.into_inner().peekable(); + + let name = inner.next().unwrap().as_str(); + + if active_features.contains(&name) { + Item::ConditionalBlock( + name.into(), + inner.map(|i| parse_item(i, active_features)).collect(), + ) + } else { + Item::ConditionalBlock(name.into(), vec![]) + } +} + +fn parse_macro_def(item: Pair, active_features: &HashSet<&str>) -> Item { assert_eq!(item.as_rule(), Rule::macro_def); let mut inner = item.into_inner().peekable(); @@ -71,7 +91,11 @@ fn parse_macro_def(item: Pair) -> Item { vec![] }; - Item::MacroDef(name, params, inner.map(parse_item).collect()) + Item::MacroDef( + name, + params, + inner.map(|i| parse_item(i, active_features)).collect(), + ) } fn parse_macro_call(item: Pair) -> Item { @@ -91,11 +115,14 @@ fn parse_macro_call(item: Pair) -> Item { Item::MacroCall(name, args) } -fn parse_repeat(item: Pair) -> Item { +fn parse_repeat(item: Pair, active_features: &HashSet<&str>) -> Item { assert_eq!(item.as_rule(), Rule::repeat); let mut inner = item.into_inner(); let count = parse_literal_u256(inner.next().unwrap()); - Item::Repeat(count, inner.map(parse_item).collect()) + Item::Repeat( + count, + inner.map(|i| parse_item(i, active_features)).collect(), + ) } fn parse_stack(item: Pair) -> Item { @@ -208,3 +235,186 @@ fn parse_hex(hex: Pair) -> String { debug_assert!(prefix == "0x" || prefix == "0X"); hex.as_str()[2..].to_string() } + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use super::*; + use crate::cpu::kernel::assembler::assemble; + + #[test] + fn test_feature() { + let code = r#" + %macro bar_foo + #[cfg(feature = feature_1)] + { + %bar + PUSH 3 + ADD + } + %endmacro + #[cfg(feature = feature_1)] + { + %macro bar + PUSH 2 + MUL + %endmacro + } + + global foo_1: + PUSH 1 + PUSH 2 + + #[cfg(feature = feature_1)] + { + %bar_foo + PUSH 1 + } + PUSH 3 + PUSH 4 + ADD + + global foo_3: + PUSH 5 + PUSH 6 + DIV + + #[cfg(feature = feature_2)] + { + global foo_4: + PUSH 7 + PUSH 8 + MOD + } + "#; + + // Test `feature_1`. + let active_features = HashSet::from(["feature_1"]); + + let parsed_code = parse(code, active_features); + let final_code = assemble(vec![parsed_code], HashMap::new(), false); + + let expected_code = r#" + %macro bar + PUSH 2 + MUL + PUSH 3 + ADD + %endmacro + + global foo_1: + PUSH 1 + PUSH 2 + %bar + PUSH 1 + PUSH 3 + PUSH 4 + ADD + + global foo_3: + PUSH 5 + PUSH 6 + DIV + "#; + + let parsed_expected = parse(expected_code, HashSet::new()); + let final_expected = assemble(vec![parsed_expected], HashMap::new(), false); + + assert_eq!(final_code.code, final_expected.code); + + // Test `feature_2`. + let active_features = HashSet::from(["feature_2"]); + + let parsed_code = parse(code, active_features); + let final_code = assemble(vec![parsed_code], HashMap::new(), false); + + let expected_code = r#" + global foo_1: + PUSH 1 + PUSH 2 + PUSH 3 + PUSH 4 + ADD + + global foo_3: + PUSH 5 + PUSH 6 + DIV + + global foo_4: + PUSH 7 + PUSH 8 + MOD + "#; + + let parsed_expected = parse(expected_code, HashSet::new()); + let final_expected = assemble(vec![parsed_expected], HashMap::new(), false); + + assert_eq!(final_code.code, final_expected.code); + + // Test with both features enabled. + let active_features = HashSet::from(["feature_1", "feature_2"]); + + let parsed_code = parse(code, active_features); + let final_code = assemble(vec![parsed_code], HashMap::new(), false); + + let expected_code = r#" + %macro bar + PUSH 2 + MUL + PUSH 3 + ADD + %endmacro + + global foo_1: + PUSH 1 + PUSH 2 + %bar + PUSH 1 + PUSH 3 + PUSH 4 + ADD + + global foo_3: + PUSH 5 + PUSH 6 + DIV + + global foo_4: + PUSH 7 + PUSH 8 + MOD + "#; + + let parsed_expected = parse(expected_code, HashSet::new()); + let final_expected = assemble(vec![parsed_expected], HashMap::new(), false); + + assert_eq!(final_code.code, final_expected.code); + + // Test with all features disabled. + let active_features = HashSet::new(); + + let parsed_code = parse(code, active_features); + let final_code = assemble(vec![parsed_code], HashMap::new(), false); + + let expected_code = r#" + global foo_1: + PUSH 1 + PUSH 2 + PUSH 3 + PUSH 4 + ADD + + global foo_3: + PUSH 5 + PUSH 6 + DIV + "#; + + let parsed_expected = parse(expected_code, HashSet::new()); + let final_expected = assemble(vec![parsed_expected], HashMap::new(), false); + + assert_eq!(final_code.code, final_expected.code); + } +} diff --git a/evm_arithmetization/src/cpu/kernel/tests/account_code.rs b/evm_arithmetization/src/cpu/kernel/tests/account_code.rs index 125760ee5..13ac32ec7 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/account_code.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/account_code.rs @@ -1,20 +1,21 @@ use std::collections::HashMap; use anyhow::Result; -use ethereum_types::{Address, BigEndianHash, H256, U256}; +use ethereum_types::{Address, H160, U256}; use hex_literal::hex; -use keccak_hash::keccak; -use mpt_trie::nibbles::Nibbles; -use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField as F; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use rand::{thread_rng, Rng}; +use smt_trie::code::{hash_bytecode_u256, hash_contract_bytecode}; +use smt_trie::db::{Db, MemoryDb}; +use smt_trie::keys::{key_balance, key_code, key_code_length, key_nonce, key_storage}; +use smt_trie::smt::Smt; +use smt_trie::utils::{hashout2u, key2u}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::context_metadata::ContextMetadata::{self, GasLimit}; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::mpt::nibbles_64; use crate::generation::mpt::{load_all_mpts, AccountRlp}; use crate::generation::TrieInputs; use crate::memory::segments::Segment; @@ -22,7 +23,7 @@ use crate::witness::memory::MemoryAddress; use crate::witness::operation::CONTEXT_SCALING_FACTOR; use crate::Node; -pub(crate) fn initialize_mpts( +pub(crate) fn initialize_mpts( interpreter: &mut Interpreter, trie_inputs: &TrieInputs, ) { @@ -62,8 +63,8 @@ fn test_account(code: &[u8]) -> AccountRlp { AccountRlp { nonce: U256::from(1111), balance: U256::from(2222), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak(code), + code_hash: hashout2u(hash_contract_bytecode(code.to_vec())), + code_length: code.len().into(), } } @@ -75,64 +76,65 @@ fn random_code() -> Vec { // Stolen from `tests/mpt/insert.rs` // Prepare the interpreter by inserting the account in the state trie. -fn prepare_interpreter( +fn prepare_interpreter( interpreter: &mut Interpreter, address: Address, account: &AccountRlp, ) -> Result<()> { - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - let mut state_trie: HashedPartialTrie = Default::default(); + let smt_insert_state = KERNEL.global_labels["smt_insert_state"]; + let smt_hash_state = KERNEL.global_labels["smt_hash_state"]; + let mut state_smt = Smt::::default(); let trie_inputs = Default::default(); initialize_mpts(interpreter, &trie_inputs); - let k = nibbles_64(U256::from_big_endian( - keccak(address.to_fixed_bytes()).as_bytes(), - )); - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; + // Next, execute smt_insert_state. let trie_data = interpreter.get_trie_data_mut(); if trie_data.is_empty() { // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. // Since we don't explicitly set it to 0, we need to do so here. trie_data.push(Some(0.into())); + trie_data.push(Some(0.into())); } - let value_ptr = trie_data.len(); - trie_data.push(Some(account.nonce)); - trie_data.push(Some(account.balance)); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(Some(H256::zero().into_uint())); - trie_data.push(Some(account.code_hash.into_uint())); let trie_data_len = trie_data.len().into(); interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(value_ptr.into()) - .expect("The stack should not overflow"); // value_ptr - interpreter - .push(k.try_into().unwrap()) - .expect("The stack should not overflow"); // key + for (key, value) in [ + (key_balance(address), account.balance), + (key_nonce(address), account.nonce), + (key_code(address), account.code_hash), + (key_code_length(address), account.code_length), + ] { + if value.is_zero() { + continue; + } + interpreter.generation_state.registers.program_counter = smt_insert_state; + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(value) + .expect("The stack should not overflow"); // value_ptr + let keyu = key2u(key); + interpreter + .push(keyu) + .expect("The stack should not overflow"); // key - interpreter.run()?; - assert_eq!( - interpreter.stack().len(), - 0, - "Expected empty stack after insert, found {:?}", - interpreter.stack() - ); + interpreter.run()?; + assert_eq!( + interpreter.stack().len(), + 0, + "Expected empty stack after insert, found {:?}", + interpreter.stack() + ); + } - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; + // Now, execute smt_hash_state. + interpreter.generation_state.registers.program_counter = smt_hash_state; interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(1.into()) // Initial length of the trie data segment, unused. + .push(2.into()) // Initial length of the trie data segment, unused. .expect("The stack should not overflow"); interpreter.run()?; @@ -142,10 +144,10 @@ fn prepare_interpreter( "Expected 2 items on stack after hashing, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[1]); + let hash = interpreter.stack()[1]; - state_trie.insert(k, rlp::encode(account).to_vec())?; - let expected_state_trie_hash = state_trie.hash(); + set_account(&mut state_smt, address, account, &HashMap::new()); + let expected_state_trie_hash = hashout2u(state_smt.root); assert_eq!(hash, expected_state_trie_hash); Ok(()) @@ -174,8 +176,10 @@ fn test_extcodesize() -> Result<()> { interpreter .push(U256::from_big_endian(address.as_bytes())) .expect("The stack should not overflow"); - interpreter.generation_state.inputs.contract_code = - HashMap::from([(keccak(&code), code.clone())]); + interpreter.generation_state.inputs.contract_code = HashMap::from([( + hashout2u(hash_contract_bytecode(code.clone())), + code.clone(), + )]); interpreter.run()?; assert_eq!(interpreter.stack(), vec![code.len().into()]); @@ -245,8 +249,10 @@ fn test_extcodecopy() -> Result<()> { interpreter .push((0xDEADBEEFu64 + (1 << 32)).into()) .expect("The stack should not overflow"); // kexit_info - interpreter.generation_state.inputs.contract_code = - HashMap::from([(keccak(&code), code.clone())]); + interpreter.generation_state.inputs.contract_code = HashMap::from([( + hashout2u(hash_contract_bytecode(code.clone())), + code.clone(), + )]); interpreter.run()?; assert!(interpreter.stack().is_empty()); @@ -267,7 +273,7 @@ fn test_extcodecopy() -> Result<()> { /// Prepare the interpreter for storage tests by inserting all necessary /// accounts in the state trie, adding the code we want to context 1 and /// switching the context. -fn prepare_interpreter_all_accounts( +fn prepare_interpreter_all_accounts( interpreter: &mut Interpreter, trie_inputs: TrieInputs, addr: [u8; 20], @@ -308,12 +314,8 @@ fn sstore() -> Result<()> { // We take the same `to` account as in add11_yml. let addr = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - let addr_hashed = keccak(addr); - - let addr_nibbles = Nibbles::from_bytes_be(addr_hashed.as_bytes()).unwrap(); - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00]; - let code_hash = keccak(code); + let code_hash = hash_bytecode_u256(code.to_vec()); let account_before = AccountRlp { balance: 0x0de0b6b3a7640000u64.into(), @@ -321,15 +323,18 @@ fn sstore() -> Result<()> { ..AccountRlp::default() }; - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - - state_trie_before.insert(addr_nibbles, rlp::encode(&account_before).to_vec())?; + let mut state_smt_before = Smt::::default(); + set_account( + &mut state_smt_before, + H160(addr), + &account_before, + &HashMap::new(), + ); let trie_inputs = TrieInputs { - state_trie: state_trie_before.clone(), + state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![(addr_hashed, Node::Empty.into())], }; let initial_stack = vec![]; @@ -353,21 +358,9 @@ fn sstore() -> Result<()> { interpreter.pop().expect("Stack should not be empty"); interpreter.pop().expect("Stack should not be empty"); - // The code should have added an element to the storage of `to_account`. We run - // `mpt_hash_state_trie` to check that. - let account_after = AccountRlp { - balance: 0x0de0b6b3a7640000u64.into(), - code_hash, - storage_root: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_h256_be(keccak([0u8; 32])), - value: vec![2], - }) - .hash(), - ..AccountRlp::default() - }; - // Now, execute mpt_hash_state_trie. - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; + // Now, execute smt_hash_state. + let smt_hash_state = KERNEL.global_labels["smt_hash_state"]; + interpreter.generation_state.registers.program_counter = smt_hash_state; interpreter.set_is_kernel(true); interpreter.set_context(0); interpreter @@ -385,12 +378,17 @@ fn sstore() -> Result<()> { interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[1]); + let hash = interpreter.stack()[1]; - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert(addr_nibbles, rlp::encode(&account_after).to_vec())?; + let mut expected_state_smt_after = Smt::::default(); + set_account( + &mut expected_state_smt_after, + H160(addr), + &account_before, + &[(0.into(), 2.into())].into(), + ); - let expected_state_trie_hash = expected_state_trie_after.hash(); + let expected_state_trie_hash = hashout2u(expected_state_smt_after.root); assert_eq!(hash, expected_state_trie_hash); Ok(()) } @@ -401,17 +399,13 @@ fn sload() -> Result<()> { // We take the same `to` account as in add11_yml. let addr = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - let addr_hashed = keccak(addr); - - let addr_nibbles = Nibbles::from_bytes_be(addr_hashed.as_bytes()).unwrap(); - // This code is similar to the one in add11_yml's contract, but we pop the added // value and carry out an SLOAD instead of an SSTORE. We also add a PUSH at // the end. let code = [ 0x60, 0x01, 0x60, 0x01, 0x01, 0x50, 0x60, 0x00, 0x54, 0x60, 0x03, 0x00, ]; - let code_hash = keccak(code); + let code_hash = hash_bytecode_u256(code.to_vec()); let account_before = AccountRlp { balance: 0x0de0b6b3a7640000u64.into(), @@ -419,15 +413,18 @@ fn sload() -> Result<()> { ..AccountRlp::default() }; - let mut state_trie_before = HashedPartialTrie::from(Node::Empty); - - state_trie_before.insert(addr_nibbles, rlp::encode(&account_before).to_vec())?; + let mut state_smt_before = Smt::::default(); + set_account( + &mut state_smt_before, + H160(addr), + &account_before, + &HashMap::new(), + ); let trie_inputs = TrieInputs { - state_trie: state_trie_before.clone(), + state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries: vec![(addr_hashed, Node::Empty.into())], }; let initial_stack = vec![]; @@ -464,17 +461,17 @@ fn sload() -> Result<()> { interpreter .pop() .expect("The stack length should not be empty."); - // Now, execute mpt_hash_state_trie. We check that the state trie has not - // changed. - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; + + // Now, execute smt_hash_state. + let smt_hash_state = KERNEL.global_labels["smt_hash_state"]; + interpreter.generation_state.registers.program_counter = smt_hash_state; interpreter.set_is_kernel(true); interpreter.set_context(0); interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow."); interpreter - .push(1.into()) // Initial length of the trie data segment, unused. + .push(2.into()) // Initial length of the trie data segment, unused. .expect("The stack should not overflow."); interpreter.run()?; @@ -486,6 +483,7 @@ fn sload() -> Result<()> { ); let trie_data_segment_len = interpreter.stack()[0]; + dbg!(interpreter.get_memory_segment(Segment::TrieData)); assert_eq!( trie_data_segment_len, interpreter @@ -494,9 +492,24 @@ fn sload() -> Result<()> { .into() ); - let hash = H256::from_uint(&interpreter.stack()[1]); + let hash = interpreter.stack()[1]; - let expected_state_trie_hash = state_trie_before.hash(); + let expected_state_trie_hash = hashout2u(state_smt_before.root); assert_eq!(hash, expected_state_trie_hash); Ok(()) } + +pub(crate) fn set_account( + smt: &mut Smt, + addr: Address, + account: &AccountRlp, + storage: &HashMap, +) { + smt.set(key_balance(addr), account.balance); + smt.set(key_nonce(addr), account.nonce); + smt.set(key_code(addr), account.code_hash); + smt.set(key_code_length(addr), account.code_length); + for (&k, &v) in storage { + smt.set(key_storage(addr, k), v); + } +} diff --git a/evm_arithmetization/src/cpu/kernel/tests/add11.rs b/evm_arithmetization/src/cpu/kernel/tests/add11.rs index ae5ac3871..986724148 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/add11.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/add11.rs @@ -1,44 +1,41 @@ use std::collections::HashMap; use std::str::FromStr; -use ethereum_types::{Address, BigEndianHash, H256}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use hex_literal::hex; -use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, Node, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField as F; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::utils::hashout2u; +use super::account_code::set_account; use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::interpreter::Interpreter; use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use crate::generation::TrieInputs; use crate::proof::{BlockHashes, BlockMetadata, TrieRoots}; use crate::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, - preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, - GLOBAL_EXIT_ROOT_ACCOUNT, + compute_beacon_roots_account_storage, init_logger, preinitialized_state, + preinitialized_state_with_updated_storage, set_beacon_roots_account, + set_global_exit_root_account, }; use crate::GenerationInputs; #[test] fn test_add11_yml() { + init_logger(); + let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00]; - let code_hash = keccak(code); + let code_hash = hash_bytecode_u256(code.to_vec()); let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); contract_code.insert(code_hash, code.to_vec()); let beneficiary_account_before = AccountRlp { @@ -55,35 +52,34 @@ fn test_add11_yml() { ..AccountRlp::default() }; - let (mut state_trie_before, mut storage_tries) = - preinitialized_state_and_storage_tries().unwrap(); - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before - .insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ) - .unwrap(); - state_trie_before - .insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()) - .unwrap(); - state_trie_before - .insert(to_nibbles, rlp::encode(&to_account_before).to_vec()) - .unwrap(); - - storage_tries.push((to_hashed, Node::Empty.into())); + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(beneficiary), + &beneficiary_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries, }; let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); - let gas_used = 0xa868u64.into(); - let block_metadata = BlockMetadata { block_beneficiary: Address::from(beneficiary), block_timestamp: 0x03e8.into(), @@ -93,11 +89,12 @@ fn test_add11_yml() { block_gaslimit: 0xff112233u32.into(), block_chain_id: 1.into(), block_base_fee: 0xa.into(), - block_gas_used: gas_used, + block_gas_used: 0xa868u64.into(), ..Default::default() }; - let expected_state_trie_after = { + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let beneficiary_account_after = AccountRlp { nonce: 1.into(), ..AccountRlp::default() @@ -110,53 +107,41 @@ fn test_add11_yml() { let to_account_after = AccountRlp { balance: 0xde0b6b3a76586a0u64.into(), code_hash, - // Storage map: { 0 => 2 } - storage_root: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_h256_be(keccak([0u8; 32])), - value: vec![2], - }) - .hash(), ..AccountRlp::default() }; - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, + + let beacon_roots_storage = compute_beacon_roots_account_storage( block_metadata.block_timestamp, block_metadata.parent_beacon_block_root, - ) - .unwrap(); - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after - .insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ) - .unwrap(); - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()) - .unwrap(); - expected_state_trie_after - .insert(to_nibbles, rlp::encode(&to_account_after).to_vec()) - .unwrap(); - expected_state_trie_after - .insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - ) - .unwrap(); - expected_state_trie_after - .insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - ) - .unwrap(); - expected_state_trie_after + ); + set_beacon_roots_account(&mut smt, &beacon_roots_storage); + set_global_exit_root_account(&mut smt, &HashMap::new()); + + set_account( + &mut smt, + H160(beneficiary), + &beneficiary_account_after, + &HashMap::new(), + ); + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account( + &mut smt, + H160(to), + &to_account_after, + &HashMap::from([(U256::zero(), 2.into())]), // Storage map: { 0 => 2 } + ); + + smt }; + let receipt_0 = LegacyReceiptRlp { status: true, - cum_gas_used: gas_used, + cum_gas_used: 0xa868u64.into(), bloom: vec![0; 256].into(), logs: vec![], }; @@ -174,7 +159,7 @@ fn test_add11_yml() { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -185,23 +170,27 @@ fn test_add11_yml() { global_exit_roots: vec![], tries: tries_before, trie_roots_after, - contract_code: contract_code.clone(), + contract_code, block_metadata, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), txn_number_before: 0.into(), gas_used_before: 0.into(), - gas_used_after: gas_used, + gas_used_after: 0xa868u64.into(), block_hashes: BlockHashes { prev_hashes: vec![H256::default(); 256], cur_hash: H256::default(), }, }; - let initial_stack = vec![]; let initial_offset = KERNEL.global_labels["main"]; + let initial_stack = vec![]; let mut interpreter: Interpreter = Interpreter::new_with_generation_inputs(initial_offset, initial_stack, inputs); + let route_txn_label = KERNEL.global_labels["main"]; + // Switch context and initialize memory with the data we need for the tests. + interpreter.generation_state.registers.program_counter = route_txn_label; + interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); interpreter.set_is_kernel(true); interpreter.run().expect("Proving add11 failed."); } @@ -210,23 +199,16 @@ fn test_add11_yml() { fn test_add11_yml_with_exception() { // In this test, we make sure that the user code throws a stack underflow // exception. + let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x8e, 0x00]; - let code_hash = keccak(code); + let code_hash = hash_bytecode_u256(code.to_vec()); let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); contract_code.insert(code_hash, code.to_vec()); let beneficiary_account_before = AccountRlp { @@ -243,32 +225,35 @@ fn test_add11_yml_with_exception() { ..AccountRlp::default() }; - let (mut state_trie_before, mut storage_tries) = - preinitialized_state_and_storage_tries().unwrap(); - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before - .insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - ) - .unwrap(); - state_trie_before - .insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec()) - .unwrap(); - state_trie_before - .insert(to_nibbles, rlp::encode(&to_account_before).to_vec()) - .unwrap(); - - storage_tries.push((to_hashed, Node::Empty.into())); + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(beneficiary), + &beneficiary_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries, }; - let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); + let txn = +hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16" +); let txn_gas_limit = 400_000; let gas_price = 10; @@ -286,53 +271,33 @@ fn test_add11_yml_with_exception() { }; // Here, since the transaction fails, it consumes its gas limit, and does - // nothing else. The beacon roots contract is still updated prior transaction - // execution. - let expected_state_trie_after = { + // nothing else. + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let beneficiary_account_after = beneficiary_account_before; + let to_account_after = to_account_before; // This is the only account that changes: the nonce and the balance are updated. let sender_account_after = AccountRlp { balance: sender_account_before.balance - txn_gas_limit * gas_price, nonce: 1.into(), ..AccountRlp::default() }; - let to_account_after = to_account_before; - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - ) - .unwrap(); - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); - - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after - .insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - ) - .unwrap(); - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec()) - .unwrap(); - expected_state_trie_after - .insert(to_nibbles, rlp::encode(&to_account_after).to_vec()) - .unwrap(); - expected_state_trie_after - .insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - ) - .unwrap(); - expected_state_trie_after - .insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - ) - .unwrap(); - expected_state_trie_after + set_account( + &mut smt, + H160(beneficiary), + &beneficiary_account_after, + &HashMap::new(), + ); + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account(&mut smt, H160(to), &to_account_after, &HashMap::new()); + + smt }; let receipt_0 = LegacyReceiptRlp { @@ -355,7 +320,7 @@ fn test_add11_yml_with_exception() { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -378,11 +343,15 @@ fn test_add11_yml_with_exception() { }, }; - let initial_stack = vec![]; let initial_offset = KERNEL.global_labels["main"]; + let initial_stack = vec![]; let mut interpreter: Interpreter = Interpreter::new_with_generation_inputs(initial_offset, initial_stack, inputs); + let route_txn_label = KERNEL.global_labels["main"]; + // Switch context and initialize memory with the data we need for the tests. + interpreter.generation_state.registers.program_counter = route_txn_label; + interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); interpreter.set_is_kernel(true); interpreter .run() diff --git a/evm_arithmetization/src/cpu/kernel/tests/balance.rs b/evm_arithmetization/src/cpu/kernel/tests/balance.rs index 2b8f8c241..0180486c3 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/balance.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/balance.rs @@ -1,74 +1,86 @@ +use std::collections::HashMap; + use anyhow::Result; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use keccak_hash::keccak; -use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; +use ethereum_types::{Address, U256}; use plonky2::field::goldilocks_field::GoldilocksField as F; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use rand::{thread_rng, Rng}; +use smt_trie::db::MemoryDb; +use smt_trie::keys::{key_balance, key_code, key_code_length, key_nonce}; +use smt_trie::smt::Smt; +use smt_trie::utils::{hashout2u, key2u}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; -use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::nibbles_64; +use crate::cpu::kernel::tests::account_code::{initialize_mpts, set_account}; use crate::generation::mpt::AccountRlp; -use crate::Node; // Test account with a given code hash. fn test_account(balance: U256) -> AccountRlp { AccountRlp { nonce: U256::from(1111), balance, - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: H256::from_uint(&U256::from(8888)), + code_hash: U256::from(8888), + code_length: 42.into(), // arbitrary } } // Stolen from `tests/mpt/insert.rs` // Prepare the interpreter by inserting the account in the state trie. -fn prepare_interpreter( +fn prepare_interpreter( interpreter: &mut Interpreter, address: Address, account: &AccountRlp, ) -> Result<()> { - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; - let mut state_trie: HashedPartialTrie = Default::default(); + let smt_insert_state = KERNEL.global_labels["smt_insert_state"]; + let smt_hash_state = KERNEL.global_labels["smt_hash_state"]; + let mut state_smt = Smt::::default(); let trie_inputs = Default::default(); initialize_mpts(interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); - let k = nibbles_64(U256::from_big_endian( - keccak(address.to_fixed_bytes()).as_bytes(), - )); - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; + // Next, execute smt_insert_state. + interpreter.generation_state.registers.program_counter = smt_insert_state; let trie_data = interpreter.get_trie_data_mut(); if trie_data.is_empty() { // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. // Since we don't explicitly set it to 0, we need to do so here. trie_data.push(Some(0.into())); + trie_data.push(Some(0.into())); } - let value_ptr = trie_data.len(); - trie_data.push(Some(account.nonce)); - trie_data.push(Some(account.balance)); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(Some(H256::zero().into_uint())); - trie_data.push(Some(account.code_hash.into_uint())); let trie_data_len = trie_data.len().into(); interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); - interpreter - .push(0xDEADBEEFu32.into()) - .expect("The stack should not overflow"); - interpreter - .push(value_ptr.into()) - .expect("The stack should not overflow"); // value_ptr - interpreter - .push(k.try_into().unwrap()) - .expect("The stack should not overflow"); // key + for (key, value) in [ + (key_balance(address), account.balance), + (key_nonce(address), account.nonce), + (key_code(address), account.code_hash), + (key_code_length(address), account.code_length), + ] { + if value.is_zero() { + continue; + } + interpreter.generation_state.registers.program_counter = smt_insert_state; + interpreter + .push(0xDEADBEEFu32.into()) + .expect("The stack should not overflow"); + interpreter + .push(value) + .expect("The stack should not overflow"); // value_ptr + let keyu = key2u(key); + interpreter + .push(keyu) + .expect("The stack should not overflow"); // key + + interpreter.run()?; + assert_eq!( + interpreter.stack().len(), + 0, + "Expected empty stack after insert, found {:?}", + interpreter.stack() + ); + } interpreter.run()?; assert_eq!( @@ -78,13 +90,13 @@ fn prepare_interpreter( interpreter.stack() ); - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; + // Now, execute smt_hash_state. + interpreter.generation_state.registers.program_counter = smt_hash_state; interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(1.into()) // Initial trie data segment size, unused. + .push(2.into()) // Initial trie data segment size, unused. .expect("The stack should not overflow"); interpreter.run()?; @@ -94,10 +106,10 @@ fn prepare_interpreter( "Expected 2 items on stack after hashing, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[1]); + let hash = interpreter.stack()[1]; - state_trie.insert(k, rlp::encode(account).to_vec())?; - let expected_state_trie_hash = state_trie.hash(); + set_account(&mut state_smt, address, account, &HashMap::new()); + let expected_state_trie_hash = hashout2u(state_smt.root); assert_eq!(hash, expected_state_trie_hash); Ok(()) diff --git a/evm_arithmetization/src/cpu/kernel/tests/mod.rs b/evm_arithmetization/src/cpu/kernel/tests/mod.rs index c44eb8454..4257af7ef 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mod.rs @@ -30,7 +30,7 @@ use std::{ use anyhow::Result; use ethereum_types::U256; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use super::{ aggregator::KERNEL, @@ -55,7 +55,7 @@ pub(crate) fn u256ify<'a>(hexes: impl IntoIterator) -> Result, _>>()?) } -pub(crate) fn run_interpreter( +pub(crate) fn run_interpreter( initial_offset: usize, initial_stack: Vec, ) -> anyhow::Result> { @@ -72,7 +72,7 @@ pub(crate) struct InterpreterMemoryInitialization { pub memory: Vec<(usize, Vec)>, } -pub(crate) fn run_interpreter_with_memory( +pub(crate) fn run_interpreter_with_memory( memory_init: InterpreterMemoryInitialization, ) -> anyhow::Result> { let label = KERNEL.global_labels[&memory_init.label]; @@ -91,7 +91,7 @@ pub(crate) fn run_interpreter_with_memory( Ok(interpreter) } -impl Interpreter { +impl Interpreter { pub(crate) fn get_txn_field(&self, field: NormalizedTxnField) -> U256 { // These fields are already scaled by their respective segment. self.generation_state.memory.contexts[0].segments[Segment::TxnFields.unscale()] diff --git a/evm_arithmetization/src/cpu/kernel/tests/mpt/delete.rs b/evm_arithmetization/src/cpu/kernel/tests/mpt/delete.rs index 15a3a36cd..3fd7f6288 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mpt/delete.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mpt/delete.rs @@ -1,9 +1,13 @@ use anyhow::Result; -use ethereum_types::{BigEndianHash, H256}; +use ethereum_types::{BigEndianHash, H160, H256, U256, U512}; use mpt_trie::nibbles::{Nibbles, NibblesIntern}; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::random; +use rand::{random, thread_rng, Rng}; +use smt_trie::db::MemoryDb; +use smt_trie::keys::key_balance; +use smt_trie::smt::{Key, Smt}; +use smt_trie::utils::{hashout2u, key2u}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -15,89 +19,51 @@ use crate::generation::TrieInputs; use crate::Node; #[test] -fn mpt_delete_empty() -> Result<()> { - test_state_trie(Default::default(), nibbles_64(0xABC), test_account_2()) +fn smt_delete_empty() -> Result<()> { + test_state_trie( + Smt::::default(), + key_balance(H160(random())), + U256(random()), + ) } #[test] -fn mpt_delete_leaf_nonoverlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), +fn smt_delete_random() -> Result<()> { + const N: usize = 100; + let mut rng = thread_rng(); + for _iter in 0..N { + let mut state_smt = Smt::::default(); + let num_keys: usize = rng.gen_range(0..100); + for _ in 0..num_keys { + let key = key_balance(H160(rng.gen())); + let value = U256(rng.gen()); + state_smt.set(key, value); + } + let trie_inputs = TrieInputs { + state_smt: state_smt.serialize(), + transactions_trie: Default::default(), + receipts_trie: Default::default(), + }; + + let key = key_balance(H160(rng.gen())); + let value = U256(rng.gen()); + test_state_trie(state_smt, key, value)?; } - .into(); - test_state_trie(state_trie, nibbles_64(0x123), test_account_2()) -} - -#[test] -fn mpt_delete_leaf_overlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xADE), test_account_2()) -} - -#[test] -fn mpt_delete_branch_into_hash() -> Result<()> { - let hash = Node::Hash(H256::random()); - let state_trie = Node::Extension { - nibbles: nibbles_64(0xADF), - child: hash.into(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xADE), test_account_2()) -} - -#[test] -fn test_after_mpt_delete_extension_branch() -> Result<()> { - let hash = Node::Hash(H256::random()); - let branch = Node::Branch { - children: std::array::from_fn(|i| { - if i == 0 { - Node::Empty.into() - } else { - hash.clone().into() - } - }), - value: vec![], - }; - let nibbles = Nibbles::from_bytes_be(&random::<[u8; 5]>()).unwrap(); - let state_trie = Node::Extension { - nibbles, - child: branch.into(), - } - .into(); - let key = nibbles.merge_nibbles(&Nibbles { - packed: NibblesIntern::zero(), - count: 64 - nibbles.count, - }); - test_state_trie(state_trie, key, test_account_2()) + Ok(()) } /// Note: The account's storage_root is ignored, as we can't insert a new /// storage_root without the accompanying trie data. An empty trie's /// storage_root is used instead. -fn test_state_trie( - state_trie: HashedPartialTrie, - k: Nibbles, - mut account: AccountRlp, -) -> Result<()> { - assert_eq!(k.count, 64); - - // Ignore any storage_root; see documentation note. - account.storage_root = HashedPartialTrie::from(Node::Empty).hash(); - +fn test_state_trie(state_smt: Smt, k: Key, value: U256) -> Result<()> { let trie_inputs = TrieInputs { - state_trie: state_trie.clone(), + state_smt: state_smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_delete = KERNEL.global_labels["mpt_delete"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; + let smt_insert_state = KERNEL.global_labels["smt_insert_state"]; + let smt_delete = KERNEL.global_labels["smt_delete"]; + let smt_hash = KERNEL.global_labels["smt_hash"]; let initial_stack = vec![]; let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); @@ -105,32 +71,25 @@ fn test_state_trie( initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; + // Next, execute smt_insert_state. + interpreter.generation_state.registers.program_counter = smt_insert_state; let trie_data = interpreter.get_trie_data_mut(); if trie_data.is_empty() { // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. // Since we don't explicitly set it to 0, we need to do so here. trie_data.push(Some(0.into())); + trie_data.push(Some(0.into())); } - let value_ptr = trie_data.len(); - trie_data.push(Some(account.nonce)); - trie_data.push(Some(account.balance)); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(Some(H256::zero().into_uint())); - trie_data.push(Some(account.code_hash.into_uint())); - let trie_data_len = trie_data.len().into(); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); + let len = trie_data.len(); + interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, len.into()); interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(value_ptr.into()) - .expect("The stack should not overflow"); // value_ptr + .push(value) + .expect("The stack should not overflow"); interpreter - .push(k.try_into().unwrap()) + .push(key2u(k)) .expect("The stack should not overflow"); // key interpreter.run()?; assert_eq!( @@ -140,39 +99,37 @@ fn test_state_trie( interpreter.stack() ); - // Next, execute mpt_delete, deleting the account we just inserted. + // Next, execute smt_delete, deleting the account we just inserted. let state_trie_ptr = interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot); - interpreter.generation_state.registers.program_counter = mpt_delete; + interpreter.generation_state.registers.program_counter = smt_delete; interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(k.try_into().unwrap()) - .expect("The stack should not overflow"); - interpreter - .push(64.into()) + .push(key2u(k)) .expect("The stack should not overflow"); interpreter .push(state_trie_ptr) .expect("The stack should not overflow"); interpreter.run()?; let state_trie_ptr = interpreter.pop().expect("The stack should not be empty"); - interpreter.set_global_metadata_field(GlobalMetadata::StateTrieRoot, state_trie_ptr); - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; + // Now, execute smt_hash_state. + interpreter.generation_state.registers.program_counter = smt_hash; interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(1.into()) // Initial length of the trie data segment, unused. + .push(2.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); + interpreter + .push(state_trie_ptr) .expect("The stack should not overflow"); interpreter.run()?; - let state_trie_hash = - H256::from_uint(&interpreter.pop().expect("The stack should not be empty")); - let expected_state_trie_hash = state_trie.hash(); - assert_eq!(state_trie_hash, expected_state_trie_hash); + let state_smt_hash = interpreter.pop().expect("The stack should not be empty"); + let expected_state_smt_hash = hashout2u(state_smt.root); + assert_eq!(state_smt_hash, expected_state_smt_hash); Ok(()) } diff --git a/evm_arithmetization/src/cpu/kernel/tests/mpt/hash.rs b/evm_arithmetization/src/cpu/kernel/tests/mpt/hash.rs index 18e3ae1fe..c92995999 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mpt/hash.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mpt/hash.rs @@ -1,7 +1,12 @@ use anyhow::Result; -use ethereum_types::{BigEndianHash, H256}; +use ethereum_types::{BigEndianHash, H160, H256, U256}; use mpt_trie::partial_trie::PartialTrie; use plonky2::field::goldilocks_field::GoldilocksField as F; +use rand::{thread_rng, Rng}; +use smt_trie::db::MemoryDb; +use smt_trie::keys::key_balance; +use smt_trie::smt::{hash_serialize_u256, Smt}; +use smt_trie::utils::hashout2u; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -13,103 +18,55 @@ use crate::Node; // TODO: Test with short leaf. Might need to be a storage trie. #[test] -fn mpt_hash_empty() -> Result<()> { +fn smt_hash_empty() -> Result<()> { + let mut state_smt = Smt::::default(); let trie_inputs = TrieInputs { - state_trie: Default::default(), + state_smt: state_smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; test_state_trie(trie_inputs) } #[test] -fn mpt_hash_empty_branch() -> Result<()> { - let children = core::array::from_fn(|_| Node::Empty.into()); - let state_trie = Node::Branch { - children, - value: vec![], +fn smt_hash_random() -> Result<()> { + const N: usize = 100; + let mut rng = thread_rng(); + for _iter in 0..N { + let mut state_smt = Smt::::default(); + let num_keys: usize = rng.gen_range(0..100); + for _ in 0..num_keys { + let key = key_balance(H160(rng.gen())); + let value = U256(rng.gen()); + state_smt.set(key, value); + } + let trie_inputs = TrieInputs { + state_smt: state_smt.serialize(), + transactions_trie: Default::default(), + receipts_trie: Default::default(), + }; + + test_state_trie(trie_inputs)?; } - .into(); - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_hash() -> Result<()> { - let hash = H256::random(); - let trie_inputs = TrieInputs { - state_trie: Node::Hash(hash).into(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_leaf() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_1_rlp(), - } - .into(); - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - test_state_trie(trie_inputs) -} - -#[test] -fn mpt_hash_extension_to_leaf() -> Result<()> { - let state_trie = extension_to_leaf(test_account_1_rlp()); - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - test_state_trie(trie_inputs) + Ok(()) } -#[test] -fn mpt_hash_branch_to_leaf() -> Result<()> { - let leaf = Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_2_rlp(), - } - .into(); - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[3] = leaf; - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - - let trie_inputs = TrieInputs { - state_trie, - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - test_state_trie(trie_inputs) -} +// #[test] +// fn mpt_hash_hash() -> Result<()> { +// let hash = H256::random(); +// let trie_inputs = TrieInputs { +// state_trie: Node::Hash(hash).into(), +// transactions_trie: Default::default(), +// receipts_trie: Default::default(), +// storage_tries: vec![], +// }; +// +// test_state_trie(trie_inputs) +// } fn test_state_trie(trie_inputs: TrieInputs) -> Result<()> { - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; + let smt_hash_state = KERNEL.global_labels["smt_hash_state"]; let initial_stack = vec![]; let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); @@ -118,12 +75,12 @@ fn test_state_trie(trie_inputs: TrieInputs) -> Result<()> { assert_eq!(interpreter.stack(), vec![]); // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; + interpreter.generation_state.registers.program_counter = smt_hash_state; interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(1.into()) // Initial length of the trie data segment, unused. + .push(2.into()) // Initial length of the trie data segment, unused. .expect("The stack should not overflow"); interpreter.run()?; @@ -133,8 +90,8 @@ fn test_state_trie(trie_inputs: TrieInputs) -> Result<()> { "Expected 2 items on stack, found {:?}", interpreter.stack() ); - let hash = H256::from_uint(&interpreter.stack()[1]); - let expected_state_trie_hash = trie_inputs.state_trie.hash(); + let hash = interpreter.stack()[1]; + let expected_state_trie_hash = hash_serialize_u256(&trie_inputs.state_smt); assert_eq!(hash, expected_state_trie_hash); Ok(()) diff --git a/evm_arithmetization/src/cpu/kernel/tests/mpt/insert.rs b/evm_arithmetization/src/cpu/kernel/tests/mpt/insert.rs index d25138631..276201b96 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mpt/insert.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mpt/insert.rs @@ -1,8 +1,13 @@ use anyhow::Result; -use ethereum_types::{BigEndianHash, H256}; +use ethereum_types::{BigEndianHash, H160, H256, U256}; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField as F; +use rand::{random, thread_rng, Rng}; +use smt_trie::db::MemoryDb; +use smt_trie::keys::key_balance; +use smt_trie::smt::{Key, Smt}; +use smt_trie::utils::{hashout2u, key2u}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -16,163 +21,51 @@ use crate::generation::TrieInputs; use crate::Node; #[test] -fn mpt_insert_empty() -> Result<()> { - test_state_trie(Default::default(), nibbles_64(0xABC), test_account_2()) +fn smt_insert_empty() -> Result<()> { + test_state_trie( + Smt::::default(), + key_balance(H160(random())), + U256(random()), + ) } #[test] -fn mpt_insert_leaf_identical_keys() -> Result<()> { - let key = nibbles_64(0xABC); - let state_trie = Node::Leaf { - nibbles: key, - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, key, test_account_2()) -} - -#[test] -fn mpt_insert_leaf_nonoverlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0x123), test_account_2()) -} - -#[test] -fn mpt_insert_leaf_overlapping_keys() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: nibbles_64(0xABC), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xADE), test_account_2()) -} - -#[test] -#[ignore] // TODO: Not valid for state trie, all keys have same len. -fn mpt_insert_leaf_insert_key_extends_leaf_key() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xABCDE), test_account_2()) -} - -#[test] -#[ignore] // TODO: Not valid for state trie, all keys have same len. -fn mpt_insert_leaf_leaf_key_extends_insert_key() -> Result<()> { - let state_trie = Node::Leaf { - nibbles: 0xABCDE_u64.into(), - value: test_account_1_rlp(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xABC), test_account_2()) -} - -#[test] -fn mpt_insert_branch_replacing_empty_child() -> Result<()> { - let children = core::array::from_fn(|_| Node::Empty.into()); - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - - test_state_trie(state_trie, nibbles_64(0xABC), test_account_2()) -} - -#[test] -// TODO: Not a valid test because branches state trie cannot have branch values. -// We should change it to use a different trie. -#[ignore] -fn mpt_insert_extension_nonoverlapping_keys() -> Result<()> { - // Existing keys are 0xABC, 0xABCDEF; inserted key is 0x12345. - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[0xD] = Node::Leaf { - nibbles: 0xEF_u64.into(), - value: test_account_1_rlp(), - } - .into(); - let state_trie = Node::Extension { - nibbles: 0xABC_u64.into(), - child: Node::Branch { - children, - value: test_account_1_rlp(), +fn smt_insert_random() -> Result<()> { + const N: usize = 100; + let mut rng = thread_rng(); + for _iter in 0..N { + let mut state_smt = Smt::::default(); + let num_keys: usize = rng.gen_range(0..100); + for _ in 0..num_keys { + let key = key_balance(H160(rng.gen())); + let value = U256(rng.gen()); + state_smt.set(key, value); } - .into(), + let trie_inputs = TrieInputs { + state_smt: state_smt.serialize(), + transactions_trie: Default::default(), + receipts_trie: Default::default(), + }; + + let key = key_balance(H160(rng.gen())); + let value = U256(rng.gen()); + test_state_trie(state_smt, key, value)?; } - .into(); - test_state_trie(state_trie, nibbles_64(0x12345), test_account_2()) -} - -#[test] -// TODO: Not a valid test because branches state trie cannot have branch values. -// We should change it to use a different trie. -#[ignore] -fn mpt_insert_extension_insert_key_extends_node_key() -> Result<()> { - // Existing keys are 0xA, 0xABCD; inserted key is 0xABCDEF. - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[0xB] = Node::Leaf { - nibbles: 0xCD_u64.into(), - value: test_account_1_rlp(), - } - .into(); - let state_trie = Node::Extension { - nibbles: 0xA_u64.into(), - child: Node::Branch { - children, - value: test_account_1_rlp(), - } - .into(), - } - .into(); - test_state_trie(state_trie, nibbles_64(0xABCDEF), test_account_2()) -} - -#[test] -fn mpt_insert_branch_to_leaf_same_key() -> Result<()> { - let leaf = Node::Leaf { - nibbles: nibbles_count(0xBCD, 63), - value: test_account_1_rlp(), - } - .into(); - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[0] = leaf; - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - - test_state_trie(state_trie, nibbles_64(0xABCD), test_account_2()) + Ok(()) } /// Note: The account's storage_root is ignored, as we can't insert a new /// storage_root without the accompanying trie data. An empty trie's /// storage_root is used instead. -fn test_state_trie( - mut state_trie: HashedPartialTrie, - k: Nibbles, - mut account: AccountRlp, -) -> Result<()> { - assert_eq!(k.count, 64); - - // Ignore any storage_root; see documentation note. - account.storage_root = HashedPartialTrie::from(Node::Empty).hash(); - +fn test_state_trie(mut state_smt: Smt, k: Key, value: U256) -> Result<()> { let trie_inputs = TrieInputs { - state_trie: state_trie.clone(), + state_smt: state_smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; - let mpt_insert_state_trie = KERNEL.global_labels["mpt_insert_state_trie"]; - let mpt_hash_state_trie = KERNEL.global_labels["mpt_hash_state_trie"]; + let smt_insert_state = KERNEL.global_labels["smt_insert_state"]; + let smt_delete = KERNEL.global_labels["smt_delete"]; + let smt_hash = KERNEL.global_labels["smt_hash"]; let initial_stack = vec![]; let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); @@ -180,34 +73,26 @@ fn test_state_trie( initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); - // Next, execute mpt_insert_state_trie. - interpreter.generation_state.registers.program_counter = mpt_insert_state_trie; + // Next, execute smt_insert_state. + interpreter.generation_state.registers.program_counter = smt_insert_state; let trie_data = interpreter.get_trie_data_mut(); if trie_data.is_empty() { // In the assembly we skip over 0, knowing trie_data[0] = 0 by default. // Since we don't explicitly set it to 0, we need to do so here. trie_data.push(Some(0.into())); + trie_data.push(Some(0.into())); } - let value_ptr = trie_data.len(); - trie_data.push(Some(account.nonce)); - trie_data.push(Some(account.balance)); - // In memory, storage_root gets interpreted as a pointer to a storage trie, - // so we have to ensure the pointer is valid. It's easiest to set it to 0, - // which works as an empty node, since trie_data[0] = 0 = MPT_TYPE_EMPTY. - trie_data.push(Some(H256::zero().into_uint())); - trie_data.push(Some(account.code_hash.into_uint())); - let trie_data_len = trie_data.len().into(); - interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, trie_data_len); + let len = trie_data.len(); + interpreter.set_global_metadata_field(GlobalMetadata::TrieDataSize, len.into()); interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(value_ptr.into()) + .push(value) .expect("The stack should not overflow"); // value_ptr interpreter - .push(k.try_into().unwrap()) + .push(key2u(k)) .expect("The stack should not overflow"); // key - interpreter.run()?; assert_eq!( interpreter.stack().len(), @@ -216,27 +101,25 @@ fn test_state_trie( interpreter.stack() ); - // Now, execute mpt_hash_state_trie. - interpreter.generation_state.registers.program_counter = mpt_hash_state_trie; + let state_trie_ptr = interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot); + + // Now, execute smt_hash_state. + interpreter.generation_state.registers.program_counter = smt_hash; interpreter .push(0xDEADBEEFu32.into()) .expect("The stack should not overflow"); interpreter - .push(1.into()) // Initial length of the trie data segment, unused. + .push(2.into()) // Initial length of the trie data segment, unused. + .expect("The stack should not overflow"); + interpreter + .push(state_trie_ptr) // Initial length of the trie data segment, unused. .expect("The stack should not overflow"); interpreter.run()?; - assert_eq!( - interpreter.stack().len(), - 2, - "Expected 2 items on stack after hashing, found {:?}", - interpreter.stack() - ); - let hash = H256::from_uint(&interpreter.stack()[1]); - - state_trie.insert(k, rlp::encode(&account).to_vec())?; - let expected_state_trie_hash = state_trie.hash(); - assert_eq!(hash, expected_state_trie_hash); + let state_smt_hash = interpreter.pop().expect("The stack should not be empty"); + state_smt.set(k, value); + let expected_state_smt_hash = hashout2u(state_smt.root); + assert_eq!(state_smt_hash, expected_state_smt_hash); Ok(()) } diff --git a/evm_arithmetization/src/cpu/kernel/tests/mpt/load.rs b/evm_arithmetization/src/cpu/kernel/tests/mpt/load.rs index 9aa8a1f0b..5884eaf89 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mpt/load.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mpt/load.rs @@ -1,13 +1,19 @@ use std::str::FromStr; use anyhow::Result; -use ethereum_types::{BigEndianHash, H256, U256}; +use ethereum_types::{BigEndianHash, H160, H256, U256}; use hex_literal::hex; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::HashedPartialTrie; use plonky2::field::goldilocks_field::GoldilocksField as F; +use rand::{thread_rng, Rng}; +use smt_trie::db::MemoryDb; +use smt_trie::keys::key_balance; +use smt_trie::smt::Smt; +use smt_trie::utils::key2u; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; +use crate::cpu::kernel::constants::smt_type::PartialSmtType; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::tests::account_code::initialize_mpts; @@ -17,11 +23,11 @@ use crate::Node; #[test] fn load_all_mpts_empty() -> Result<()> { + let smt = Smt::::default(); let trie_inputs = TrieInputs { - state_trie: Default::default(), + state_smt: smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; let initial_stack = vec![]; @@ -30,11 +36,11 @@ fn load_all_mpts_empty() -> Result<()> { assert_eq!(interpreter.stack(), vec![]); // We need to have the first element in `TrieData` be 0. - assert_eq!(interpreter.get_trie_data(), vec![0.into()]); + assert_eq!(interpreter.get_trie_data(), vec![0.into(); 4]); assert_eq!( interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot), - 0.into() + 2.into() ); assert_eq!( interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), @@ -50,99 +56,14 @@ fn load_all_mpts_empty() -> Result<()> { #[test] fn load_all_mpts_leaf() -> Result<()> { + let mut state_smt = Smt::::default(); + let key = key_balance(H160(thread_rng().gen())); + let value = U256(thread_rng().gen()); + state_smt.set(key, value); let trie_inputs = TrieInputs { - state_trie: Node::Leaf { - nibbles: 0xABC_u64.into(), - value: test_account_1_rlp(), - } - .into(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let type_leaf = U256::from(PartialTrieType::Leaf as u32); - assert_eq!( - interpreter.get_trie_data(), - vec![ - 0.into(), - type_leaf, - 3.into(), - 0xABC.into(), - 5.into(), // value ptr - test_account_1().nonce, - test_account_1().balance, - 9.into(), // pointer to storage trie root - test_account_1().code_hash.into_uint(), - // These last two elements encode the storage trie, which is a hash node. - (PartialTrieType::Hash as u32).into(), - test_account_1().storage_root.into_uint(), - ] - ); - - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), - 0.into() - ); - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), - 0.into() - ); - - Ok(()) -} - -#[test] -fn load_all_mpts_hash() -> Result<()> { - let hash = H256::random(); - let trie_inputs = TrieInputs { - state_trie: Node::Hash(hash).into(), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let type_hash = U256::from(PartialTrieType::Hash as u32); - assert_eq!( - interpreter.get_trie_data(), - vec![0.into(), type_hash, hash.into_uint(),] - ); - - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), - 0.into() - ); - assert_eq!( - interpreter.get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), - 0.into() - ); - - Ok(()) -} - -#[test] -fn load_all_mpts_empty_branch() -> Result<()> { - let children = core::array::from_fn(|_| Node::Empty.into()); - let state_trie = Node::Branch { - children, - value: vec![], - } - .into(); - let trie_inputs = TrieInputs { - state_trie, + state_smt: state_smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; let initial_stack = vec![]; @@ -150,30 +71,10 @@ fn load_all_mpts_empty_branch() -> Result<()> { initialize_mpts(&mut interpreter, &trie_inputs); assert_eq!(interpreter.stack(), vec![]); - let type_branch = U256::from(PartialTrieType::Branch as u32); + let type_leaf = U256::from(PartialSmtType::Leaf as u32); assert_eq!( interpreter.get_trie_data(), - vec![ - 0.into(), // First address is unused, so that 0 can be treated as a null pointer. - type_branch, - 0.into(), // child 0 - 0.into(), // ... - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), - 0.into(), // child 16 - 0.into(), // value_ptr - ] + vec![0.into(), 0.into(), type_leaf, key2u(key), value,] ); assert_eq!( @@ -188,78 +89,173 @@ fn load_all_mpts_empty_branch() -> Result<()> { Ok(()) } -#[test] -fn load_all_mpts_ext_to_leaf() -> Result<()> { - let trie_inputs = TrieInputs { - state_trie: extension_to_leaf(test_account_1_rlp()), - transactions_trie: Default::default(), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let type_extension = U256::from(PartialTrieType::Extension as u32); - let type_leaf = U256::from(PartialTrieType::Leaf as u32); - assert_eq!( - interpreter.get_trie_data(), - vec![ - 0.into(), // First address is unused, so that 0 can be treated as a null pointer. - type_extension, - 3.into(), // 3 nibbles - 0xABC.into(), // key part - 5.into(), // Pointer to the leaf node immediately below. - type_leaf, - 3.into(), // 3 nibbles - 0xDEF.into(), // key part - 9.into(), // value pointer - test_account_1().nonce, - test_account_1().balance, - 13.into(), // pointer to storage trie root - test_account_1().code_hash.into_uint(), - // These last two elements encode the storage trie, which is a hash node. - (PartialTrieType::Hash as u32).into(), - test_account_1().storage_root.into_uint(), - ] - ); - - Ok(()) -} - -#[test] -fn load_mpt_txn_trie() -> Result<()> { - let txn = hex!("f860010a830186a094095e7baea6a6c7c4c2dfeb977efac326af552e89808025a04a223955b0bd3827e3740a9a427d0ea43beb5bafa44a0204bf0a3306c8219f7ba0502c32d78f233e9e7ce9f5df3b576556d5d49731e0678fd5a068cdf359557b5b").to_vec(); - - let trie_inputs = TrieInputs { - state_trie: Default::default(), - transactions_trie: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.clone(), - }), - receipts_trie: Default::default(), - storage_tries: vec![], - }; - - let initial_stack = vec![]; - let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); - initialize_mpts(&mut interpreter, &trie_inputs); - assert_eq!(interpreter.stack(), vec![]); - - let mut expected_trie_data = vec![ - 0.into(), - U256::from(PartialTrieType::Leaf as u32), - 2.into(), - 128.into(), // Nibble - 5.into(), // value_ptr - txn.len().into(), - ]; - expected_trie_data.extend(txn.into_iter().map(U256::from)); - let trie_data = interpreter.get_trie_data(); - - assert_eq!(trie_data, expected_trie_data); - - Ok(()) -} +// #[test] +// fn load_all_mpts_hash() -> Result<()> { +// let hash = H256::random(); +// let trie_inputs = TrieInputs { +// state_trie: Node::Hash(hash).into(), +// transactions_trie: Default::default(), +// receipts_trie: Default::default(), +// storage_tries: vec![], +// }; + +// let initial_stack = vec![]; +// let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, +// initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); +// assert_eq!(interpreter.stack(), vec![]); + +// let type_hash = U256::from(PartialTrieType::Hash as u32); +// assert_eq!( +// interpreter.get_trie_data(), +// vec![0.into(), type_hash, hash.into_uint(),] +// ); + +// assert_eq!( +// interpreter. +// get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), +// 0.into() +// ); +// assert_eq!( +// interpreter. +// get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), 0.into() +// ); + +// Ok(()) +// } + +// #[test] +// fn load_all_mpts_empty_branch() -> Result<()> { +// let children = core::array::from_fn(|_| Node::Empty.into()); +// let state_trie = Node::Branch { +// children, +// value: vec![], +// } +// .into(); +// let trie_inputs = TrieInputs { +// state_trie, +// transactions_trie: Default::default(), +// receipts_trie: Default::default(), +// storage_tries: vec![], +// }; + +// let initial_stack = vec![]; +// let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, +// initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); +// assert_eq!(interpreter.stack(), vec![]); + +// let type_branch = U256::from(PartialTrieType::Branch as u32); +// assert_eq!( +// interpreter.get_trie_data(), +// vec![ +// 0.into(), // First address is unused, so that 0 can be treated as +// a null pointer. type_branch, +// 0.into(), // child 0 +// 0.into(), // ... +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), +// 0.into(), // child 16 +// 0.into(), // value_ptr +// ] +// ); + +// assert_eq!( +// interpreter. +// get_global_metadata_field(GlobalMetadata::TransactionTrieRoot), +// 0.into() +// ); +// assert_eq!( +// interpreter. +// get_global_metadata_field(GlobalMetadata::ReceiptTrieRoot), 0.into() +// ); + +// Ok(()) +// } + +// #[test] +// fn load_all_mpts_ext_to_leaf() -> Result<()> { +// let trie_inputs = TrieInputs { +// state_trie: extension_to_leaf(test_account_1_rlp()), +// transactions_trie: Default::default(), +// receipts_trie: Default::default(), +// storage_tries: vec![], +// }; + +// let initial_stack = vec![]; +// let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, +// initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); +// assert_eq!(interpreter.stack(), vec![]); + +// let type_extension = U256::from(PartialTrieType::Extension as u32); +// let type_leaf = U256::from(PartialTrieType::Leaf as u32); +// assert_eq!( +// interpreter.get_trie_data(), +// vec![ +// 0.into(), // First address is unused, so that 0 can be treated as +// a null pointer. type_extension, +// 3.into(), // 3 nibbles +// 0xABC.into(), // key part +// 5.into(), // Pointer to the leaf node immediately below. +// type_leaf, +// 3.into(), // 3 nibbles +// 0xDEF.into(), // key part +// 9.into(), // value pointer +// test_account_1().nonce, +// test_account_1().balance, +// 13.into(), // pointer to storage trie root +// test_account_1().code_hash.into_uint(), +// // These last two elements encode the storage trie, which is a +// hash node. (PartialTrieType::Hash as u32).into(), +// test_account_1().storage_root.into_uint(), +// ] +// ); + +// Ok(()) +// } + +// #[test] +// fn load_mpt_txn_trie() -> Result<()> { +// let txn = +// hex!("f860010a830186a094095e7baea6a6c7c4c2dfeb977efac326af552e89808025a04a223955b0bd3827e3740a9a427d0ea43beb5bafa44a0204bf0a3306c8219f7ba0502c32d78f233e9e7ce9f5df3b576556d5d49731e0678fd5a068cdf359557b5b" +// ).to_vec(); + +// let trie_inputs = TrieInputs { +// state_trie: Default::default(), +// transactions_trie: HashedPartialTrie::from(Node::Leaf { +// nibbles: Nibbles::from_str("0x80").unwrap(), +// value: txn.clone(), +// }), +// receipts_trie: Default::default(), +// storage_tries: vec![], +// }; + +// let initial_stack = vec![]; +// let mut interpreter: Interpreter = Interpreter::new_with_kernel(0, +// initial_stack); initialize_mpts(&mut interpreter, &trie_inputs); +// assert_eq!(interpreter.stack(), vec![]); + +// let mut expected_trie_data = vec![ +// 0.into(), +// U256::from(PartialTrieType::Leaf as u32), +// 2.into(), +// 128.into(), // Nibble +// 5.into(), // value_ptr +// txn.len().into(), +// ]; +// expected_trie_data.extend(txn.into_iter().map(U256::from)); +// let trie_data = interpreter.get_trie_data(); + +// assert_eq!(trie_data, expected_trie_data); + +// Ok(()) +// } diff --git a/evm_arithmetization/src/cpu/kernel/tests/mpt/mod.rs b/evm_arithmetization/src/cpu/kernel/tests/mpt/mod.rs index 84f64bb7b..0ae03c864 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mpt/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mpt/mod.rs @@ -1,3 +1,6 @@ +// TODO: Remove after code refactoring type-1 / type-2 +#![allow(unused)] + use ethereum_types::{BigEndianHash, H256, U256}; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::HashedPartialTrie; @@ -32,8 +35,8 @@ pub(crate) fn test_account_1() -> AccountRlp { AccountRlp { nonce: U256::from(1111), balance: U256::from(2222), - storage_root: H256::from_uint(&U256::from(3333)), - code_hash: H256::from_uint(&U256::from(4444)), + code_length: U256::from(3333), + code_hash: U256::from(4444), } } @@ -45,8 +48,8 @@ pub(crate) fn test_account_2() -> AccountRlp { AccountRlp { nonce: U256::from(5555), balance: U256::from(6666), - storage_root: H256::from_uint(&U256::from(7777)), - code_hash: H256::from_uint(&U256::from(8888)), + code_length: U256::from(7777), + code_hash: U256::from(8888), } } diff --git a/evm_arithmetization/src/cpu/kernel/tests/mpt/read.rs b/evm_arithmetization/src/cpu/kernel/tests/mpt/read.rs index 9b669a21c..aae68432e 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mpt/read.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mpt/read.rs @@ -1,24 +1,30 @@ use anyhow::Result; -use ethereum_types::BigEndianHash; +use ethereum_types::{H160, U256}; use plonky2::field::goldilocks_field::GoldilocksField as F; +use rand::{thread_rng, Rng}; +use smt_trie::db::MemoryDb; +use smt_trie::keys::key_balance; +use smt_trie::smt::Smt; +use smt_trie::utils::key2u; use crate::cpu::kernel::aggregator::KERNEL; -use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::tests::account_code::initialize_mpts; -use crate::cpu::kernel::tests::mpt::{extension_to_leaf, test_account_1, test_account_1_rlp}; use crate::generation::TrieInputs; #[test] -fn mpt_read() -> Result<()> { +fn smt_read() -> Result<()> { + let mut state_smt = Smt::::default(); + let key = key_balance(H160(thread_rng().gen())); + let value = U256(thread_rng().gen()); + state_smt.set(key, value); let trie_inputs = TrieInputs { - state_trie: extension_to_leaf(test_account_1_rlp()), + state_smt: state_smt.serialize(), transactions_trie: Default::default(), receipts_trie: Default::default(), - storage_tries: vec![], }; - let mpt_read = KERNEL.global_labels["mpt_read"]; + let smt_read_state = KERNEL.global_labels["smt_read_state"]; let initial_stack = vec![]; let mut interpreter: Interpreter = Interpreter::new(0, initial_stack); @@ -26,29 +32,19 @@ fn mpt_read() -> Result<()> { assert_eq!(interpreter.stack(), vec![]); // Now, execute mpt_read on the state trie. - interpreter.generation_state.registers.program_counter = mpt_read; + interpreter.generation_state.registers.program_counter = smt_read_state; interpreter .push(0xdeadbeefu32.into()) .expect("The stack should not overflow"); interpreter - .push(0xABCDEFu64.into()) - .expect("The stack should not overflow"); - interpreter - .push(6.into()) - .expect("The stack should not overflow"); - interpreter - .push(interpreter.get_global_metadata_field(GlobalMetadata::StateTrieRoot)) + .push(key2u(key)) .expect("The stack should not overflow"); interpreter.run()?; assert_eq!(interpreter.stack().len(), 1); let result_ptr = interpreter.stack()[0].as_usize(); - let result = &interpreter.get_trie_data()[result_ptr..][..4]; - assert_eq!(result[0], test_account_1().nonce); - assert_eq!(result[1], test_account_1().balance); - // result[2] is the storage root pointer. We won't check that it matches a - // particular address, since that seems like over-specifying. - assert_eq!(result[3], test_account_1().code_hash.into_uint()); + let result = interpreter.get_trie_data()[result_ptr]; + assert_eq!(result, value); Ok(()) } diff --git a/evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs b/evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs index 774ac51d7..9b14e963b 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/transient_storage.rs @@ -2,7 +2,7 @@ use anyhow::Result; use ethereum_types::U256; use once_cell::sync::Lazy; use plonky2::field::goldilocks_field::GoldilocksField as F; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use crate::cpu::kernel::aggregator::{ combined_kernel_from_files, KERNEL_FILES, NUMBER_KERNEL_FILES, @@ -16,7 +16,7 @@ use crate::memory::segments::Segment; use crate::witness::memory::MemoryAddress; use crate::GenerationInputs; -fn initialize_interpreter(interpreter: &mut Interpreter, gas_limit: U256) { +fn initialize_interpreter(interpreter: &mut Interpreter, gas_limit: U256) { let gas_limit_address = MemoryAddress { context: 0, segment: Segment::ContextMetadata.unscale(), diff --git a/evm_arithmetization/src/cpu/stack.rs b/evm_arithmetization/src/cpu/stack.rs index cd7ca703d..a14e0ff55 100644 --- a/evm_arithmetization/src/cpu/stack.rs +++ b/evm_arithmetization/src/cpu/stack.rs @@ -29,6 +29,7 @@ pub(crate) const MIGHT_OVERFLOW: OpsColumnsView = OpsColumnsView { not_pop: false, shift: false, jumpdest_keccak_general: false, + poseidon: false, push_prover_input: true, // PROVER_INPUT doesn't require the check, but PUSH does. jumps: false, pc_push0: true, @@ -101,6 +102,20 @@ pub(crate) const JUMPDEST_OP: StackBehavior = StackBehavior { disable_other_channels: true, }; +/// Stack behavior for POSEIDON. +pub(crate) const POSEIDON_OP: StackBehavior = StackBehavior { + num_pops: 3, + pushes: true, + disable_other_channels: true, +}; + +/// Stack behavior for POSEIDON_GENERAL. +pub(crate) const POSEIDON_GENERAL_OP: StackBehavior = StackBehavior { + num_pops: 2, + pushes: true, + disable_other_channels: true, +}; + // AUDITORS: If the value below is `None`, then the operation must be manually // checked to ensure that every general-purpose memory channel is either // disabled or has its read flag and address properly constrained. The same @@ -120,6 +135,7 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsCol disable_other_channels: false, }), jumpdest_keccak_general: None, + poseidon: None, push_prover_input: Some(StackBehavior { num_pops: 0, pushes: true, @@ -330,6 +346,20 @@ pub(crate) fn eval_packed( yield_constr, ); + // Constrain stack for POSEIDON. + let poseidon_filter = lv.op.poseidon * (P::ONES - lv.opcode_bits[0]); + eval_packed_one(lv, nv, poseidon_filter, POSEIDON_OP, yield_constr); + + // Constrain stack for POSEIDON_GENERAL. + let poseidon_general_filter = lv.op.poseidon * lv.opcode_bits[0]; + eval_packed_one( + lv, + nv, + poseidon_general_filter, + POSEIDON_GENERAL_OP, + yield_constr, + ); + // Stack constraints for POP. // The only constraints POP has are stack constraints. // Since POP and NOT are combined into one flag and they have @@ -650,6 +680,22 @@ pub(crate) fn eval_ext_circuit, const D: usize>( yield_constr, ); + // Constrain stack for POSEIDON. + let mut poseidon_filter = builder.sub_extension(one, lv.opcode_bits[0]); + poseidon_filter = builder.mul_extension(lv.op.poseidon, poseidon_filter); + eval_ext_circuit_one(builder, lv, nv, poseidon_filter, POSEIDON_OP, yield_constr); + + // Constrain stack for POSEIDON_GENERAL. + let poseidon_general_filter = builder.mul_extension(lv.op.poseidon, lv.opcode_bits[0]); + eval_ext_circuit_one( + builder, + lv, + nv, + poseidon_general_filter, + POSEIDON_GENERAL_OP, + yield_constr, + ); + // Stack constraints for POP. // The only constraints POP has are stack constraints. // Since POP and NOT are combined into one flag and they have diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 3fa6e208f..acbd56c4f 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -554,6 +554,13 @@ where &all_stark.cross_table_lookups, stark_config, ); + let poseidon = RecursiveCircuitsForTable::new( + Table::Poseidon, + &all_stark.poseidon_stark, + degree_bits_ranges[*Table::Poseidon].clone(), + &all_stark.cross_table_lookups, + stark_config, + ); let by_table = [ arithmetic, @@ -563,6 +570,7 @@ where keccak_sponge, logic, memory, + poseidon, ]; let root = Self::create_root_circuit(&by_table, stark_config); let aggregation = Self::create_aggregation_circuit(&root); diff --git a/evm_arithmetization/src/generation/mod.rs b/evm_arithmetization/src/generation/mod.rs index 5940e8be3..9a73213a9 100644 --- a/evm_arithmetization/src/generation/mod.rs +++ b/evm_arithmetization/src/generation/mod.rs @@ -6,7 +6,6 @@ use log::log_enabled; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; -use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -34,6 +33,8 @@ pub(crate) mod rlp; pub(crate) mod state; mod trie_extractor; +use smt_trie::smt::hash_serialize_u256; + use self::state::State; use crate::witness::util::mem_write_log; @@ -70,7 +71,7 @@ pub struct GenerationInputs { /// Mapping between smart contract code hashes and the contract byte code. /// All account smart contracts that are invoked will have an entry present. - pub contract_code: HashMap>, + pub contract_code: HashMap>, /// Information contained in the block header. pub block_metadata: BlockMetadata, @@ -80,12 +81,12 @@ pub struct GenerationInputs { pub block_hashes: BlockHashes, } -#[derive(Clone, Debug, Deserialize, Serialize, Default)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct TrieInputs { - /// A partial version of the state trie prior to these transactions. It - /// should include all nodes that will be accessed by these - /// transactions. - pub state_trie: HashedPartialTrie, + /// A serialized partial version of the state SMT prior to these + /// transactions. It should include all nodes that will be accessed by + /// these transactions. + pub state_smt: Vec, /// A partial version of the transaction trie prior to these transactions. /// It should include all nodes that will be accessed by these @@ -96,11 +97,18 @@ pub struct TrieInputs { /// should include all nodes that will be accessed by these /// transactions. pub receipts_trie: HashedPartialTrie, +} - /// A partial version of each storage trie prior to these transactions. It - /// should include all storage tries, and nodes therein, that will be - /// accessed by these transactions. - pub storage_tries: Vec<(H256, HashedPartialTrie)>, +impl Default for TrieInputs { + fn default() -> Self { + Self { + // First 2 zeros are for the default empty node. + // The next 2 are for the current empty state trie root. + state_smt: vec![U256::zero(); 4], + transactions_trie: Default::default(), + receipts_trie: Default::default(), + } + } } fn apply_metadata_and_tries_memops, const D: usize>( @@ -151,7 +159,7 @@ fn apply_metadata_and_tries_memops, const D: usize> ), ( GlobalMetadata::StateTrieRootDigestBefore, - h2u(tries.state_trie.hash()), + hash_serialize_u256(&tries.state_smt), ), ( GlobalMetadata::TransactionTrieRootDigestBefore, @@ -220,13 +228,12 @@ fn apply_metadata_and_tries_memops, const D: usize> pub(crate) fn debug_inputs(inputs: &GenerationInputs) { log::debug!("Input signed_txn: {:?}", &inputs.signed_txn); - log::debug!("Input state_trie: {:?}", &inputs.tries.state_trie); + log::debug!("Input state_trie: {:?}", &inputs.tries.state_smt); log::debug!( "Input transactions_trie: {:?}", &inputs.tries.transactions_trie ); log::debug!("Input receipts_trie: {:?}", &inputs.tries.receipts_trie); - log::debug!("Input storage_tries: {:?}", &inputs.tries.storage_tries); log::debug!("Input contract_code: {:?}", &inputs.contract_code); } @@ -293,7 +300,7 @@ pub fn generate_traces, const D: usize>( Ok((tables, public_values)) } -fn simulate_cpu(state: &mut GenerationState) -> anyhow::Result<()> { +fn simulate_cpu(state: &mut GenerationState) -> anyhow::Result<()> { state.run_cpu()?; let pc = state.registers.program_counter; diff --git a/evm_arithmetization/src/generation/mpt.rs b/evm_arithmetization/src/generation/mpt.rs index 5c824ad9d..6076fd770 100644 --- a/evm_arithmetization/src/generation/mpt.rs +++ b/evm_arithmetization/src/generation/mpt.rs @@ -1,26 +1,24 @@ use core::ops::Deref; -use std::collections::HashMap; use bytes::Bytes; -use ethereum_types::{Address, BigEndianHash, H256, U256}; -use keccak_hash::keccak; -use mpt_trie::nibbles::{Nibbles, NibblesIntern}; -use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; +use ethereum_types::{Address, H256, U256}; +use mpt_trie::partial_trie::HashedPartialTrie; use rlp::{Decodable, DecoderError, Encodable, PayloadInfo, Rlp, RlpStream}; use rlp_derive::{RlpDecodable, RlpEncodable}; +use smt_trie::code::hash_bytecode_u256; use crate::cpu::kernel::constants::trie_type::PartialTrieType; use crate::generation::TrieInputs; use crate::util::h2u; -use crate::witness::errors::{ProgramError, ProverInputError}; +use crate::witness::errors::ProgramError; use crate::Node; #[derive(RlpEncodable, RlpDecodable, Debug)] pub struct AccountRlp { pub nonce: U256, pub balance: U256, - pub storage_root: H256, - pub code_hash: H256, + pub code_hash: U256, + pub code_length: U256, } #[derive(Clone, Debug)] @@ -30,13 +28,23 @@ pub struct TrieRootPtrs { pub receipt_root_ptr: usize, } +impl Default for TrieRootPtrs { + fn default() -> Self { + Self { + state_root_ptr: 2, + txn_root_ptr: 0, + receipt_root_ptr: 0, + } + } +} + impl Default for AccountRlp { fn default() -> Self { Self { nonce: U256::zero(), balance: U256::zero(), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), + code_hash: hash_bytecode_u256(vec![]), + code_length: U256::zero(), } } } @@ -113,18 +121,6 @@ pub(crate) fn parse_receipts(rlp: &[u8]) -> Result, ProgramError> { Ok(parsed_receipt) } -fn parse_storage_value(value_rlp: &[u8]) -> Result, ProgramError> { - let value: U256 = rlp::decode(value_rlp).map_err(|_| ProgramError::InvalidRlp)?; - Ok(vec![value]) -} - -const fn empty_nibbles() -> Nibbles { - Nibbles { - count: 0, - packed: NibblesIntern::zero(), - } -} - fn load_mpt( trie: &HashedPartialTrie, trie_data: &mut Vec, @@ -203,132 +199,12 @@ where } } -fn load_state_trie( - trie: &HashedPartialTrie, - key: Nibbles, - trie_data: &mut Vec, - storage_tries_by_state_key: &HashMap, -) -> Result { - let node_ptr = trie_data.len(); - let type_of_trie = PartialTrieType::of(trie) as u32; - if type_of_trie > 0 { - trie_data.push(type_of_trie.into()); - } - match trie.deref() { - Node::Empty => Ok(0), - Node::Hash(h) => { - trie_data.push(h2u(*h)); - - Ok(node_ptr) - } - Node::Branch { children, value } => { - if !value.is_empty() { - return Err(ProgramError::ProverInputError( - ProverInputError::InvalidMptInput, - )); - } - // First, set children pointers to 0. - let first_child_ptr = trie_data.len(); - trie_data.extend(vec![U256::zero(); 16]); - // Then, set value pointer to 0. - trie_data.push(U256::zero()); - - // Now, load all children and update their pointers. - for (i, child) in children.iter().enumerate() { - let extended_key = key.merge_nibbles(&Nibbles { - count: 1, - packed: i.into(), - }); - let child_ptr = - load_state_trie(child, extended_key, trie_data, storage_tries_by_state_key)?; - - trie_data[first_child_ptr + i] = child_ptr.into(); - } - - Ok(node_ptr) - } - Node::Extension { nibbles, child } => { - trie_data.push(nibbles.count.into()); - trie_data.push( - nibbles - .try_into() - .map_err(|_| ProgramError::IntegerTooLarge)?, - ); - // Set `value_ptr_ptr`. - trie_data.push((trie_data.len() + 1).into()); - let extended_key = key.merge_nibbles(nibbles); - let child_ptr = - load_state_trie(child, extended_key, trie_data, storage_tries_by_state_key)?; - if child_ptr == 0 { - trie_data.push(0.into()); - } - - Ok(node_ptr) - } - Node::Leaf { nibbles, value } => { - let account: AccountRlp = rlp::decode(value).map_err(|_| ProgramError::InvalidRlp)?; - let AccountRlp { - nonce, - balance, - storage_root, - code_hash, - } = account; - - let storage_hash_only = HashedPartialTrie::new(Node::Hash(storage_root)); - let merged_key = key.merge_nibbles(nibbles); - let storage_trie: &HashedPartialTrie = storage_tries_by_state_key - .get(&merged_key) - .copied() - .unwrap_or(&storage_hash_only); - - assert_eq!(storage_trie.hash(), storage_root, - "In TrieInputs, an account's storage_root didn't match the associated storage trie hash"); - - trie_data.push(nibbles.count.into()); - trie_data.push( - nibbles - .try_into() - .map_err(|_| ProgramError::IntegerTooLarge)?, - ); - // Set `value_ptr_ptr`. - trie_data.push((trie_data.len() + 1).into()); - - trie_data.push(nonce); - trie_data.push(balance); - // Storage trie ptr. - let storage_ptr_ptr = trie_data.len(); - trie_data.push((trie_data.len() + 2).into()); - trie_data.push(code_hash.into_uint()); - let storage_ptr = load_mpt(storage_trie, trie_data, &parse_storage_value)?; - if storage_ptr == 0 { - trie_data[storage_ptr_ptr] = 0.into(); - } - - Ok(node_ptr) - } - } -} - pub(crate) fn load_all_mpts( trie_inputs: &TrieInputs, ) -> Result<(TrieRootPtrs, Vec), ProgramError> { - let mut trie_data = vec![U256::zero()]; - let storage_tries_by_state_key = trie_inputs - .storage_tries - .iter() - .map(|(hashed_address, storage_trie)| { - let key = Nibbles::from_bytes_be(hashed_address.as_bytes()) - .expect("An H256 is 32 bytes long"); - (key, storage_trie) - }) - .collect(); - - let state_root_ptr = load_state_trie( - &trie_inputs.state_trie, - empty_nibbles(), - &mut trie_data, - &storage_tries_by_state_key, - )?; + let mut trie_data = trie_inputs.state_smt.clone(); + + let state_root_ptr = 2; let txn_root_ptr = load_mpt(&trie_inputs.transactions_trie, &mut trie_data, &|rlp| { let mut parsed_txn = vec![U256::from(rlp.len())]; diff --git a/evm_arithmetization/src/generation/prover_input.rs b/evm_arithmetization/src/generation/prover_input.rs index 29186d364..408222945 100644 --- a/evm_arithmetization/src/generation/prover_input.rs +++ b/evm_arithmetization/src/generation/prover_input.rs @@ -4,10 +4,10 @@ use std::collections::{BTreeSet, HashMap}; use std::str::FromStr; use anyhow::{bail, Error, Result}; -use ethereum_types::{BigEndianHash, H256, U256, U512}; +use ethereum_types::{U256, U512}; use itertools::Itertools; use num_bigint::BigUint; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use serde::{Deserialize, Serialize}; use crate::cpu::kernel::cancun_constants::KZG_VERSIONED_HASH; @@ -46,7 +46,7 @@ impl From> for ProverInputFn { } } -impl GenerationState { +impl GenerationState { pub(crate) fn prover_input(&mut self, input_fn: &ProverInputFn) -> Result { match input_fn.0[0].as_str() { "no_txn" => self.no_txn(), @@ -172,13 +172,25 @@ impl GenerationState { let code = self .inputs .contract_code - .get(&H256::from_uint(&codehash)) + .get(&codehash) .ok_or(ProgramError::ProverInputError(CodeHashNotFound))?; + let code_len = code.len(); + for &byte in code { self.memory.set(address, byte.into()); address.increment(); } - Ok(code.len().into()) + + // Padding + self.memory.set(address, 1.into()); + let mut len = code_len + 1; + len = 56 * ((len + 55) / 56); + let last_byte_addr = MemoryAddress::new(context, Segment::Code, len - 1); + let mut last_byte = u256_to_usize(self.memory.get_with_init(last_byte_addr))?; + last_byte |= 0x80; + self.memory.set(last_byte_addr, last_byte.into()); + + Ok(len.into()) } // Bignum modular multiplication. @@ -551,7 +563,7 @@ impl GenerationState { } } -impl GenerationState { +impl GenerationState { /// Simulate the user's code and store all the jump addresses with their /// respective contexts. fn generate_jumpdest_table(&mut self) -> Result<(), ProgramError> { diff --git a/evm_arithmetization/src/generation/state.rs b/evm_arithmetization/src/generation/state.rs index bc1a02203..5e3beb81f 100644 --- a/evm_arithmetization/src/generation/state.rs +++ b/evm_arithmetization/src/generation/state.rs @@ -6,7 +6,8 @@ use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use itertools::Itertools; use keccak_hash::keccak; use log::Level; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; +use smt_trie::code::hash_bytecode_u256; use super::mpt::{load_all_mpts, TrieRootPtrs}; use super::TrieInputs; @@ -20,6 +21,7 @@ use crate::generation::GenerationInputs; use crate::keccak_sponge::columns::KECCAK_WIDTH_BYTES; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; use crate::memory::segments::Segment; +use crate::poseidon::poseidon_stark::PoseidonOp; use crate::util::u256_to_usize; use crate::witness::errors::ProgramError; use crate::witness::memory::MemoryChannel::GeneralPurpose; @@ -37,7 +39,7 @@ use crate::{arithmetic, keccak, logic}; /// A State is either an `Interpreter` (used for tests and jumpdest analysis) or /// a `GenerationState`. -pub(crate) trait State { +pub(crate) trait State { /// Returns a `State`'s latest `Checkpoint`. fn checkpoint(&mut self) -> GenerationStateCheckpoint; @@ -99,6 +101,10 @@ pub(crate) trait State { self.get_mut_generation_state().traces.memory_ops.push(op); } + fn push_poseidon(&mut self, op: PoseidonOp) { + self.get_mut_generation_state().traces.poseidon_ops.push(op); + } + fn push_byte_packing(&mut self, op: BytePackingOp) { self.get_mut_generation_state() .traces @@ -270,7 +276,7 @@ pub(crate) trait State { } #[derive(Debug)] -pub(crate) struct GenerationState { +pub(crate) struct GenerationState { pub(crate) inputs: GenerationInputs, pub(crate) registers: RegistersState, pub(crate) memory: MemoryState, @@ -306,8 +312,12 @@ pub(crate) struct GenerationState { pub(crate) jumpdest_table: Option>>, } -impl GenerationState { +impl GenerationState { fn preinitialize_mpts(&mut self, trie_inputs: &TrieInputs) -> TrieRootPtrs { + if trie_inputs.state_smt == TrieInputs::default().state_smt { + return TrieRootPtrs::default(); + } + let (trie_roots_ptrs, trie_data) = load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); @@ -357,8 +367,7 @@ impl GenerationState { self.observe_address(tip_h160); } else if dst == KERNEL.global_labels["observe_new_contract"] { let tip_u256 = stack_peek(self, 0)?; - let tip_h256 = H256::from_uint(&tip_u256); - self.observe_contract(tip_h256)?; + self.observe_contract(tip_u256)?; } Ok(()) @@ -374,7 +383,7 @@ impl GenerationState { /// Observe the given code hash and store the associated code. /// When called, the code corresponding to `codehash` should be stored in /// the return data. - pub(crate) fn observe_contract(&mut self, codehash: H256) -> Result<(), ProgramError> { + pub(crate) fn observe_contract(&mut self, codehash: U256) -> Result<(), ProgramError> { if self.inputs.contract_code.contains_key(&codehash) { return Ok(()); // Return early if the code hash has already been // observed. @@ -390,7 +399,7 @@ impl GenerationState { .iter() .map(|x| x.unwrap_or_default().low_u32() as u8) .collect::>(); - debug_assert_eq!(keccak(&code), codehash); + debug_assert_eq!(hash_bytecode_u256(code.clone()), codehash); self.inputs.contract_code.insert(codehash, code); @@ -431,7 +440,7 @@ impl GenerationState { } } -impl State for GenerationState { +impl State for GenerationState { fn checkpoint(&mut self) -> GenerationStateCheckpoint { GenerationStateCheckpoint { registers: self.registers, @@ -537,7 +546,7 @@ impl State for GenerationState { } } -impl Transition for GenerationState { +impl Transition for GenerationState { fn skip_if_necessary(&mut self, op: Operation) -> Result { Ok(op) } diff --git a/evm_arithmetization/src/generation/trie_extractor.rs b/evm_arithmetization/src/generation/trie_extractor.rs index 48fc28f53..04cda9c5e 100644 --- a/evm_arithmetization/src/generation/trie_extractor.rs +++ b/evm_arithmetization/src/generation/trie_extractor.rs @@ -93,16 +93,17 @@ pub(crate) fn read_state_rlp_value( memory: &MemoryState, slice: &MemoryValues, ) -> Result, ProgramError> { - let storage_trie: HashedPartialTrie = + let _storage_trie: HashedPartialTrie = get_trie(memory, slice[2].unwrap_or_default().as_usize(), |_, x| { Ok(rlp::encode(&read_storage_trie_value(x)).to_vec()) })?; - let account = AccountRlp { - nonce: slice[0].unwrap_or_default(), - balance: slice[1].unwrap_or_default(), - storage_root: storage_trie.hash(), - code_hash: H256::from_uint(&slice[3].unwrap_or_default()), - }; + // let account = AccountRlp { + // nonce: slice[0], + // balance: slice[1], + // storage_root: storage_trie.hash(), + // code_hash: H256::from_uint(&slice[3]), + // }; + let account = AccountRlp::default(); // TODO: fix Ok(rlp::encode(&account).to_vec()) } diff --git a/evm_arithmetization/src/lib.rs b/evm_arithmetization/src/lib.rs index 0ee30fe08..8cc4381cf 100644 --- a/evm_arithmetization/src/lib.rs +++ b/evm_arithmetization/src/lib.rs @@ -174,7 +174,7 @@ //! ``` //! //! Note that an entire prover state built with wide ranges may be particularly -//! large (up to ~25 GB), hence serialization methods, while faster than doing +//! large (up to ~40 GB), hence serialization methods, while faster than doing //! another preprocessing, may take some non-negligible time. #![cfg_attr(docsrs, feature(doc_cfg))] @@ -191,6 +191,7 @@ pub mod keccak; pub mod keccak_sponge; pub mod logic; pub mod memory; +pub mod poseidon; // Proving system components pub mod all_stark; diff --git a/evm_arithmetization/src/memory/segments.rs b/evm_arithmetization/src/memory/segments.rs index 8c687ea93..23486f29a 100644 --- a/evm_arithmetization/src/memory/segments.rs +++ b/evm_arithmetization/src/memory/segments.rs @@ -77,10 +77,12 @@ pub(crate) enum Segment { CreatedContracts = 34 << SEGMENT_SCALING_FACTOR, /// Blob versioned hashes specified in a type-3 transaction. TxnBlobVersionedHashes = 35 << SEGMENT_SCALING_FACTOR, + /// List of used storage slots in newly created contracts. + NewStorageSlots = 36 << SEGMENT_SCALING_FACTOR, } impl Segment { - pub(crate) const COUNT: usize = 36; + pub(crate) const COUNT: usize = 37; /// Unscales this segment by `SEGMENT_SCALING_FACTOR`. pub(crate) const fn unscale(&self) -> usize { @@ -125,6 +127,7 @@ impl Segment { Self::TransientStorage, Self::CreatedContracts, Self::TxnBlobVersionedHashes, + Self::NewStorageSlots, ] } @@ -167,6 +170,7 @@ impl Segment { Segment::TransientStorage => "SEGMENT_TRANSIENT_STORAGE", Segment::CreatedContracts => "SEGMENT_CREATED_CONTRACTS", Segment::TxnBlobVersionedHashes => "SEGMENT_TXN_BLOB_VERSIONED_HASHES", + Segment::NewStorageSlots => "SEGMENT_NEW_STORAGE_SLOTS", } } @@ -208,6 +212,7 @@ impl Segment { Segment::TransientStorage => 256, Segment::CreatedContracts => 256, Segment::TxnBlobVersionedHashes => 256, + Segment::NewStorageSlots => 256, } } } diff --git a/evm_arithmetization/src/poseidon/columns.rs b/evm_arithmetization/src/poseidon/columns.rs new file mode 100644 index 000000000..cb3823950 --- /dev/null +++ b/evm_arithmetization/src/poseidon/columns.rs @@ -0,0 +1,138 @@ +use std::mem::{size_of, transmute}; + +use plonky2::hash::poseidon; +use zk_evm_proc_macro::Columns; + +use super::poseidon_stark::FELT_MAX_BYTES; +use crate::util::indices_arr; + +pub(crate) const POSEIDON_SPONGE_WIDTH: usize = poseidon::SPONGE_WIDTH; +pub(crate) const POSEIDON_SPONGE_RATE: usize = poseidon::SPONGE_RATE; +pub(crate) const HALF_N_FULL_ROUNDS: usize = poseidon::HALF_N_FULL_ROUNDS; +pub(crate) const N_PARTIAL_ROUNDS: usize = poseidon::N_PARTIAL_ROUNDS; +pub(crate) const POSEIDON_DIGEST: usize = 4; + +#[repr(C)] +#[derive(Columns, Eq, PartialEq, Debug)] +pub(crate) struct PoseidonColumnsView { + // The base address at which we will read the input block. + pub context: T, + pub segment: T, + pub virt: T, + + /// The timestamp at which Poseidon is called. + pub timestamp: T, + + /// The length of the original input for `PoseidonGeneralOp`. 0 for + /// `PoseidonSimpleOp`. + pub len: T, + /// The number of elements that have already been absorbed prior + /// to this block. + pub already_absorbed_elements: T, + + /// If this row represents a final block row, the `i`th entry should be 1 if + /// the final chunk of input has length `i` (in other words if `len - + /// already_absorbed == i`), otherwise 0. + /// + /// If this row represents a full input block, this should contain all 0s. + pub is_final_input_len: [T; POSEIDON_SPONGE_RATE], + + /// 1 if this row represents a full input block, i.e. one in which each + /// element is an input element, not a padding element; 0 otherwise. + pub is_full_input_block: T, + + /// Registers to hold permutation inputs. + pub input: [T; POSEIDON_SPONGE_WIDTH], + + /// Holds x^3 for all elements in full rounds. + pub cubed_full: [T; 2 * HALF_N_FULL_ROUNDS * POSEIDON_SPONGE_WIDTH], + + /// Holds x^3 for the first element in partial rounds. + pub cubed_partial: [T; N_PARTIAL_ROUNDS], + + /// Holds the input of the `i`-th S-box of the `round`-th round of the first + /// set of full rounds. + pub full_sbox_0: [T; POSEIDON_SPONGE_WIDTH * (HALF_N_FULL_ROUNDS - 1)], + + /// Holds the input of the S-box of the `round`-th round of the partial + /// rounds. + pub partial_sbox: [T; N_PARTIAL_ROUNDS], + + /// Holds the input of the `i`-th S-box of the `round`-th round of the + /// second set of full rounds. + pub full_sbox_1: [T; POSEIDON_SPONGE_WIDTH * HALF_N_FULL_ROUNDS], + + /// The digest, with each element divided into two 32-bit limbs. + pub digest: [T; 2 * POSEIDON_DIGEST], + + /// The output of the hash function with the digest removed. + pub output_partial: [T; POSEIDON_SPONGE_WIDTH - POSEIDON_DIGEST], + + /// Holds the pseudo-inverse of (digest_high_limb_i - 2^32 + 1). + pub pinv: [T; POSEIDON_DIGEST], + + /// Holds the byte decomposition of the input, except for the less + /// significant byte. + pub input_bytes: [[T; FELT_MAX_BYTES - 1]; POSEIDON_SPONGE_RATE], + + /// Indicates if this is a simple operation where inputs are + /// read from the top of the stack. + pub is_simple_op: T, + + /// Indicates if this is the first row of a general operation. + pub is_first_row_general_op: T, + + pub not_padding: T, +} + +/// Returns the index the `i`-th x^3 in the `round`-th round for full rounds. +/// Note: the cubes of the two sets of full rounds are stored one after the +/// other. +pub(crate) fn reg_cubed_full(round: usize, i: usize) -> usize { + debug_assert!(i < POSEIDON_SPONGE_WIDTH); + debug_assert!(round < 2 * HALF_N_FULL_ROUNDS); + POSEIDON_SPONGE_WIDTH * round + i +} + +/// Returns the index of x^3 within for the `round`-th partial round. +pub(crate) fn reg_cubed_partial(round: usize) -> usize { + debug_assert!(round < N_PARTIAL_ROUNDS); + round +} + +/// Returns the index of the `i`-th input in the `round`-th round within +/// `full_sbox_0`. +pub(crate) fn reg_full_sbox_0(round: usize, i: usize) -> usize { + debug_assert!( + round != 0, + "First round S-box inputs are not stored as wires" + ); + debug_assert!(round < HALF_N_FULL_ROUNDS); + debug_assert!(i < POSEIDON_SPONGE_WIDTH); + POSEIDON_SPONGE_WIDTH * (round - 1) + i +} + +/// Returns the index of the input of the S-box of the `round`-th round of the +/// partial rounds. +pub(crate) fn reg_partial_sbox(round: usize) -> usize { + debug_assert!(round < N_PARTIAL_ROUNDS); + round +} + +/// Returns the index of the `i`-th input in the `round`-th round within +/// `full_sbox_1`. +pub(crate) fn reg_full_sbox_1(round: usize, i: usize) -> usize { + debug_assert!(round < HALF_N_FULL_ROUNDS); + debug_assert!(i < POSEIDON_SPONGE_WIDTH); + POSEIDON_SPONGE_WIDTH * round + i +} + +// `u8` is guaranteed to have a `size_of` of 1. +pub(crate) const NUM_COLUMNS: usize = size_of::>(); + +const fn make_col_map() -> PoseidonColumnsView { + let indices_arr = indices_arr::(); + unsafe { transmute::<[usize; NUM_COLUMNS], PoseidonColumnsView>(indices_arr) } +} + +pub(crate) const POSEIDON_COL_MAP: PoseidonColumnsView = make_col_map(); diff --git a/evm_arithmetization/src/poseidon/mod.rs b/evm_arithmetization/src/poseidon/mod.rs new file mode 100644 index 000000000..5ee77f125 --- /dev/null +++ b/evm_arithmetization/src/poseidon/mod.rs @@ -0,0 +1,2 @@ +pub mod columns; +pub mod poseidon_stark; diff --git a/evm_arithmetization/src/poseidon/poseidon_stark.rs b/evm_arithmetization/src/poseidon/poseidon_stark.rs new file mode 100644 index 000000000..683a424eb --- /dev/null +++ b/evm_arithmetization/src/poseidon/poseidon_stark.rs @@ -0,0 +1,1005 @@ +use std::borrow::Borrow; +use std::marker::PhantomData; + +use itertools::Itertools; +use plonky2::field::extension::{Extendable, FieldExtension}; +use plonky2::field::packed::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; +use plonky2::hash::poseidon::Poseidon; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::timed; +use plonky2::util::timing::TimingTree; +use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use starky::cross_table_lookup::TableWithColumns; +use starky::evaluation_frame::StarkEvaluationFrame; +use starky::lookup::{Column, Filter}; +use starky::stark::Stark; +use starky::util::trace_rows_to_poly_values; + +use super::columns::{ + reg_cubed_full, reg_cubed_partial, reg_full_sbox_0, reg_full_sbox_1, reg_partial_sbox, + PoseidonColumnsView, HALF_N_FULL_ROUNDS, NUM_COLUMNS, N_PARTIAL_ROUNDS, POSEIDON_COL_MAP, + POSEIDON_DIGEST, POSEIDON_SPONGE_RATE, POSEIDON_SPONGE_WIDTH, +}; +use crate::all_stark::{EvmStarkFrame, Table}; +use crate::witness::memory::MemoryAddress; + +/// Maximum number of bytes that can be packed into a field element without +/// performing a modular reduction. +// TODO: this constant depends on the size of F, which is not bounded. +pub const FELT_MAX_BYTES: usize = 7; + +pub(crate) fn ctl_looked_simple_op() -> TableWithColumns { + let mut columns = Column::singles(POSEIDON_COL_MAP.input).collect_vec(); + columns.extend(Column::singles(POSEIDON_COL_MAP.digest)); + TableWithColumns::new( + *Table::Poseidon, + columns, + Filter::new_simple(Column::single(POSEIDON_COL_MAP.is_simple_op)), + ) +} + +pub(crate) fn ctl_looked_general_output() -> TableWithColumns { + let mut columns = Column::singles(POSEIDON_COL_MAP.digest).collect_vec(); + columns.push(Column::single(POSEIDON_COL_MAP.timestamp)); + TableWithColumns::new( + *Table::Poseidon, + columns, + Filter::new( + vec![( + Column::sum(POSEIDON_COL_MAP.is_final_input_len), + Column::linear_combination_with_constant( + [(POSEIDON_COL_MAP.is_simple_op, -F::ONE)], + F::ONE, + ), + )], + vec![], + ), + ) +} + +pub(crate) fn ctl_looked_general_input() -> TableWithColumns { + let context = Column::single(POSEIDON_COL_MAP.context); + let segment: Column = Column::single(POSEIDON_COL_MAP.segment); + let virt = Column::single(POSEIDON_COL_MAP.virt); + let len = Column::single(POSEIDON_COL_MAP.len); + + let timestamp = Column::single(POSEIDON_COL_MAP.timestamp); + + TableWithColumns::new( + *Table::Poseidon, + vec![context, segment, virt, len, timestamp], + Filter::new_simple(Column::single(POSEIDON_COL_MAP.is_first_row_general_op)), + ) +} + +pub fn ctl_looking_memory(i: usize) -> Vec> { + let cols = POSEIDON_COL_MAP; + let mut res = vec![Column::constant(F::ONE)]; // is_read + + res.extend(Column::singles([cols.context, cols.segment])); + + res.push(Column::linear_combination_with_constant( + [ + (cols.virt, F::ONE), + (cols.already_absorbed_elements, F::ONE), + ], + F::from_canonical_usize(i), + )); + + // The first byte of of each field element is + // mem[virt + already_absorbed_elements + i/ FELT_MAX_BYTES] - \sum_j + // input_bytes[i/FELT_MAX_BYTES][j]* 256^j. + // The other bytes are input_bytes[i/FELT_MAX_BYTES][i % FELT_MAX_BYTES - 1] + if i % FELT_MAX_BYTES == 0 { + res.push(Column::linear_combination( + std::iter::once((cols.input[i / FELT_MAX_BYTES], F::ONE)).chain( + (0..FELT_MAX_BYTES - 1).map(|j| { + ( + cols.input_bytes[i / FELT_MAX_BYTES][j], + -F::from_canonical_u64(1 << (8 * (j + 1))), + ) + }), + ), + )); + } else { + res.push(Column::single( + cols.input_bytes[i / FELT_MAX_BYTES][(i % FELT_MAX_BYTES) - 1], + )); + } + res.extend((1..8).map(|_| Column::zero())); + + res.push(Column::single(cols.timestamp)); + + assert_eq!( + res.len(), + crate::memory::memory_stark::ctl_data::().len() + ); + + res +} + +// TODO: Support non-padded inputs? +pub fn ctl_looking_memory_filter() -> Filter { + let cols = POSEIDON_COL_MAP; + Filter::new( + vec![( + Column::single(cols.not_padding), + Column::linear_combination_with_constant([(cols.is_simple_op, -F::ONE)], F::ONE), + )], + vec![], + ) +} + +#[derive(Clone, Debug)] +pub enum PoseidonOp { + PoseidonSimpleOp(PoseidonSimpleOp), + PoseidonGeneralOp(PoseidonGeneralOp), +} + +#[derive(Copy, Clone, Debug)] +pub struct PoseidonSimpleOp(pub [F; POSEIDON_SPONGE_WIDTH]); + +#[derive(Clone, Debug)] +pub struct PoseidonGeneralOp { + /// The base address at which inputs are read. + pub(crate) base_address: MemoryAddress, + + /// The timestamp at which inputs are read. + pub(crate) timestamp: usize, + + /// The input that was read. We assume that it was + /// previously padded. + pub(crate) input: Vec, + + /// Length of the input before paddding. + pub(crate) len: usize, +} + +#[derive(Copy, Clone, Default)] +pub struct PoseidonStark { + pub(crate) f: PhantomData, +} + +/// Information about a Poseidon operation needed for witness generation. +impl, const D: usize> PoseidonStark { + /// Generate the rows of the trace. Note that this does not generate the + /// permuted columns used in our lookup arguments, as those are computed + /// after transposing to column-wise form. + fn generate_trace_rows( + &self, + operations: Vec>, + min_rows: usize, + ) -> Vec<[F; NUM_COLUMNS]> { + let base_len: usize = operations + .iter() + .map(|op| match op { + PoseidonOp::PoseidonSimpleOp(_) => 1, + PoseidonOp::PoseidonGeneralOp(op) => { + debug_assert!(op.input.len() % (FELT_MAX_BYTES * POSEIDON_SPONGE_RATE) == 0); + (op.input.len() + FELT_MAX_BYTES * POSEIDON_SPONGE_RATE - 1) + / (FELT_MAX_BYTES * POSEIDON_SPONGE_RATE) + } + }) + .sum(); + + let num_rows = base_len.max(min_rows).next_power_of_two(); + let mut rows = Vec::with_capacity(base_len.max(min_rows)); + + for op in operations { + match op { + PoseidonOp::PoseidonSimpleOp(op) => rows.push(self.generate_row_for_simple_op(op)), + PoseidonOp::PoseidonGeneralOp(op) => { + rows.extend(self.generate_rows_for_general_op(op)) + } + } + } + + // We generate "actual" rows for padding to avoid having to store + // another power of x, on top of x^3 and x^6. + let padding_row: [F; NUM_COLUMNS] = { + let mut tmp_row = PoseidonColumnsView::default(); + let padding_inp = [F::ZERO; POSEIDON_SPONGE_WIDTH]; + Self::generate_perm(&mut tmp_row, padding_inp); + tmp_row + } + .into(); + while rows.len() < num_rows { + rows.push(padding_row); + } + rows + } + + fn generate_row_for_simple_op(&self, op: PoseidonSimpleOp) -> [F; NUM_COLUMNS] { + let mut row = PoseidonColumnsView::default(); + Self::generate_perm(&mut row, op.0); + row.is_final_input_len[POSEIDON_SPONGE_RATE - 1] = F::ONE; + row.not_padding = F::ONE; + row.is_simple_op = F::ONE; + row.into() + } + + fn generate_rows_for_general_op(&self, op: PoseidonGeneralOp) -> Vec<[F; NUM_COLUMNS]> { + let input_blocks = op.input.chunks_exact(FELT_MAX_BYTES * POSEIDON_SPONGE_RATE); + let mut rows: Vec<[F; NUM_COLUMNS]> = + Vec::with_capacity(op.input.len() / (FELT_MAX_BYTES * POSEIDON_SPONGE_RATE)); + let last_non_padding_elt = op.len % (FELT_MAX_BYTES * POSEIDON_SPONGE_RATE); + let total_length = input_blocks.len(); + let mut already_absorbed_elements = 0; + let mut state = [F::ZERO; POSEIDON_SPONGE_WIDTH]; + for (counter, block) in input_blocks.enumerate() { + state[0..POSEIDON_SPONGE_RATE].copy_from_slice( + &block + .chunks_exact(FELT_MAX_BYTES) + .map(|first_bytes| { + let mut bytes = [0u8; POSEIDON_SPONGE_RATE]; + bytes[..7].copy_from_slice(first_bytes); + F::from_canonical_u64(u64::from_le_bytes(bytes)) + }) + .collect::>(), + ); + let mut row = if counter == total_length - 1 { + let tmp_row = + self.generate_trace_final_row_for_perm(state, &op, already_absorbed_elements); + already_absorbed_elements += last_non_padding_elt; + tmp_row + } else { + let tmp_row = + self.generate_trace_row_for_perm(state, &op, already_absorbed_elements); + already_absorbed_elements += FELT_MAX_BYTES * POSEIDON_SPONGE_RATE; + tmp_row + }; + row.not_padding = F::ONE; + for (input_bytes_chunk, block_chunk) in row + .input_bytes + .iter_mut() + .zip_eq(block.chunks(FELT_MAX_BYTES)) + { + input_bytes_chunk.copy_from_slice( + &block_chunk[1..] + .iter() + .map(|&byte| F::from_canonical_u8(byte)) + .collect::>(), + ); + } + // Set the capacity to the digest + state[POSEIDON_SPONGE_RATE..POSEIDON_SPONGE_WIDTH].copy_from_slice( + &row.digest + .chunks(2) + .map(|x| x[0] + F::from_canonical_u64(1 << 32) * x[1]) + .collect_vec(), + ); + + rows.push(row.into()); + } + if let Some(first_row) = rows.first_mut() { + first_row[POSEIDON_COL_MAP.is_first_row_general_op] = F::ONE; + } + + rows + } + + fn generate_commons( + row: &mut PoseidonColumnsView, + input: [F; POSEIDON_SPONGE_WIDTH], + op: &PoseidonGeneralOp, + already_absorbed_elements: usize, + ) { + row.context = F::from_canonical_usize(op.base_address.context); + row.segment = F::from_canonical_usize(op.base_address.segment); + row.virt = F::from_canonical_usize(op.base_address.virt); + row.timestamp = F::from_canonical_usize(op.timestamp); + row.len = F::from_canonical_usize(op.len); + row.already_absorbed_elements = F::from_canonical_usize(already_absorbed_elements); + + Self::generate_perm(row, input); + } + // One row per permutation. + fn generate_trace_row_for_perm( + &self, + input: [F; POSEIDON_SPONGE_WIDTH], + op: &PoseidonGeneralOp, + already_absorbed_elements: usize, + ) -> PoseidonColumnsView { + let mut row = PoseidonColumnsView::default(); + row.is_full_input_block = F::ONE; + + Self::generate_commons(&mut row, input, op, already_absorbed_elements); + row + } + + fn generate_trace_final_row_for_perm( + &self, + input: [F; POSEIDON_SPONGE_WIDTH], + op: &PoseidonGeneralOp, + already_absorbed_elements: usize, + ) -> PoseidonColumnsView { + let mut row = PoseidonColumnsView::default(); + // TODO: I think we're assumming op.len is a multiple FELT_MAX_BYTES * + // POSEIDON_SPONGE_RATE + row.is_final_input_len[op.len % (FELT_MAX_BYTES * POSEIDON_SPONGE_RATE)] = F::ONE; + + Self::generate_commons(&mut row, input, op, already_absorbed_elements); + + row + } + + fn generate_perm(row: &mut PoseidonColumnsView, input: [F; POSEIDON_SPONGE_WIDTH]) { + // Populate the round input for the first round. + row.input.copy_from_slice(&input); + + let mut state = input; + let mut round_ctr = 0; + + for r in 0..HALF_N_FULL_ROUNDS { + ::constant_layer_field(&mut state, round_ctr); + + for i in 0..POSEIDON_SPONGE_WIDTH { + // We do not need to store the first full_sbox_0 inputs, since they are + // the permutation's inputs. + if r != 0 { + row.full_sbox_0[reg_full_sbox_0(r, i)] = state[i]; + } + // Generate x^3 and x^6 for the SBox layer constraints. + row.cubed_full[reg_cubed_full(r, i)] = state[i].cube(); + + // Apply x^7 to the state. + state[i] *= + row.cubed_full[reg_cubed_full(r, i)] * row.cubed_full[reg_cubed_full(r, i)]; + } + state = ::mds_layer_field(&state); + round_ctr += 1; + } + + ::partial_first_constant_layer(&mut state); + state = ::mds_partial_layer_init(&state); + for r in 0..(N_PARTIAL_ROUNDS - 1) { + row.partial_sbox[reg_partial_sbox(r)] = state[0]; + + // Generate x^3 for the SBox layer constraints. + row.cubed_partial[reg_cubed_partial(r)] = state[0] * state[0] * state[0]; + + state[0] *= + row.cubed_partial[reg_cubed_partial(r)] * row.cubed_partial[reg_cubed_partial(r)]; + state[0] += F::from_canonical_u64(::FAST_PARTIAL_ROUND_CONSTANTS[r]); + state = ::mds_partial_layer_fast_field(&state, r); + } + + row.partial_sbox[reg_partial_sbox(N_PARTIAL_ROUNDS - 1)] = state[0]; + // Generate x^3 and x^6 for the SBox layer constraints. + row.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)] = state[0].cube(); + + state[0] *= row.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)] + * row.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)]; + state = ::mds_partial_layer_fast_field(&state, N_PARTIAL_ROUNDS - 1); + round_ctr += N_PARTIAL_ROUNDS; + + for r in 0..HALF_N_FULL_ROUNDS { + ::constant_layer_field(&mut state, round_ctr); + for i in 0..POSEIDON_SPONGE_WIDTH { + row.full_sbox_1[reg_full_sbox_1(r, i)] = state[i]; + // Generate x^3 and x^6 for the SBox layer constraints. + row.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)] = state[i].cube(); + + state[i] *= row.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)] + * row.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)]; + } + state = ::mds_layer_field(&state); + round_ctr += 1; + } + + for i in 0..POSEIDON_DIGEST { + let state_val = state[i].to_canonical_u64(); + let hi_limb = F::from_canonical_u32((state_val >> 32) as u32); + row.pinv[i] = + if let Some(inv) = (hi_limb - F::from_canonical_u32(u32::MAX)).try_inverse() { + inv + } else { + F::ZERO + }; + row.digest[2 * i] = F::from_canonical_u32(state_val as u32); + row.digest[2 * i + 1] = hi_limb; + } + row.output_partial + .copy_from_slice(&state[POSEIDON_DIGEST..POSEIDON_SPONGE_WIDTH]); + } + + pub fn generate_trace( + &self, + operations: Vec>, + min_rows: usize, + timing: &mut TimingTree, + ) -> Vec> { + // Generate the witness, except for permuted columns in the lookup argument. + let trace_rows = timed!( + timing, + "generate trace rows", + self.generate_trace_rows(operations, min_rows) + ); + let trace_polys = timed!( + timing, + "convert to PolynomialValues", + trace_rows_to_poly_values(trace_rows) + ); + trace_polys + } +} + +impl, const D: usize> Stark for PoseidonStark { + type EvaluationFrame = EvmStarkFrame + where + FE: FieldExtension, + P: PackedField; + + type EvaluationFrameTarget = EvmStarkFrame, ExtensionTarget, NUM_COLUMNS>; + + fn eval_packed_generic( + &self, + vars: &Self::EvaluationFrame, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + let lv: &[P; NUM_COLUMNS] = vars.get_local_values().try_into().unwrap(); + let lv: &PoseidonColumnsView

= lv.borrow(); + let nv: &[P; NUM_COLUMNS] = vars.get_next_values().try_into().unwrap(); + let nv: &PoseidonColumnsView

= nv.borrow(); + + // Each flag must be boolean. + let is_full_input_block = lv.is_full_input_block; + yield_constr.constraint(is_full_input_block * (is_full_input_block - P::ONES)); + + let is_final_block: P = lv.is_final_input_len.iter().copied().sum(); + yield_constr.constraint(is_final_block * (is_final_block - P::ONES)); + + for &is_final_len in lv.is_final_input_len.iter() { + yield_constr.constraint(is_final_len * (is_final_len - P::ONES)); + } + + let is_first_row_general_op = lv.is_first_row_general_op; + yield_constr.constraint(is_first_row_general_op * (is_first_row_general_op - P::ONES)); + + // Ensure that full-input block and final block flags are not set to 1 at the + // same time. + yield_constr.constraint(is_final_block * is_full_input_block); + + // If this is the first row, the original sponge state should have the input in + // the first `POSEIDON_SPONGE_RATE` elements followed by 0 for the + // capacity elements. The input values are checked with a CTL. + // Also, already_absorbed_elements = 0. + let already_absorbed_elements = lv.already_absorbed_elements; + yield_constr.constraint_first_row(already_absorbed_elements); + + for i in POSEIDON_SPONGE_RATE..POSEIDON_SPONGE_WIDTH { + // If the operation has len > 0 the capacity must be 0 + yield_constr.constraint_first_row(lv.len * lv.input[i]); + } + + // If this is a final row and there is an upcoming operation, then + // we make the previous checks for next row's `already_absorbed_elements` + // and the original sponge state. + yield_constr.constraint_transition(is_final_block * nv.already_absorbed_elements); + + for i in POSEIDON_SPONGE_RATE..POSEIDON_SPONGE_WIDTH { + // If the next block is a general operation (len > 0) and this is a final block, + // the capacity must be 0 for the next row. + yield_constr.constraint_transition(nv.len * is_final_block * nv.input[i]); + } + + // If this is a full-input block, the next row's address, + // time and len must match as well as its timestamp. + yield_constr.constraint_transition(is_full_input_block * (lv.context - nv.context)); + yield_constr.constraint_transition(is_full_input_block * (lv.segment - nv.segment)); + yield_constr.constraint_transition(is_full_input_block * (lv.virt - nv.virt)); + yield_constr.constraint_transition(is_full_input_block * (lv.timestamp - nv.timestamp)); + + // If this is a full-input block, the next row's already_absorbed_elements + // should be ours plus `POSEIDON_SPONGE_RATE`, and the next input's + // capacity is the current digest. + yield_constr.constraint_transition( + is_full_input_block + * (already_absorbed_elements + + P::from(FE::from_canonical_usize( + FELT_MAX_BYTES * POSEIDON_SPONGE_RATE, + )) + - nv.already_absorbed_elements), + ); + + for i in 0..POSEIDON_SPONGE_WIDTH - POSEIDON_SPONGE_RATE { + yield_constr.constraint_transition( + is_full_input_block + * (lv.digest[2 * i] + + lv.digest[2 * i + 1] * P::Scalar::from_canonical_u64(1 << 32) + - nv.input[POSEIDON_SPONGE_RATE + i]), + ); + } + + // A dummy row is always followed by another dummy row, so the prover + // can't put dummy rows "in between" to avoid the above checks. + let is_dummy = P::ONES - is_full_input_block - is_final_block; + let next_is_final_block: P = nv.is_final_input_len.iter().copied().sum(); + yield_constr + .constraint_transition(is_dummy * (nv.is_full_input_block + next_is_final_block)); + + // If len > 0 and this is a final block, is_final_input_len implies `len + // - already_absorbed == i`. + let offset = lv.len - already_absorbed_elements; + for (i, &is_final_len) in lv.is_final_input_len.iter().enumerate() { + let entry_match = offset + - P::from(FE::from_canonical_usize( + FELT_MAX_BYTES * POSEIDON_SPONGE_RATE - i, + )); + yield_constr.constraint(lv.len * is_final_len * entry_match); + } + + // Compute the input layer. We assume that, when necessary, + // input values were previously swapped before being passed + // to Poseidon. + let mut state = lv.input; + + let mut round_ctr = 0; + + // First set of full rounds. + for r in 0..HALF_N_FULL_ROUNDS { + ::constant_layer_packed_field(&mut state, round_ctr); + + for i in 0..POSEIDON_SPONGE_WIDTH { + if r != 0 { + let sbox_in = lv.full_sbox_0[reg_full_sbox_0(r, i)]; + yield_constr.constraint(state[i] - sbox_in); + state[i] = sbox_in; + } + + // Check that the powers were correctly generated. + let cube = state[i] * state[i] * state[i]; + yield_constr.constraint(cube - lv.cubed_full[reg_cubed_full(r, i)]); + + state[i] *= + lv.cubed_full[reg_cubed_full(r, i)] * lv.cubed_full[reg_cubed_full(r, i)]; + } + + state = ::mds_layer_packed_field(&state); + round_ctr += 1; + } + + // Partial rounds. + ::partial_first_constant_layer_packed_field(&mut state); + state = ::mds_partial_layer_init_packed_field(&state); + for r in 0..(N_PARTIAL_ROUNDS - 1) { + let sbox_in = lv.partial_sbox[reg_partial_sbox(r)]; + yield_constr.constraint(state[0] - sbox_in); + state[0] = sbox_in; + + // Check that the powers were generated correctly. + let cube = state[0] * state[0] * state[0]; + yield_constr.constraint(cube - lv.cubed_partial[reg_cubed_partial(r)]); + + state[0] = lv.cubed_partial[reg_cubed_partial(r)] + * lv.cubed_partial[reg_cubed_partial(r)] + * sbox_in; + state[0] += + P::Scalar::from_canonical_u64(::FAST_PARTIAL_ROUND_CONSTANTS[r]); + state = ::mds_partial_layer_fast_packed_field(&state, r); + } + let sbox_in = lv.partial_sbox[reg_partial_sbox(N_PARTIAL_ROUNDS - 1)]; + yield_constr.constraint(state[0] - sbox_in); + state[0] = sbox_in; + + // Check that the powers were generated correctly. + let cube = state[0] * state[0] * state[0]; + yield_constr.constraint(cube - lv.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)]); + + state[0] = lv.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)] + * lv.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)] + * sbox_in; + state = ::mds_partial_layer_fast_packed_field(&state, N_PARTIAL_ROUNDS - 1); + round_ctr += N_PARTIAL_ROUNDS; + + // Second set of full rounds. + for r in 0..HALF_N_FULL_ROUNDS { + ::constant_layer_packed_field(&mut state, round_ctr); + for i in 0..POSEIDON_SPONGE_WIDTH { + let sbox_in = lv.full_sbox_1[reg_full_sbox_1(r, i)]; + yield_constr.constraint(state[i] - sbox_in); + state[i] = sbox_in; + + // Check that the powers were correctly generated. + let cube = state[i] * state[i] * state[i]; + yield_constr + .constraint(cube - lv.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)]); + + state[i] *= lv.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)] + * lv.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)]; + } + state = ::mds_layer_packed_field(&state); + round_ctr += 1; + } + + for i in 0..POSEIDON_DIGEST { + yield_constr.constraint( + state[i] + - (lv.digest[2 * i] + + lv.digest[2 * i + 1] * P::Scalar::from_canonical_u64(1 << 32)), + ); + } + for i in POSEIDON_DIGEST..POSEIDON_SPONGE_WIDTH { + yield_constr.constraint(state[i] - lv.output_partial[i - POSEIDON_DIGEST]) + } + + // Ensure that the output limbs are written in canonical form. + for i in 0..POSEIDON_DIGEST { + let constr = ((lv.digest[2 * i + 1] - P::Scalar::from_canonical_u32(u32::MAX)) + * lv.pinv[i] + - P::ONES) + * lv.digest[2 * i]; + yield_constr.constraint(constr); + } + } + + fn eval_ext_circuit( + &self, + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + vars: &Self::EvaluationFrameTarget, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + let lv: &[ExtensionTarget; NUM_COLUMNS] = vars.get_local_values().try_into().unwrap(); + let lv: &PoseidonColumnsView> = lv.borrow(); + let nv: &[ExtensionTarget; NUM_COLUMNS] = vars.get_next_values().try_into().unwrap(); + let nv: &PoseidonColumnsView> = nv.borrow(); + + //Each flag (full-input block, final block or implied dummy flag) must be + //boolean. + let is_full_input_block = lv.is_full_input_block; + let constr = builder.mul_sub_extension( + is_full_input_block, + is_full_input_block, + is_full_input_block, + ); + yield_constr.constraint(builder, constr); + + let is_final_block = builder.add_many_extension(lv.is_final_input_len); + let constr = builder.mul_sub_extension(is_final_block, is_final_block, is_final_block); + yield_constr.constraint(builder, constr); + + for &is_final_len in lv.is_final_input_len.iter() { + let constr = builder.mul_sub_extension(is_final_len, is_final_len, is_final_len); + yield_constr.constraint(builder, constr); + } + + let one = builder.one_extension(); + let is_first_row_general_op = lv.is_first_row_general_op; + let constr = builder.mul_sub_extension( + is_first_row_general_op, + is_first_row_general_op, + is_first_row_general_op, + ); + yield_constr.constraint(builder, constr); + + // Ensure that full-input block and final block flags are not set to 1 at the + // same time. + let constr = builder.mul_extension(is_final_block, is_full_input_block); + yield_constr.constraint(builder, constr); + + // If this is the first row, the original sponge state should have the input in + // the first `POSEIDON_SPONGE_RATE` elements followed by 0 for the + // capacity elements. Also, already_absorbed_elements = 0. + let already_absorbed_elements = lv.already_absorbed_elements; + yield_constr.constraint_first_row(builder, already_absorbed_elements); + + for i in POSEIDON_SPONGE_RATE..POSEIDON_SPONGE_WIDTH { + let constr = builder.mul_extension(lv.input[i], lv.len); + yield_constr.constraint_first_row(builder, constr); + } + + // If this is a final row and there is an upcoming operation, then + // we make the previous checks for next row's `already_absorbed_elements` + // and the original sponge state. + let constr = builder.mul_extension(is_final_block, nv.already_absorbed_elements); + yield_constr.constraint_transition(builder, constr); + + for i in POSEIDON_SPONGE_RATE..POSEIDON_SPONGE_WIDTH { + let mut constr = builder.mul_extension(is_final_block, nv.input[i]); + constr = builder.mul_extension(constr, nv.len); + yield_constr.constraint_transition(builder, constr); + } + + // If this is a full-input block, the next row's address, + // time and len must match as well as its timestamp. + let mut constr = builder.sub_extension(lv.context, nv.context); + constr = builder.mul_extension(is_full_input_block, constr); + yield_constr.constraint_transition(builder, constr); + let mut constr = builder.sub_extension(lv.segment, nv.segment); + constr = builder.mul_extension(is_full_input_block, constr); + yield_constr.constraint_transition(builder, constr); + let mut constr = builder.sub_extension(lv.virt, nv.virt); + constr = builder.mul_extension(is_full_input_block, constr); + yield_constr.constraint_transition(builder, constr); + let mut constr = builder.sub_extension(lv.timestamp, nv.timestamp); + constr = builder.mul_extension(is_full_input_block, constr); + yield_constr.constraint_transition(builder, constr); + + // If this is a full-input block, the next row's already_absorbed_elements + // should be ours plus `POSEIDON_SPONGE_RATE`, and the next input's + // capacity is the current output's capacity. + let diff = builder.sub_extension(already_absorbed_elements, nv.already_absorbed_elements); + let constr = builder.arithmetic_extension( + F::ONE, + F::from_canonical_usize(FELT_MAX_BYTES * POSEIDON_SPONGE_RATE), + diff, + is_full_input_block, + is_full_input_block, + ); + yield_constr.constraint_transition(builder, constr); + + for i in 0..POSEIDON_SPONGE_WIDTH - POSEIDON_SPONGE_RATE { + let mut constr = builder.mul_const_add_extension( + F::from_canonical_u64(1 << 32), + lv.digest[2 * i + 1], + lv.digest[2 * i], + ); + constr = builder.sub_extension(constr, nv.input[POSEIDON_SPONGE_RATE + i]); + constr = builder.mul_extension(is_full_input_block, constr); + yield_constr.constraint_transition(builder, constr); + } + + // A dummy row is always followed by another dummy row, so the prover can't put + // dummy rows "in between" to avoid the above checks. + let mut is_dummy = builder.add_extension(is_full_input_block, is_final_block); + is_dummy = builder.sub_extension(one, is_dummy); + let next_is_final_block = builder.add_many_extension(nv.is_final_input_len.iter()); + let mut constr = builder.add_extension(nv.is_full_input_block, next_is_final_block); + constr = builder.mul_extension(is_dummy, constr); + yield_constr.constraint_transition(builder, constr); + + // If len > 0 and this is a final block, is_final_input_len implies `len - + // already_absorbed == i` + let offset = builder.sub_extension(lv.len, already_absorbed_elements); + for (i, &is_final_len) in lv.is_final_input_len.iter().enumerate() { + let index = builder.constant_extension( + F::from_canonical_usize(FELT_MAX_BYTES * POSEIDON_SPONGE_RATE - i).into(), + ); + let entry_match = builder.sub_extension(offset, index); + let mut constr = builder.mul_extension(is_final_len, entry_match); + constr = builder.mul_extension(constr, lv.len); + yield_constr.constraint(builder, constr); + } + + // Compute the input layer. We assume that, when necessary, + // input values were previously swapped before being passed + // to Poseidon. + let mut state = lv.input; + + let mut round_ctr = 0; + + // First set of full rounds. + for r in 0..HALF_N_FULL_ROUNDS { + ::constant_layer_circuit(builder, &mut state, round_ctr); + for i in 0..POSEIDON_SPONGE_WIDTH { + if r != 0 { + let sbox_in = lv.full_sbox_0[reg_full_sbox_0(r, i)]; + let constr = builder.sub_extension(state[i], sbox_in); + yield_constr.constraint(builder, constr); + state[i] = sbox_in; + } + + // Check that the powers were correctly generated. + let cube = builder.mul_many_extension([state[i], state[i], state[i]]); + let constr = builder.sub_extension(cube, lv.cubed_full[reg_cubed_full(r, i)]); + yield_constr.constraint(builder, constr); + + // Update the i'th element of the state. + state[i] = builder.mul_many_extension([ + state[i], + lv.cubed_full[reg_cubed_full(r, i)], + lv.cubed_full[reg_cubed_full(r, i)], + ]); + } + + state = ::mds_layer_circuit(builder, &state); + round_ctr += 1; + } + + // Partial rounds. + ::partial_first_constant_layer_circuit(builder, &mut state); + state = ::mds_partial_layer_init_circuit(builder, &state); + for r in 0..(N_PARTIAL_ROUNDS - 1) { + let sbox_in = lv.partial_sbox[reg_partial_sbox(r)]; + let constr = builder.sub_extension(state[0], sbox_in); + yield_constr.constraint(builder, constr); + state[0] = sbox_in; + + // Check that the powers were generated correctly. + let cube = builder.mul_many_extension([state[0], state[0], state[0]]); + let constr = builder.sub_extension(cube, lv.cubed_partial[reg_cubed_partial(r)]); + yield_constr.constraint(builder, constr); + + // Update state[0]. + state[0] = builder.mul_many_extension([ + lv.cubed_partial[reg_cubed_partial(r)], + lv.cubed_partial[reg_cubed_partial(r)], + sbox_in, + ]); + state[0] = builder.add_const_extension( + state[0], + F::from_canonical_u64(::FAST_PARTIAL_ROUND_CONSTANTS[r]), + ); + state = ::mds_partial_layer_fast_circuit(builder, &state, r); + } + let sbox_in = lv.partial_sbox[reg_partial_sbox(N_PARTIAL_ROUNDS - 1)]; + let constr = builder.sub_extension(state[0], sbox_in); + yield_constr.constraint(builder, constr); + state[0] = sbox_in; + + // Check that the powers were generated correctly. + let mut constr = builder.mul_many_extension([state[0], state[0], state[0]]); + constr = builder.sub_extension( + constr, + lv.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)], + ); + yield_constr.constraint(builder, constr); + + state[0] = builder.mul_many_extension([ + lv.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)], + lv.cubed_partial[reg_cubed_partial(N_PARTIAL_ROUNDS - 1)], + sbox_in, + ]); + state = + ::mds_partial_layer_fast_circuit(builder, &state, N_PARTIAL_ROUNDS - 1); + round_ctr += N_PARTIAL_ROUNDS; + + // Second set of full rounds. + for r in 0..HALF_N_FULL_ROUNDS { + ::constant_layer_circuit(builder, &mut state, round_ctr); + for i in 0..POSEIDON_SPONGE_WIDTH { + let sbox_in = lv.full_sbox_1[reg_full_sbox_1(r, i)]; + let constr = builder.sub_extension(state[i], sbox_in); + yield_constr.constraint(builder, constr); + state[i] = sbox_in; + + // Check that the powers were correctly generated. + let mut constr = builder.mul_many_extension([state[i], state[i], state[i]]); + constr = builder.sub_extension( + constr, + lv.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)], + ); + yield_constr.constraint(builder, constr); + + // Update the i'th element of the state. + state[i] = builder.mul_many_extension([ + lv.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)], + lv.cubed_full[reg_cubed_full(HALF_N_FULL_ROUNDS + r, i)], + state[i], + ]); + } + + state = ::mds_layer_circuit(builder, &state); + round_ctr += 1; + } + + for i in 0..POSEIDON_DIGEST { + let val = builder.mul_const_add_extension( + F::from_canonical_u64(1 << 32), + lv.digest[2 * i + 1], + lv.digest[2 * i], + ); + let constr = builder.sub_extension(state[i], val); + yield_constr.constraint(builder, constr); + } + for i in POSEIDON_DIGEST..POSEIDON_SPONGE_WIDTH { + let constr = builder.sub_extension(state[i], lv.output_partial[i - POSEIDON_DIGEST]); + yield_constr.constraint(builder, constr); + } + + // Ensure that the output limbs are written in canonical form. + for i in 0..POSEIDON_DIGEST { + let mut constr = builder.arithmetic_extension( + F::ONE, + F::NEG_ONE * F::from_canonical_u32(u32::MAX), + lv.digest[2 * i + 1], + lv.pinv[i], + lv.pinv[i], + ); + constr = builder.mul_sub_extension(lv.digest[2 * i], constr, lv.digest[2 * i]); + + yield_constr.constraint(builder, constr); + } + } + + fn constraint_degree(&self) -> usize { + 3 + } + + fn requires_ctls(&self) -> bool { + true + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::{ + field::{goldilocks_field::GoldilocksField, types::PrimeField64}, + plonk::config::{GenericConfig, PoseidonGoldilocksConfig}, + }; + use smt_trie::code::poseidon_hash_padded_byte_vec; + use starky::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; + + use super::*; + + #[test] + fn test_stark_degree() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = PoseidonStark; + + let stark = S { + f: Default::default(), + }; + test_stark_low_degree(stark) + } + + #[test] + fn test_stark_circuit() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = PoseidonStark; + + let stark = S { + f: Default::default(), + }; + test_stark_circuit_constraints::(stark) + } + + #[test] + fn poseidon_correctness_test() -> Result<()> { + let input: Vec = (0..POSEIDON_SPONGE_RATE * FELT_MAX_BYTES) + .map(|_| rand::random()) + .collect(); + let int_inputs = PoseidonOp::PoseidonGeneralOp(PoseidonGeneralOp { + base_address: MemoryAddress::new( + 0, + crate::memory::segments::Segment::AccessedAddresses, + 0, + ), + input: input.clone(), + timestamp: 0, + len: POSEIDON_SPONGE_RATE * FELT_MAX_BYTES, + }); + const D: usize = 2; + type F = GoldilocksField; + type S = PoseidonStark; + + let stark = S { + f: Default::default(), + }; + + let rows = stark.generate_trace_rows(vec![int_inputs], 8); + assert_eq!(rows.len(), 8); + let last_row: &PoseidonColumnsView<_> = rows[0].borrow(); + let output: Vec<_> = (0..POSEIDON_DIGEST) + .map(|i| { + last_row.digest[2 * i] + F::from_canonical_u64(1 << 32) * last_row.digest[2 * i + 1] + }) + .collect(); + + let hash = poseidon_hash_padded_byte_vec(input); + + assert_eq!( + output + .iter() + .map(|x| x.to_noncanonical_u64()) + .collect::>(), + hash.elements + .iter() + .map(|x| x.to_noncanonical_u64()) + .collect::>() + ); + + Ok(()) + } +} diff --git a/evm_arithmetization/src/prover.rs b/evm_arithmetization/src/prover.rs index f6c40cf89..ba5d474ee 100644 --- a/evm_arithmetization/src/prover.rs +++ b/evm_arithmetization/src/prover.rs @@ -296,6 +296,21 @@ where ctl_challenges, challenger, timing, + abort_signal.clone(), + )? + ); + let poseidon_proof = timed!( + timing, + "prove memory STARK", + prove_single_table( + &all_stark.poseidon_stark, + config, + &trace_poly_values[Table::Poseidon as usize], + &trace_commitments[Table::Poseidon as usize], + &ctl_data_per_table[Table::Poseidon as usize], + ctl_challenges, + challenger, + timing, abort_signal, )? ); @@ -308,6 +323,7 @@ where keccak_sponge_proof, logic_proof, memory_proof, + poseidon_proof, ]) } diff --git a/evm_arithmetization/src/testing_utils.rs b/evm_arithmetization/src/testing_utils.rs index bb1f07b64..2cf5d61a9 100644 --- a/evm_arithmetization/src/testing_utils.rs +++ b/evm_arithmetization/src/testing_utils.rs @@ -1,20 +1,30 @@ //! A set of utility functions and constants to be used by `evm_arithmetization` //! unit and integration tests. +use std::collections::HashMap; + use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use ethereum_types::{BigEndianHash, H256, U256}; +use ethereum_types::{Address, H160, H256, U256}; use hex_literal::hex; use keccak_hash::keccak; -use mpt_trie::{ - nibbles::Nibbles, - partial_trie::{HashedPartialTrie, Node, PartialTrie}, +use smt_trie::{ + db::{Db, MemoryDb}, + keys::{key_balance, key_code, key_code_length, key_nonce, key_storage}, + smt::Smt, }; pub use crate::cpu::kernel::cancun_constants::*; pub use crate::cpu::kernel::constants::global_exit_root::{ - GLOBAL_EXIT_ROOT_ACCOUNT, GLOBAL_EXIT_ROOT_ADDRESS_HASHED, GLOBAL_EXIT_ROOT_STORAGE_POS, + GLOBAL_EXIT_ROOT_ADDRESS_HASHED, GLOBAL_EXIT_ROOT_STORAGE_POS, +}; +use crate::{ + cpu::kernel::global_exit_root::{ + global_exit_root_account, GLOBAL_EXIT_ROOT_MANAGER_L2_STATE_KEY, + }, + generation::mpt::AccountRlp, + proof::BlockMetadata, + util::h2u, }; -use crate::{generation::mpt::AccountRlp, util::h2u}; pub const EMPTY_NODE_HASH: H256 = H256(hex!( "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" @@ -35,104 +45,91 @@ pub fn sh2u(s: &str) -> U256 { } /// Inserts a new pair `(slot, value)` into the provided storage trie. -fn insert_storage(trie: &mut HashedPartialTrie, slot: U256, value: U256) -> anyhow::Result<()> { - let mut bytes = [0; 32]; - slot.to_big_endian(&mut bytes); - let key = keccak(bytes); - let nibbles = Nibbles::from_bytes_be(key.as_bytes()).unwrap(); - if value.is_zero() { - trie.delete(nibbles)?; - } else { - let r = rlp::encode(&value); - trie.insert(nibbles, r.freeze().to_vec())?; - } - Ok(()) -} - -/// Creates a storage trie for an account, given a list of `(slot, value)` -/// pairs. -pub fn create_account_storage(storage_pairs: &[(U256, U256)]) -> anyhow::Result { - let mut trie = HashedPartialTrie::from(Node::Empty); - for (slot, value) in storage_pairs { - insert_storage(&mut trie, *slot, *value)?; - } - Ok(trie) +fn insert_storage(smt: &mut Smt, addr: Address, slot: U256, value: U256) { + smt.set(key_storage(addr, slot), value); } /// Updates the beacon roots account storage with the provided timestamp and /// block parent root. pub fn update_beacon_roots_account_storage( - storage_trie: &mut HashedPartialTrie, + state_smt: &mut Smt, timestamp: U256, parent_root: H256, -) -> anyhow::Result<()> { +) { let timestamp_idx = timestamp % HISTORY_BUFFER_LENGTH.1; let root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH.1; - insert_storage(storage_trie, timestamp_idx, timestamp)?; - insert_storage(storage_trie, root_idx, h2u(parent_root)) + insert_storage( + state_smt, + H160(BEACON_ROOTS_CONTRACT_STATE_KEY.1), + timestamp_idx, + timestamp, + ); + insert_storage( + state_smt, + H160(BEACON_ROOTS_CONTRACT_STATE_KEY.1), + root_idx, + h2u(parent_root), + ); } -/// Returns the beacon roots contract account from its provided storage trie. -pub fn beacon_roots_contract_from_storage(storage_trie: &HashedPartialTrie) -> AccountRlp { - AccountRlp { - storage_root: storage_trie.hash(), - ..BEACON_ROOTS_ACCOUNT - } +/// Calculates the beacon roots account storage with the provided timestamp and +/// block parent root. +pub fn compute_beacon_roots_account_storage( + timestamp: U256, + parent_root: H256, +) -> HashMap { + let timestamp_idx = timestamp % HISTORY_BUFFER_LENGTH.1; + let root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH.1; + + HashMap::from([(timestamp_idx, timestamp), (root_idx, h2u(parent_root))]) } /// Returns an initial state trie containing the beacon roots and global exit /// roots contracts, along with their storage tries. -pub fn preinitialized_state_and_storage_tries( -) -> anyhow::Result<(HashedPartialTrie, Vec<(H256, HashedPartialTrie)>)> { - let mut state_trie = HashedPartialTrie::from(Node::Empty); - state_trie.insert( - beacon_roots_account_nibbles(), - rlp::encode(&BEACON_ROOTS_ACCOUNT).to_vec(), - )?; - state_trie.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; - - let storage_tries = vec![ - ( - H256(BEACON_ROOTS_CONTRACT_ADDRESS_HASHED), - Node::Empty.into(), - ), - (H256(GLOBAL_EXIT_ROOT_ADDRESS_HASHED), Node::Empty.into()), - ]; - - Ok((state_trie, storage_tries)) -} +pub fn preinitialized_state() -> Smt { + let mut smt = Smt::::default(); + set_beacon_roots_account(&mut smt, &HashMap::new()); + set_global_exit_root_account(&mut smt, &HashMap::new()); -/// Returns the `Nibbles` corresponding to the beacon roots contract account. -pub fn beacon_roots_account_nibbles() -> Nibbles { - Nibbles::from_bytes_be(&BEACON_ROOTS_CONTRACT_ADDRESS_HASHED).unwrap() + smt } -/// Returns the `Nibbles` corresponding to the beacon roots contract account. -pub fn ger_account_nibbles() -> Nibbles { - Nibbles::from_bytes_be(&GLOBAL_EXIT_ROOT_ADDRESS_HASHED).unwrap() +/// Returns a final state trie containing the beacon roots and global exit +/// roots contracts, with their updated storage. +pub fn preinitialized_state_with_updated_storage( + block: &BlockMetadata, + global_exit_roots: &[(U256, H256)], +) -> Smt { + let mut smt = Smt::::default(); + let beacon_roots_storage = + compute_beacon_roots_account_storage(block.block_timestamp, block.parent_beacon_block_root); + set_beacon_roots_account(&mut smt, &beacon_roots_storage); + + let mut storage = HashMap::new(); + for &(timestamp, root) in global_exit_roots { + storage.insert(ger_storage_slot(root), timestamp); + } + set_global_exit_root_account(&mut smt, &storage); + + smt } -pub fn update_ger_account_storage( - storage_trie: &mut HashedPartialTrie, - root: H256, - timestamp: U256, -) -> anyhow::Result<()> { +fn ger_storage_slot(root: H256) -> U256 { let mut arr = [0; 64]; arr[0..32].copy_from_slice(&root.0); U256::from(GLOBAL_EXIT_ROOT_STORAGE_POS.1).to_big_endian(&mut arr[32..64]); let slot = keccak(arr); - insert_storage(storage_trie, slot.into_uint(), timestamp) + h2u(slot) } -pub fn ger_contract_from_storage(storage_trie: &HashedPartialTrie) -> AccountRlp { - AccountRlp { - storage_root: storage_trie.hash(), - ..GLOBAL_EXIT_ROOT_ACCOUNT - } +pub fn update_ger_account_storage(state_smt: &mut Smt, root: H256, timestamp: U256) { + insert_storage( + state_smt, + H160(GLOBAL_EXIT_ROOT_MANAGER_L2_STATE_KEY.1), + ger_storage_slot(root), + timestamp, + ); } /// Converts an amount in `ETH` to `wei` units. @@ -140,3 +137,36 @@ pub fn eth_to_wei(eth: U256) -> U256 { // 1 ether = 10^18 wei. eth * U256::from(10).pow(18.into()) } + +pub fn set_account( + smt: &mut Smt, + addr: Address, + account: &AccountRlp, + storage: &HashMap, +) { + smt.set(key_balance(addr), account.balance); + smt.set(key_nonce(addr), account.nonce); + smt.set(key_code(addr), account.code_hash); + smt.set(key_code_length(addr), account.code_length); + for (&k, &v) in storage { + smt.set(key_storage(addr, k), v); + } +} + +pub fn set_beacon_roots_account(smt: &mut Smt, storage: &HashMap) { + set_account( + smt, + H160(BEACON_ROOTS_CONTRACT_STATE_KEY.1), + &beacon_roots_account(), + storage, + ); +} + +pub fn set_global_exit_root_account(smt: &mut Smt, storage: &HashMap) { + set_account( + smt, + H160(GLOBAL_EXIT_ROOT_MANAGER_L2_STATE_KEY.1), + &global_exit_root_account(), + storage, + ); +} diff --git a/evm_arithmetization/src/verifier.rs b/evm_arithmetization/src/verifier.rs index 90a287f1e..25b8cfeab 100644 --- a/evm_arithmetization/src/verifier.rs +++ b/evm_arithmetization/src/verifier.rs @@ -42,6 +42,7 @@ where keccak_sponge_stark, logic_stark, memory_stark, + poseidon_stark, cross_table_lookups, } = all_stark; @@ -112,6 +113,14 @@ where &[], config, )?; + verify_stark_proof_with_challenges( + poseidon_stark, + &stark_proofs[Table::Poseidon as usize].proof, + &stark_challenges[Table::Poseidon as usize], + Some(&ctl_vars_per_table[Table::Poseidon as usize]), + &[], + config, + )?; let public_values = all_proof.public_values; diff --git a/evm_arithmetization/src/witness/gas.rs b/evm_arithmetization/src/witness/gas.rs index 54597a3eb..60fbf0846 100644 --- a/evm_arithmetization/src/witness/gas.rs +++ b/evm_arithmetization/src/witness/gas.rs @@ -35,6 +35,8 @@ pub(crate) const fn gas_to_charge(op: Operation) -> u64 { TernaryArithmetic(MulMod) => G_MID, TernaryArithmetic(SubMod) => KERNEL_ONLY_INSTR, KeccakGeneral => KERNEL_ONLY_INSTR, + Poseidon => KERNEL_ONLY_INSTR, + PoseidonGeneral => KERNEL_ONLY_INSTR, ProverInput => KERNEL_ONLY_INSTR, Pop => G_BASE, Jump => G_MID, diff --git a/evm_arithmetization/src/witness/operation.rs b/evm_arithmetization/src/witness/operation.rs index ac884c74a..e5ff04980 100644 --- a/evm_arithmetization/src/witness/operation.rs +++ b/evm_arithmetization/src/witness/operation.rs @@ -1,7 +1,9 @@ use ethereum_types::{BigEndianHash, U256}; use itertools::Itertools; use keccak_hash::keccak; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; +use smt_trie::code::poseidon_hash_padded_byte_vec; +use smt_trie::utils::hashout2u; use super::state::KERNEL_CONTEXT; use super::transition::Transition; @@ -14,10 +16,12 @@ use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::assembler::BYTES_PER_OFFSET; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; +use crate::cpu::membus::NUM_CHANNELS; use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff; use crate::cpu::stack::MAX_USER_STACK_SIZE; use crate::extension_tower::BN_BASE; use crate::memory::segments::Segment; +use crate::poseidon::poseidon_stark::{PoseidonGeneralOp, PoseidonOp, PoseidonSimpleOp}; use crate::util::u256_to_usize; use crate::witness::errors::MemoryError::VirtTooLarge; use crate::witness::errors::ProgramError; @@ -40,6 +44,8 @@ pub(crate) enum Operation { BinaryArithmetic(arithmetic::BinaryOperator), TernaryArithmetic(arithmetic::TernaryOperator), KeccakGeneral, + Poseidon, + PoseidonGeneral, ProverInput, Pop, Jump, @@ -66,7 +72,7 @@ pub(crate) const CONTEXT_SCALING_FACTOR: usize = 64; /// operation. Generates a new logic operation and adds it to the vector of /// operation in `LogicStark`. Adds three memory read operations to /// `MemoryStark`: for the two inputs and the output. -pub(crate) fn generate_binary_logic_op>( +pub(crate) fn generate_binary_logic_op>( op: logic::Op, state: &mut T, mut row: CpuColumnsView, @@ -84,7 +90,7 @@ pub(crate) fn generate_binary_logic_op>( Ok(()) } -pub(crate) fn generate_binary_arithmetic_op>( +pub(crate) fn generate_binary_arithmetic_op>( operator: arithmetic::BinaryOperator, state: &mut T, mut row: CpuColumnsView, @@ -115,7 +121,7 @@ pub(crate) fn generate_binary_arithmetic_op>( Ok(()) } -pub(crate) fn generate_ternary_arithmetic_op>( +pub(crate) fn generate_ternary_arithmetic_op>( operator: arithmetic::TernaryOperator, state: &mut T, mut row: CpuColumnsView, @@ -134,7 +140,7 @@ pub(crate) fn generate_ternary_arithmetic_op>( Ok(()) } -pub(crate) fn generate_keccak_general>( +pub(crate) fn generate_keccak_general>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -167,7 +173,84 @@ pub(crate) fn generate_keccak_general>( Ok(()) } -pub(crate) fn generate_prover_input>( +/// Pops 3 elements `x,y,z` from the stack, and returns `Poseidon(x || y || +/// z)[0..4]`, where values are split into 64-bit limbs, and `z` is used as the +/// capacity. Limbs are range-checked to be in canonical form in the +/// PoseidonStark. +pub(crate) fn generate_poseidon>( + state: &mut T, + mut row: CpuColumnsView, +) -> Result<(), ProgramError> { + let generation_state = state.get_mut_generation_state(); + let [(x, _), (y, log_in1), (z, log_in2)] = + stack_pop_with_log_and_fill::<3, _>(generation_state, &mut row)?; + let arr = [ + x.0[0], x.0[1], x.0[2], x.0[3], y.0[0], y.0[1], y.0[2], y.0[3], z.0[0], z.0[1], z.0[2], + z.0[3], + ] + .map(F::from_canonical_u64); + let hash = F::poseidon(arr); + let hash = U256(std::array::from_fn(|i| hash[i].to_canonical_u64())); + log::debug!("Poseidon hashing {:?} -> {}", arr, hash); + push_no_write(generation_state, hash); + + state.push_poseidon(PoseidonOp::PoseidonSimpleOp(PoseidonSimpleOp(arr))); + + state.push_memory(log_in1); + state.push_memory(log_in2); + state.push_cpu(row); + Ok(()) +} + +pub(crate) fn generate_poseidon_general>( + state: &mut T, + mut row: CpuColumnsView, +) -> Result<(), ProgramError> { + let clock = state.get_clock(); + let generation_state = state.get_mut_generation_state(); + let [(addr, _), (len, log_in1)] = + stack_pop_with_log_and_fill::<2, _>(generation_state, &mut row)?; + let len = u256_to_usize(len)?; + + let base_address = MemoryAddress::new_bundle(addr)?; + let input = (0..len) + .map(|i| { + let address = MemoryAddress { + virt: base_address.virt.saturating_add(i), + ..base_address + }; + let val = generation_state.memory.get_with_init(address); + generation_state.traces.memory_ops.push(MemoryOp::new( + MemoryChannel::Code, + clock, + address, + MemoryOpKind::Read, + val.0[0].into(), + )); + + val.0[0] as u8 + }) + .collect_vec(); + + let poseidon_op = PoseidonOp::PoseidonGeneralOp(PoseidonGeneralOp { + base_address, + timestamp: clock * NUM_CHANNELS, + input: input.clone(), + len: input.len(), + }); + + let hash = hashout2u(poseidon_hash_padded_byte_vec(input.clone())); + + push_no_write(generation_state, hash); + + state.push_poseidon(poseidon_op); + + state.push_memory(log_in1); + state.push_cpu(row); + Ok(()) +} + +pub(crate) fn generate_prover_input>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -195,7 +278,7 @@ pub(crate) fn generate_prover_input>( Ok(()) } -pub(crate) fn generate_pop>( +pub(crate) fn generate_pop>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -218,7 +301,7 @@ pub(crate) fn generate_pop>( Ok(()) } -pub(crate) fn generate_pc>( +pub(crate) fn generate_pc>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -231,7 +314,7 @@ pub(crate) fn generate_pc>( Ok(()) } -pub(crate) fn generate_jumpdest>( +pub(crate) fn generate_jumpdest>( state: &mut T, row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -239,7 +322,7 @@ pub(crate) fn generate_jumpdest>( Ok(()) } -pub(crate) fn generate_get_context>( +pub(crate) fn generate_get_context>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -275,7 +358,7 @@ pub(crate) fn generate_get_context>( Ok(()) } -pub(crate) fn generate_set_context>( +pub(crate) fn generate_set_context>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -355,7 +438,7 @@ pub(crate) fn generate_set_context>( Ok(()) } -pub(crate) fn generate_push>( +pub(crate) fn generate_push>( n: u8, state: &mut T, mut row: CpuColumnsView, @@ -406,7 +489,7 @@ pub(crate) fn generate_push>( // - Update `stack_top` with `val` and add 1 to `stack_len` // Since the write must happen before the read, the normal way of assigning // GP channels doesn't work and we must handle them manually. -pub(crate) fn generate_dup>( +pub(crate) fn generate_dup>( n: u8, state: &mut T, mut row: CpuColumnsView, @@ -474,7 +557,7 @@ pub(crate) fn generate_dup>( Ok(()) } -pub(crate) fn generate_swap>( +pub(crate) fn generate_swap>( n: u8, state: &mut T, mut row: CpuColumnsView, @@ -505,7 +588,7 @@ pub(crate) fn generate_swap>( Ok(()) } -pub(crate) fn generate_not>( +pub(crate) fn generate_not>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -529,7 +612,7 @@ pub(crate) fn generate_not>( Ok(()) } -pub(crate) fn generate_iszero>( +pub(crate) fn generate_iszero>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -548,7 +631,7 @@ pub(crate) fn generate_iszero>( Ok(()) } -fn append_shift>( +fn append_shift>( state: &mut T, mut row: CpuColumnsView, is_shl: bool, @@ -599,7 +682,7 @@ fn append_shift>( Ok(()) } -pub(crate) fn generate_shl>( +pub(crate) fn generate_shl>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -615,7 +698,7 @@ pub(crate) fn generate_shl>( append_shift(state, row, true, input0, input1, log_in1, result) } -pub(crate) fn generate_shr>( +pub(crate) fn generate_shr>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -630,7 +713,7 @@ pub(crate) fn generate_shr>( append_shift(state, row, false, input0, input1, log_in1, result) } -pub(crate) fn generate_syscall>( +pub(crate) fn generate_syscall>( opcode: u8, stack_values_read: usize, stack_len_increased: bool, @@ -723,7 +806,7 @@ pub(crate) fn generate_syscall>( Ok(()) } -pub(crate) fn generate_eq>( +pub(crate) fn generate_eq>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -741,7 +824,7 @@ pub(crate) fn generate_eq>( Ok(()) } -pub(crate) fn generate_exit_kernel>( +pub(crate) fn generate_exit_kernel>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -770,7 +853,7 @@ pub(crate) fn generate_exit_kernel>( Ok(()) } -pub(crate) fn generate_mload_general>( +pub(crate) fn generate_mload_general>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -803,7 +886,7 @@ pub(crate) fn generate_mload_general>( Ok(()) } -pub(crate) fn generate_mload_32bytes>( +pub(crate) fn generate_mload_32bytes>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -843,7 +926,7 @@ pub(crate) fn generate_mload_32bytes>( Ok(()) } -pub(crate) fn generate_mstore_general>( +pub(crate) fn generate_mstore_general>( state: &mut T, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { @@ -873,7 +956,7 @@ pub(crate) fn generate_mstore_general>( Ok(()) } -pub(crate) fn generate_mstore_32bytes>( +pub(crate) fn generate_mstore_32bytes>( n: u8, state: &mut T, mut row: CpuColumnsView, @@ -893,7 +976,7 @@ pub(crate) fn generate_mstore_32bytes>( Ok(()) } -pub(crate) fn generate_exception>( +pub(crate) fn generate_exception>( exc_code: u8, state: &mut T, mut row: CpuColumnsView, diff --git a/evm_arithmetization/src/witness/traces.rs b/evm_arithmetization/src/witness/traces.rs index e70f36824..b538e185b 100644 --- a/evm_arithmetization/src/witness/traces.rs +++ b/evm_arithmetization/src/witness/traces.rs @@ -1,5 +1,6 @@ use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; +use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -9,8 +10,9 @@ use starky::util::trace_rows_to_poly_values; use crate::all_stark::{AllStark, NUM_TABLES}; use crate::arithmetic::{BinaryOperator, Operation}; use crate::byte_packing::byte_packing_stark::BytePackingOp; -use crate::cpu::columns::CpuColumnsView; +use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS}; use crate::keccak_sponge::keccak_sponge_stark::KeccakSpongeOp; +use crate::poseidon::poseidon_stark::PoseidonOp; use crate::witness::memory::MemoryOp; use crate::{arithmetic, keccak, keccak_sponge, logic}; @@ -23,10 +25,11 @@ pub(crate) struct TraceCheckpoint { pub(self) keccak_sponge_len: usize, pub(self) logic_len: usize, pub(self) memory_len: usize, + pub(self) poseidon_len: usize, } #[derive(Debug)] -pub(crate) struct Traces { +pub(crate) struct Traces { pub(crate) arithmetic_ops: Vec, pub(crate) byte_packing_ops: Vec, pub(crate) cpu: Vec>, @@ -34,9 +37,10 @@ pub(crate) struct Traces { pub(crate) memory_ops: Vec, pub(crate) keccak_inputs: Vec<([u64; keccak::keccak_stark::NUM_INPUTS], usize)>, pub(crate) keccak_sponge_ops: Vec, + pub(crate) poseidon_ops: Vec>, } -impl Traces { +impl Traces { pub(crate) const fn new() -> Self { Traces { arithmetic_ops: vec![], @@ -46,6 +50,7 @@ impl Traces { memory_ops: vec![], keccak_inputs: vec![], keccak_sponge_ops: vec![], + poseidon_ops: vec![], } } @@ -77,6 +82,7 @@ impl Traces { // This is technically a lower-bound, as we may fill gaps, // but this gives a relatively good estimate. memory_len: self.memory_ops.len(), + poseidon_len: self.poseidon_ops.len(), } } @@ -90,6 +96,7 @@ impl Traces { keccak_sponge_len: self.keccak_sponge_ops.len(), logic_len: self.logic_ops.len(), memory_len: self.memory_ops.len(), + poseidon_len: self.poseidon_ops.len(), } } @@ -102,6 +109,7 @@ impl Traces { .truncate(checkpoint.keccak_sponge_len); self.logic_ops.truncate(checkpoint.logic_len); self.memory_ops.truncate(checkpoint.memory_len); + self.poseidon_ops.truncate(checkpoint.poseidon_len); } pub(crate) fn mem_ops_since(&self, checkpoint: TraceCheckpoint) -> &[MemoryOp] { @@ -130,6 +138,7 @@ impl Traces { memory_ops, keccak_inputs, keccak_sponge_ops, + poseidon_ops, } = self; let arithmetic_trace = timed!( @@ -144,7 +153,8 @@ impl Traces { .byte_packing_stark .generate_trace(byte_packing_ops, cap_elements, timing) ); - let cpu_rows = cpu.into_iter().map(|x| x.into()).collect(); + let cpu_rows: Vec<[T; NUM_CPU_COLUMNS]> = cpu.into_iter().map(|x| x.into()).collect(); + let cpu_trace = trace_rows_to_poly_values(cpu_rows); let keccak_trace = timed!( timing, @@ -172,6 +182,13 @@ impl Traces { "generate memory trace", all_stark.memory_stark.generate_trace(memory_ops, timing) ); + let poseidon_trace = timed!( + timing, + "generate memory trace", + all_stark + .poseidon_stark + .generate_trace(poseidon_ops, cap_elements, timing) + ); [ arithmetic_trace, @@ -181,11 +198,12 @@ impl Traces { keccak_sponge_trace, logic_trace, memory_trace, + poseidon_trace, ] } } -impl Default for Traces { +impl Default for Traces { fn default() -> Self { Self::new() } diff --git a/evm_arithmetization/src/witness/transition.rs b/evm_arithmetization/src/witness/transition.rs index 71d4632bb..09412c347 100644 --- a/evm_arithmetization/src/witness/transition.rs +++ b/evm_arithmetization/src/witness/transition.rs @@ -1,6 +1,7 @@ use ethereum_types::U256; use log::log_enabled; use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use super::util::{mem_read_gp_with_log_and_fill, stack_pop_with_log_and_fill}; use crate::cpu::columns::CpuColumnsView; @@ -20,7 +21,7 @@ use crate::witness::state::RegistersState; use crate::witness::util::mem_read_code_with_log_and_fill; use crate::{arithmetic, logic}; -pub(crate) fn read_code_memory>( +pub(crate) fn read_code_memory>( state: &mut T, row: &mut CpuColumnsView, ) -> u8 { @@ -88,6 +89,8 @@ pub(crate) fn decode(registers: RegistersState, opcode: u8) -> Result Ok(Operation::Syscall(opcode, 2, false)), // SAR (0x20, _) => Ok(Operation::Syscall(opcode, 2, false)), // KECCAK256 (0x21, true) => Ok(Operation::KeccakGeneral), + (0x22, true) => Ok(Operation::Poseidon), + (0x23, true) => Ok(Operation::PoseidonGeneral), (0x30, _) => Ok(Operation::Syscall(opcode, 0, true)), // ADDRESS (0x31, _) => Ok(Operation::Syscall(opcode, 1, false)), // BALANCE (0x32, _) => Ok(Operation::Syscall(opcode, 0, true)), // ORIGIN @@ -185,6 +188,7 @@ pub(crate) fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) Operation::BinaryArithmetic(_) => &mut flags.binary_op, Operation::TernaryArithmetic(_) => &mut flags.ternary_op, Operation::KeccakGeneral | Operation::Jumpdest => &mut flags.jumpdest_keccak_general, + Operation::Poseidon | Operation::PoseidonGeneral => &mut flags.poseidon, Operation::ProverInput | Operation::Push(1..) => &mut flags.push_prover_input, Operation::Jump | Operation::Jumpi => &mut flags.jumps, Operation::Pc | Operation::Push(0) => &mut flags.pc_push0, @@ -217,6 +221,7 @@ pub(crate) const fn get_op_special_length(op: Operation) -> Option { Operation::BinaryArithmetic(_) => STACK_BEHAVIORS.binary_op, Operation::TernaryArithmetic(_) => STACK_BEHAVIORS.ternary_op, Operation::KeccakGeneral | Operation::Jumpdest => STACK_BEHAVIORS.jumpdest_keccak_general, + Operation::Poseidon | Operation::PoseidonGeneral => STACK_BEHAVIORS.poseidon, Operation::Jump => JUMP_OP, Operation::Jumpi => JUMPI_OP, Operation::GetContext | Operation::SetContext => None, @@ -256,6 +261,7 @@ pub(crate) const fn might_overflow_op(op: Operation) -> bool { Operation::BinaryArithmetic(_) => MIGHT_OVERFLOW.binary_op, Operation::TernaryArithmetic(_) => MIGHT_OVERFLOW.ternary_op, Operation::KeccakGeneral | Operation::Jumpdest => MIGHT_OVERFLOW.jumpdest_keccak_general, + Operation::Poseidon | Operation::PoseidonGeneral => MIGHT_OVERFLOW.poseidon, Operation::Jump | Operation::Jumpi => MIGHT_OVERFLOW.jumps, Operation::Pc | Operation::Push(0) => MIGHT_OVERFLOW.pc_push0, Operation::GetContext | Operation::SetContext => MIGHT_OVERFLOW.context_op, @@ -265,7 +271,7 @@ pub(crate) const fn might_overflow_op(op: Operation) -> bool { } } -pub(crate) fn log_kernel_instruction>(state: &mut S, op: Operation) { +pub(crate) fn log_kernel_instruction>(state: &mut S, op: Operation) { // The logic below is a bit costly, so skip it if debug logs aren't enabled. if !log_enabled!(log::Level::Debug) { return; @@ -296,7 +302,7 @@ pub(crate) fn log_kernel_instruction>(state: &mut S, op: O assert!(pc < KERNEL.code.len(), "Kernel PC is out of range: {}", pc); } -pub(crate) trait Transition: State { +pub(crate) trait Transition: State { /// When in jumpdest analysis, adds the offset `dst` to the jumpdest table. /// Returns a boolean indicating whether we are running the jumpdest /// analysis. @@ -311,6 +317,7 @@ pub(crate) trait Transition: State { ) -> Result where Self: Sized, + F: RichField, { self.perform_op(op, row)?; self.incr_pc(match op { @@ -466,6 +473,7 @@ pub(crate) trait Transition: State { fn perform_op(&mut self, op: Operation, row: CpuColumnsView) -> Result<(), ProgramError> where Self: Sized, + F: RichField, { let op = self.skip_if_necessary(op)?; @@ -488,6 +496,8 @@ pub(crate) trait Transition: State { Operation::TernaryArithmetic(op) => generate_ternary_arithmetic_op(op, self, row), Operation::KeccakGeneral => generate_keccak_general(self, row), Operation::ProverInput => generate_prover_input(self, row), + Operation::Poseidon => generate_poseidon(self, row), + Operation::PoseidonGeneral => generate_poseidon_general(self, row), Operation::Pop => generate_pop(self, row), Operation::Jump => self.generate_jump(row), Operation::Jumpi => self.generate_jumpi(row), diff --git a/evm_arithmetization/src/witness/util.rs b/evm_arithmetization/src/witness/util.rs index 090a13981..808a45b15 100644 --- a/evm_arithmetization/src/witness/util.rs +++ b/evm_arithmetization/src/witness/util.rs @@ -1,5 +1,5 @@ use ethereum_types::U256; -use plonky2::field::types::Field; +use plonky2::hash::hash_types::RichField; use super::memory::DUMMY_MEMOP; use super::transition::Transition; @@ -22,7 +22,7 @@ fn to_byte_checked(n: U256) -> u8 { res } -fn to_bits_le(n: u8) -> [F; 8] { +fn to_bits_le(n: u8) -> [F; 8] { let mut res = [F::ZERO; 8]; for (i, bit) in res.iter_mut().enumerate() { *bit = F::from_bool(n & (1 << i) != 0); @@ -31,7 +31,7 @@ fn to_bits_le(n: u8) -> [F; 8] { } /// Peek at the stack item `i`th from the top. If `i=0` this gives the tip. -pub(crate) fn stack_peek( +pub(crate) fn stack_peek( state: &GenerationState, i: usize, ) -> Result { @@ -50,7 +50,7 @@ pub(crate) fn stack_peek( } /// Peek at kernel at specified segment and address -pub(crate) fn current_context_peek( +pub(crate) fn current_context_peek( state: &GenerationState, segment: Segment, virt: usize, @@ -61,7 +61,11 @@ pub(crate) fn current_context_peek( .get_with_init(MemoryAddress::new(context, segment, virt)) } -pub(crate) fn fill_channel_with_value(row: &mut CpuColumnsView, n: usize, val: U256) { +pub(crate) fn fill_channel_with_value( + row: &mut CpuColumnsView, + n: usize, + val: U256, +) { let channel = &mut row.mem_channels[n]; let val_limbs: [u64; 4] = val.0; for (i, limb) in val_limbs.into_iter().enumerate() { @@ -72,14 +76,14 @@ pub(crate) fn fill_channel_with_value(row: &mut CpuColumnsView, n: /// Pushes without writing in memory. This happens in opcodes where a push /// immediately follows a pop. -pub(crate) fn push_no_write(state: &mut GenerationState, val: U256) { +pub(crate) fn push_no_write(state: &mut GenerationState, val: U256) { state.registers.stack_top = val; state.registers.stack_len += 1; } /// Pushes and (maybe) writes the previous stack top in memory. This happens in /// opcodes which only push. -pub(crate) fn push_with_write>( +pub(crate) fn push_with_write>( state: &mut T, row: &mut CpuColumnsView, val: U256, @@ -115,7 +119,7 @@ pub(crate) fn push_with_write>( Ok(()) } -pub(crate) fn mem_read_with_log( +pub(crate) fn mem_read_with_log( channel: MemoryChannel, address: MemoryAddress, state: &GenerationState, @@ -131,7 +135,7 @@ pub(crate) fn mem_read_with_log( (val, op) } -pub(crate) fn mem_write_log( +pub(crate) fn mem_write_log( channel: MemoryChannel, address: MemoryAddress, state: &GenerationState, @@ -146,7 +150,7 @@ pub(crate) fn mem_write_log( ) } -pub(crate) fn mem_read_code_with_log_and_fill( +pub(crate) fn mem_read_code_with_log_and_fill( address: MemoryAddress, state: &GenerationState, row: &mut CpuColumnsView, @@ -159,7 +163,7 @@ pub(crate) fn mem_read_code_with_log_and_fill( (val_u8, op) } -pub(crate) fn mem_read_gp_with_log_and_fill( +pub(crate) fn mem_read_gp_with_log_and_fill( n: usize, address: MemoryAddress, state: &GenerationState, @@ -183,7 +187,7 @@ pub(crate) fn mem_read_gp_with_log_and_fill( (val, op) } -pub(crate) fn mem_write_gp_log_and_fill( +pub(crate) fn mem_write_gp_log_and_fill( n: usize, address: MemoryAddress, state: &GenerationState, @@ -208,7 +212,7 @@ pub(crate) fn mem_write_gp_log_and_fill( op } -pub(crate) fn mem_write_partial_log_and_fill( +pub(crate) fn mem_write_partial_log_and_fill( address: MemoryAddress, state: &GenerationState, row: &mut CpuColumnsView, @@ -230,7 +234,7 @@ pub(crate) fn mem_write_partial_log_and_fill( // Channel 0 already contains the top of the stack. You only need to read // from the second popped element. // If the resulting stack isn't empty, update `stack_top`. -pub(crate) fn stack_pop_with_log_and_fill( +pub(crate) fn stack_pop_with_log_and_fill( state: &mut GenerationState, row: &mut CpuColumnsView, ) -> Result<[(U256, MemoryOp); N], ProgramError> { @@ -267,7 +271,7 @@ pub(crate) fn stack_pop_with_log_and_fill( Ok(result) } -fn xor_into_sponge>( +fn xor_into_sponge>( state: &mut T, sponge_state: &mut [u8; KECCAK_WIDTH_BYTES], block: &[u8; KECCAK_RATE_BYTES], @@ -283,7 +287,7 @@ fn xor_into_sponge>( } } -pub(crate) fn keccak_sponge_log>( +pub(crate) fn keccak_sponge_log>( state: &mut T, base_address: MemoryAddress, input: Vec, @@ -339,7 +343,7 @@ pub(crate) fn keccak_sponge_log>( }); } -pub(crate) fn byte_packing_log>( +pub(crate) fn byte_packing_log>( state: &mut T, base_address: MemoryAddress, bytes: Vec, @@ -371,7 +375,7 @@ pub(crate) fn byte_packing_log>( }); } -pub(crate) fn byte_unpacking_log>( +pub(crate) fn byte_unpacking_log>( state: &mut T, base_address: MemoryAddress, val: U256, diff --git a/evm_arithmetization/tests/add11_yml.rs b/evm_arithmetization/tests/add11_yml.rs index dca625d36..de333927e 100644 --- a/evm_arithmetization/tests/add11_yml.rs +++ b/evm_arithmetization/tests/add11_yml.rs @@ -2,25 +2,24 @@ use std::collections::HashMap; use std::str::FromStr; use std::time::Duration; -use ethereum_types::{Address, BigEndianHash, H256}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, - init_logger, preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, - GLOBAL_EXIT_ROOT_ACCOUNT, + init_logger, preinitialized_state, preinitialized_state_with_updated_storage, set_account, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; use hex_literal::hex; -use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -38,16 +37,8 @@ fn add11_yml() -> anyhow::Result<()> { let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - let code = [0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x55, 0x00]; - let code_hash = keccak(code); + let code_hash = hash_bytecode_u256(code.to_vec()); let beneficiary_account_before = AccountRlp { nonce: 1.into(), @@ -63,22 +54,30 @@ fn add11_yml() -> anyhow::Result<()> { ..AccountRlp::default() }; - let (mut state_trie_before, mut storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - )?; - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec())?; - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec())?; - - storage_tries.push((to_hashed, Node::Empty.into())); + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(beneficiary), + &beneficiary_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: Node::Empty.into(), - storage_tries, }; let txn = hex!("f863800a83061a8094095e7baea6a6c7c4c2dfeb977efac326af552d87830186a0801ba0ffb600e63115a7362e7811894a91d8ba4330e526f22121c994c4692035dfdfd5a06198379fcac8de3dbfac48b165df4bf88e2088f294b61efb9a65fe2281c76e16"); @@ -97,18 +96,11 @@ fn add11_yml() -> anyhow::Result<()> { }; let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); contract_code.insert(code_hash, code.to_vec()); - let expected_state_trie_after = { - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); - + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let beneficiary_account_after = AccountRlp { nonce: 1.into(), ..AccountRlp::default() @@ -121,33 +113,29 @@ fn add11_yml() -> anyhow::Result<()> { let to_account_after = AccountRlp { balance: 0xde0b6b3a76586a0u64.into(), code_hash, - // Storage map: { 0 => 2 } - storage_root: HashedPartialTrie::from(Node::Leaf { - nibbles: Nibbles::from_h256_be(keccak([0u8; 32])), - value: vec![2], - }) - .hash(), ..AccountRlp::default() }; - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - )?; - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec())?; - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec())?; - expected_state_trie_after.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - expected_state_trie_after.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; - - expected_state_trie_after + set_account( + &mut smt, + H160(beneficiary), + &beneficiary_account_after, + &HashMap::new(), + ); + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account( + &mut smt, + H160(to), + &to_account_after, + &HashMap::from([(U256::zero(), 2.into())]), // Storage map: { 0 => 2 } + ); + + smt }; let receipt_0 = LegacyReceiptRlp { @@ -168,7 +156,7 @@ fn add11_yml() -> anyhow::Result<()> { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; diff --git a/evm_arithmetization/tests/erc20.rs b/evm_arithmetization/tests/erc20.rs index 1c829efc1..f1607bd89 100644 --- a/evm_arithmetization/tests/erc20.rs +++ b/evm_arithmetization/tests/erc20.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::str::FromStr; use std::time::Duration; @@ -7,19 +8,21 @@ use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, create_account_storage, - ger_account_nibbles, init_logger, preinitialized_state_and_storage_tries, sd2u, - update_beacon_roots_account_storage, GLOBAL_EXIT_ROOT_ACCOUNT, + init_logger, preinitialized_state, preinitialized_state_with_updated_storage, sd2u, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; use hex_literal::hex; -use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::db::{Db, MemoryDb}; +use smt_trie::keys::{key_balance, key_code, key_code_length, key_nonce, key_storage}; +use smt_trie::smt::Smt; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -57,30 +60,30 @@ fn test_erc20() -> anyhow::Result<()> { let giver = hex!("e7f1725E7734CE288F8367e1Bb143E90bb3F0512"); let token = hex!("5FbDB2315678afecb367f032d93F642f64180aa3"); - let sender_state_key = keccak(sender); - let giver_state_key = keccak(giver); - let token_state_key = keccak(token); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let giver_nibbles = Nibbles::from_bytes_be(giver_state_key.as_bytes()).unwrap(); - let token_nibbles = Nibbles::from_bytes_be(token_state_key.as_bytes()).unwrap(); - - let (mut state_trie_before, mut storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account()).to_vec())?; - state_trie_before.insert(giver_nibbles, rlp::encode(&giver_account()?).to_vec())?; - state_trie_before.insert(token_nibbles, rlp::encode(&token_account()?).to_vec())?; - - storage_tries.extend([ - (giver_state_key, giver_storage()?), - (token_state_key, token_storage()?), - ]); + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account(), + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(giver), + &giver_account(), + &giver_storage(), + ); + set_account( + &mut state_smt_before, + H160(token), + &token_account(), + &token_storage(), + ); let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: HashedPartialTrie::from(Node::Empty), receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries, }; let txn = signed_tx(); @@ -102,42 +105,32 @@ fn test_erc20() -> anyhow::Result<()> { }; let contract_code = [giver_bytecode(), token_bytecode(), vec![]] - .map(|v| (keccak(v.clone()), v)) + .map(|v| (hash_bytecode_u256(v.clone()), v)) .into(); - let expected_state_trie_after: HashedPartialTrie = { - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); - - let mut state_trie_after = HashedPartialTrie::from(Node::Empty); + let expected_smt_after: Smt = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let sender_account = sender_account(); let sender_account_after = AccountRlp { nonce: sender_account.nonce + 1, balance: sender_account.balance - gas_used * 0xa, ..sender_account }; - state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec())?; - state_trie_after.insert(giver_nibbles, rlp::encode(&giver_account()?).to_vec())?; - let token_account_after = AccountRlp { - storage_root: token_storage_after()?.hash(), - ..token_account()? - }; - state_trie_after.insert(token_nibbles, rlp::encode(&token_account_after).to_vec())?; - state_trie_after.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - state_trie_after.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account(&mut smt, H160(giver), &giver_account(), &giver_storage()); + set_account( + &mut smt, + H160(token), + &token_account(), + &token_storage_after(), + ); - state_trie_after + smt }; let receipt_0 = LegacyReceiptRlp { @@ -174,7 +167,7 @@ fn test_erc20() -> anyhow::Result<()> { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -211,57 +204,64 @@ fn token_bytecode() -> Vec { hex!("608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce567146100fe57806370a082311461010d57806395d89b4114610136578063a9059cbb1461013e578063dd62ed3e1461015157600080fd5b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100d957806323b872dd146100eb575b600080fd5b6100a061018a565b6040516100ad919061056a565b60405180910390f35b6100c96100c43660046105d4565b61021c565b60405190151581526020016100ad565b6002545b6040519081526020016100ad565b6100c96100f93660046105fe565b610236565b604051601281526020016100ad565b6100dd61011b36600461063a565b6001600160a01b031660009081526020819052604090205490565b6100a061025a565b6100c961014c3660046105d4565b610269565b6100dd61015f36600461065c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6060600380546101999061068f565b80601f01602080910402602001604051908101604052809291908181526020018280546101c59061068f565b80156102125780601f106101e757610100808354040283529160200191610212565b820191906000526020600020905b8154815290600101906020018083116101f557829003601f168201915b5050505050905090565b60003361022a818585610277565b60019150505b92915050565b600033610244858285610289565b61024f85858561030c565b506001949350505050565b6060600480546101999061068f565b60003361022a81858561030c565b610284838383600161036b565b505050565b6001600160a01b03838116600090815260016020908152604080832093861683529290522054600019811461030657818110156102f757604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b6103068484848403600061036b565b50505050565b6001600160a01b03831661033657604051634b637e8f60e11b8152600060048201526024016102ee565b6001600160a01b0382166103605760405163ec442f0560e01b8152600060048201526024016102ee565b610284838383610440565b6001600160a01b0384166103955760405163e602df0560e01b8152600060048201526024016102ee565b6001600160a01b0383166103bf57604051634a1406b160e11b8152600060048201526024016102ee565b6001600160a01b038085166000908152600160209081526040808320938716835292905220829055801561030657826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161043291815260200190565b60405180910390a350505050565b6001600160a01b03831661046b57806002600082825461046091906106c9565b909155506104dd9050565b6001600160a01b038316600090815260208190526040902054818110156104be5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102ee565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b0382166104f957600280548290039055610518565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161055d91815260200190565b60405180910390a3505050565b600060208083528351808285015260005b818110156105975785810183015185820160400152820161057b565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146105cf57600080fd5b919050565b600080604083850312156105e757600080fd5b6105f0836105b8565b946020939093013593505050565b60008060006060848603121561061357600080fd5b61061c846105b8565b925061062a602085016105b8565b9150604084013590509250925092565b60006020828403121561064c57600080fd5b610655826105b8565b9392505050565b6000806040838503121561066f57600080fd5b610678836105b8565b9150610686602084016105b8565b90509250929050565b600181811c908216806106a357607f821691505b6020821081036106c357634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561023057634e487b7160e01b600052601160045260246000fdfea2646970667358221220266a323ae4a816f6c6342a5be431fedcc0d45c44b02ea75f5474eb450b5d45b364736f6c63430008140033").into() } -fn giver_storage() -> anyhow::Result { - create_account_storage(&[( +fn giver_storage() -> HashMap { + let mut storage = HashMap::new(); + storage.insert( U256::zero(), sd2u("546584486846459126461364135121053344201067465379"), - )]) + ); + storage } -fn token_storage() -> anyhow::Result { - create_account_storage(&[( +fn token_storage() -> HashMap { + let mut storage = HashMap::new(); + storage.insert( sd2u("82183438603287090451672504949863617512989139203883434767553028632841710582583"), sd2u("1000000000000000000000"), - )]) + ); + storage } -fn token_storage_after() -> anyhow::Result { - create_account_storage(&[ - ( - sd2u("82183438603287090451672504949863617512989139203883434767553028632841710582583"), - sd2u("900000000000000000000"), - ), - ( - sd2u("53006154680716014998529145169423020330606407246856709517064848190396281160729"), - sd2u("100000000000000000000"), - ), - ]) +fn token_storage_after() -> HashMap { + let mut storage = HashMap::new(); + storage.insert( + sd2u("82183438603287090451672504949863617512989139203883434767553028632841710582583"), + sd2u("900000000000000000000"), + ); + storage.insert( + sd2u("53006154680716014998529145169423020330606407246856709517064848190396281160729"), + sd2u("100000000000000000000"), + ); + storage } -fn giver_account() -> anyhow::Result { - Ok(AccountRlp { +fn giver_account() -> AccountRlp { + let code = giver_bytecode(); + let len = code.len(); + AccountRlp { nonce: 1.into(), balance: 0.into(), - storage_root: giver_storage()?.hash(), - code_hash: keccak(giver_bytecode()), - }) + code_hash: hash_bytecode_u256(code), + code_length: len.into(), + } } -fn token_account() -> anyhow::Result { - Ok(AccountRlp { +fn token_account() -> AccountRlp { + let code = token_bytecode(); + let len = code.len(); + AccountRlp { nonce: 1.into(), balance: 0.into(), - storage_root: token_storage()?.hash(), - code_hash: keccak(token_bytecode()), - }) + code_hash: hash_bytecode_u256(code), + code_length: len.into(), + } } fn sender_account() -> AccountRlp { AccountRlp { nonce: 0.into(), balance: sd2u("10000000000000000000000"), - storage_root: Default::default(), - code_hash: keccak([]), + ..Default::default() } } @@ -280,3 +280,18 @@ fn bloom() -> [U256; 8] { .collect::>(); bloom.try_into().unwrap() } + +fn set_account( + smt: &mut Smt, + addr: Address, + account: &AccountRlp, + storage: &HashMap, +) { + smt.set(key_balance(addr), account.balance); + smt.set(key_nonce(addr), account.nonce); + smt.set(key_code(addr), account.code_hash); + smt.set(key_code_length(addr), account.code_length); + for (&k, &v) in storage { + smt.set(key_storage(addr, k), v); + } +} diff --git a/evm_arithmetization/tests/erc721.rs b/evm_arithmetization/tests/erc721.rs index 3a02d8968..f36bd98a8 100644 --- a/evm_arithmetization/tests/erc721.rs +++ b/evm_arithmetization/tests/erc721.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::str::FromStr; use std::time::Duration; @@ -7,9 +8,7 @@ use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, create_account_storage, - ger_account_nibbles, init_logger, preinitialized_state_and_storage_tries, sd2u, sh2u, - update_beacon_roots_account_storage, GLOBAL_EXIT_ROOT_ACCOUNT, + init_logger, preinitialized_state, preinitialized_state_with_updated_storage, sd2u, sh2u, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; @@ -20,6 +19,11 @@ use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::db::Db; +use smt_trie::keys::{key_balance, key_code, key_code_length, key_nonce, key_storage}; +use smt_trie::smt::{hash_serialize, Smt}; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -59,34 +63,8 @@ fn test_erc721() -> anyhow::Result<()> { let owner = hex!("5B38Da6a701c568545dCfcB03FcB875f56beddC4"); let contract = hex!("f2B1114C644cBb3fF63Bf1dD284c8Cd716e95BE9"); - let owner_state_key = keccak(owner); - let contract_state_key = keccak(contract); - - let owner_nibbles = Nibbles::from_bytes_be(owner_state_key.as_bytes()).unwrap(); - let contract_nibbles = Nibbles::from_bytes_be(contract_state_key.as_bytes()).unwrap(); - - let (mut state_trie_before, mut storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before.insert(owner_nibbles, rlp::encode(&owner_account()).to_vec())?; - state_trie_before.insert(contract_nibbles, rlp::encode(&contract_account()?).to_vec())?; - - storage_tries.push((contract_state_key, contract_storage()?)); - - let tries_before = TrieInputs { - state_trie: state_trie_before, - transactions_trie: HashedPartialTrie::from(Node::Empty), - receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries, - }; - - let txn = signed_tx(); - let gas_used = 58_418.into(); - let contract_code = [contract_bytecode(), vec![]] - .map(|v| (keccak(v.clone()), v)) - .into(); - let logs = vec![LogRlp { address: H160::from_str("0xf2B1114C644cBb3fF63Bf1dD284c8Cd716e95BE9").unwrap(), topics: vec![ @@ -124,42 +102,50 @@ fn test_erc721() -> anyhow::Result<()> { ..Default::default() }; - let expected_state_trie_after: HashedPartialTrie = { - let mut state_trie_after = HashedPartialTrie::from(Node::Empty); + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(owner), + &owner_account(), + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(contract), + &contract_account()?, + &contract_storage(), + ); - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); + let tries_before = TrieInputs { + state_smt: state_smt_before.serialize(), + transactions_trie: HashedPartialTrie::from(Node::Empty), + receipts_trie: HashedPartialTrie::from(Node::Empty), + }; + + let txn = signed_tx(); + + let contract_code = [contract_bytecode(), vec![]] + .map(|v| (hash_bytecode_u256(v.clone()), v)) + .into(); + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let owner_account = owner_account(); let owner_account_after = AccountRlp { nonce: owner_account.nonce + 1, balance: owner_account.balance - gas_used * 0xa, ..owner_account }; - state_trie_after.insert(owner_nibbles, rlp::encode(&owner_account_after).to_vec())?; - let contract_account_after = AccountRlp { - storage_root: contract_storage_after()?.hash(), - ..contract_account()? - }; - state_trie_after.insert( - contract_nibbles, - rlp::encode(&contract_account_after).to_vec(), - )?; - state_trie_after.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - state_trie_after.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; - - state_trie_after + set_account(&mut smt, H160(owner), &owner_account_after, &HashMap::new()); + let contract_account_after = contract_account()?; + set_account( + &mut smt, + H160(contract), + &contract_account_after, + &contract_storage_after(), + ); + + smt }; let receipt_0 = LegacyReceiptRlp { @@ -176,8 +162,10 @@ fn test_erc721() -> anyhow::Result<()> { } .into(); + hash_serialize(&expected_state_smt_after.serialize()); + dbg!("done"); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -211,62 +199,61 @@ fn contract_bytecode() -> Vec { hex!("608060405234801561000f575f80fd5b5060043610610109575f3560e01c8063715018a6116100a0578063a22cb4651161006f578063a22cb465146102a1578063b88d4fde146102bd578063c87b56dd146102d9578063e985e9c514610309578063f2fde38b1461033957610109565b8063715018a61461023f5780638da5cb5b1461024957806395d89b4114610267578063a14481941461028557610109565b806323b872dd116100dc57806323b872dd146101a757806342842e0e146101c35780636352211e146101df57806370a082311461020f57610109565b806301ffc9a71461010d57806306fdde031461013d578063081812fc1461015b578063095ea7b31461018b575b5f80fd5b61012760048036038101906101229190611855565b610355565b604051610134919061189a565b60405180910390f35b610145610436565b604051610152919061193d565b60405180910390f35b61017560048036038101906101709190611990565b6104c5565b60405161018291906119fa565b60405180910390f35b6101a560048036038101906101a09190611a3d565b6104e0565b005b6101c160048036038101906101bc9190611a7b565b6104f6565b005b6101dd60048036038101906101d89190611a7b565b6105f5565b005b6101f960048036038101906101f49190611990565b610614565b60405161020691906119fa565b60405180910390f35b61022960048036038101906102249190611acb565b610625565b6040516102369190611b05565b60405180910390f35b6102476106db565b005b6102516106ee565b60405161025e91906119fa565b60405180910390f35b61026f610716565b60405161027c919061193d565b60405180910390f35b61029f600480360381019061029a9190611a3d565b6107a6565b005b6102bb60048036038101906102b69190611b48565b6107bc565b005b6102d760048036038101906102d29190611cb2565b6107d2565b005b6102f360048036038101906102ee9190611990565b6107ef565b604051610300919061193d565b60405180910390f35b610323600480360381019061031e9190611d32565b610855565b604051610330919061189a565b60405180910390f35b610353600480360381019061034e9190611acb565b6108e3565b005b5f7f80ac58cd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916148061041f57507f5b5e139f000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916145b8061042f575061042e82610967565b5b9050919050565b60605f805461044490611d9d565b80601f016020809104026020016040519081016040528092919081815260200182805461047090611d9d565b80156104bb5780601f10610492576101008083540402835291602001916104bb565b820191905f5260205f20905b81548152906001019060200180831161049e57829003601f168201915b5050505050905090565b5f6104cf826109d0565b506104d982610a56565b9050919050565b6104f282826104ed610a8f565b610a96565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610566575f6040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161055d91906119fa565b60405180910390fd5b5f6105798383610574610a8f565b610aa8565b90508373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146105ef578382826040517f64283d7b0000000000000000000000000000000000000000000000000000000081526004016105e693929190611dcd565b60405180910390fd5b50505050565b61060f83838360405180602001604052805f8152506107d2565b505050565b5f61061e826109d0565b9050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610696575f6040517f89c62b6400000000000000000000000000000000000000000000000000000000815260040161068d91906119fa565b60405180910390fd5b60035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20549050919050565b6106e3610cb3565b6106ec5f610d3a565b565b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60606001805461072590611d9d565b80601f016020809104026020016040519081016040528092919081815260200182805461075190611d9d565b801561079c5780601f106107735761010080835404028352916020019161079c565b820191905f5260205f20905b81548152906001019060200180831161077f57829003601f168201915b5050505050905090565b6107ae610cb3565b6107b88282610dfd565b5050565b6107ce6107c7610a8f565b8383610e1a565b5050565b6107dd8484846104f6565b6107e984848484610f83565b50505050565b60606107fa826109d0565b505f610804611135565b90505f8151116108225760405180602001604052805f81525061084d565b8061082c8461114b565b60405160200161083d929190611e3c565b6040516020818303038152906040525b915050919050565b5f60055f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff16905092915050565b6108eb610cb3565b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361095b575f6040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260040161095291906119fa565b60405180910390fd5b61096481610d3a565b50565b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f806109db83611215565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610a4d57826040517f7e273289000000000000000000000000000000000000000000000000000000008152600401610a449190611b05565b60405180910390fd5b80915050919050565b5f60045f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b5f33905090565b610aa3838383600161124e565b505050565b5f80610ab384611215565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614610af457610af381848661140d565b5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610b7f57610b335f855f8061124e565b600160035f8373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825403925050819055505b5f73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614610bfe57600160035f8773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f82825401925050819055505b8460025f8681526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a4809150509392505050565b610cbb610a8f565b73ffffffffffffffffffffffffffffffffffffffff16610cd96106ee565b73ffffffffffffffffffffffffffffffffffffffff1614610d3857610cfc610a8f565b6040517f118cdaa7000000000000000000000000000000000000000000000000000000008152600401610d2f91906119fa565b60405180910390fd5b565b5f60065f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160065f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610e16828260405180602001604052805f8152506114d0565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610e8a57816040517f5b08ba18000000000000000000000000000000000000000000000000000000008152600401610e8191906119fa565b60405180910390fd5b8060055f8573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3183604051610f76919061189a565b60405180910390a3505050565b5f8373ffffffffffffffffffffffffffffffffffffffff163b111561112f578273ffffffffffffffffffffffffffffffffffffffff1663150b7a02610fc6610a8f565b8685856040518563ffffffff1660e01b8152600401610fe89493929190611eb1565b6020604051808303815f875af192505050801561102357506040513d601f19601f820116820180604052508101906110209190611f0f565b60015b6110a4573d805f8114611051576040519150601f19603f3d011682016040523d82523d5f602084013e611056565b606091505b505f81510361109c57836040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161109391906119fa565b60405180910390fd5b805181602001fd5b63150b7a0260e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161461112d57836040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161112491906119fa565b60405180910390fd5b505b50505050565b606060405180602001604052805f815250905090565b60605f6001611159846114eb565b0190505f8167ffffffffffffffff81111561117757611176611b8e565b5b6040519080825280601f01601f1916602001820160405280156111a95781602001600182028036833780820191505090505b5090505f82602001820190505b60011561120a578080600190039150507f3031323334353637383961626364656600000000000000000000000000000000600a86061a8153600a85816111ff576111fe611f3a565b5b0494505f85036111b6575b819350505050919050565b5f60025f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b808061128657505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b156113b8575f611295846109d0565b90505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156112ff57508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614155b801561131257506113108184610855565b155b1561135457826040517fa9fbf51f00000000000000000000000000000000000000000000000000000000815260040161134b91906119fa565b60405180910390fd5b81156113b657838573ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b8360045f8581526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050505050565b61141883838361163c565b6114cb575f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361148c57806040517f7e2732890000000000000000000000000000000000000000000000000000000081526004016114839190611b05565b60405180910390fd5b81816040517f177e802f0000000000000000000000000000000000000000000000000000000081526004016114c2929190611f67565b60405180910390fd5b505050565b6114da83836116fc565b6114e65f848484610f83565b505050565b5f805f90507a184f03e93ff9f4daa797ed6e38ed64bf6a1f0100000000000000008310611547577a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000838161153d5761153c611f3a565b5b0492506040810190505b6d04ee2d6d415b85acef81000000008310611584576d04ee2d6d415b85acef8100000000838161157a57611579611f3a565b5b0492506020810190505b662386f26fc1000083106115b357662386f26fc1000083816115a9576115a8611f3a565b5b0492506010810190505b6305f5e10083106115dc576305f5e10083816115d2576115d1611f3a565b5b0492506008810190505b61271083106116015761271083816115f7576115f6611f3a565b5b0492506004810190505b60648310611624576064838161161a57611619611f3a565b5b0492506002810190505b600a8310611633576001810190505b80915050919050565b5f8073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141580156116f357508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614806116b457506116b38484610855565b5b806116f257508273ffffffffffffffffffffffffffffffffffffffff166116da83610a56565b73ffffffffffffffffffffffffffffffffffffffff16145b5b90509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361176c575f6040517f64a0ae9200000000000000000000000000000000000000000000000000000000815260040161176391906119fa565b60405180910390fd5b5f61177883835f610aa8565b90505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146117ea575f6040517f73c6ac6e0000000000000000000000000000000000000000000000000000000081526004016117e191906119fa565b60405180910390fd5b505050565b5f604051905090565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61183481611800565b811461183e575f80fd5b50565b5f8135905061184f8161182b565b92915050565b5f6020828403121561186a576118696117f8565b5b5f61187784828501611841565b91505092915050565b5f8115159050919050565b61189481611880565b82525050565b5f6020820190506118ad5f83018461188b565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156118ea5780820151818401526020810190506118cf565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61190f826118b3565b61191981856118bd565b93506119298185602086016118cd565b611932816118f5565b840191505092915050565b5f6020820190508181035f8301526119558184611905565b905092915050565b5f819050919050565b61196f8161195d565b8114611979575f80fd5b50565b5f8135905061198a81611966565b92915050565b5f602082840312156119a5576119a46117f8565b5b5f6119b28482850161197c565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6119e4826119bb565b9050919050565b6119f4816119da565b82525050565b5f602082019050611a0d5f8301846119eb565b92915050565b611a1c816119da565b8114611a26575f80fd5b50565b5f81359050611a3781611a13565b92915050565b5f8060408385031215611a5357611a526117f8565b5b5f611a6085828601611a29565b9250506020611a718582860161197c565b9150509250929050565b5f805f60608486031215611a9257611a916117f8565b5b5f611a9f86828701611a29565b9350506020611ab086828701611a29565b9250506040611ac18682870161197c565b9150509250925092565b5f60208284031215611ae057611adf6117f8565b5b5f611aed84828501611a29565b91505092915050565b611aff8161195d565b82525050565b5f602082019050611b185f830184611af6565b92915050565b611b2781611880565b8114611b31575f80fd5b50565b5f81359050611b4281611b1e565b92915050565b5f8060408385031215611b5e57611b5d6117f8565b5b5f611b6b85828601611a29565b9250506020611b7c85828601611b34565b9150509250929050565b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b611bc4826118f5565b810181811067ffffffffffffffff82111715611be357611be2611b8e565b5b80604052505050565b5f611bf56117ef565b9050611c018282611bbb565b919050565b5f67ffffffffffffffff821115611c2057611c1f611b8e565b5b611c29826118f5565b9050602081019050919050565b828183375f83830152505050565b5f611c56611c5184611c06565b611bec565b905082815260208101848484011115611c7257611c71611b8a565b5b611c7d848285611c36565b509392505050565b5f82601f830112611c9957611c98611b86565b5b8135611ca9848260208601611c44565b91505092915050565b5f805f8060808587031215611cca57611cc96117f8565b5b5f611cd787828801611a29565b9450506020611ce887828801611a29565b9350506040611cf98782880161197c565b925050606085013567ffffffffffffffff811115611d1a57611d196117fc565b5b611d2687828801611c85565b91505092959194509250565b5f8060408385031215611d4857611d476117f8565b5b5f611d5585828601611a29565b9250506020611d6685828601611a29565b9150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680611db457607f821691505b602082108103611dc757611dc6611d70565b5b50919050565b5f606082019050611de05f8301866119eb565b611ded6020830185611af6565b611dfa60408301846119eb565b949350505050565b5f81905092915050565b5f611e16826118b3565b611e208185611e02565b9350611e308185602086016118cd565b80840191505092915050565b5f611e478285611e0c565b9150611e538284611e0c565b91508190509392505050565b5f81519050919050565b5f82825260208201905092915050565b5f611e8382611e5f565b611e8d8185611e69565b9350611e9d8185602086016118cd565b611ea6816118f5565b840191505092915050565b5f608082019050611ec45f8301876119eb565b611ed160208301866119eb565b611ede6040830185611af6565b8181036060830152611ef08184611e79565b905095945050505050565b5f81519050611f098161182b565b92915050565b5f60208284031215611f2457611f236117f8565b5b5f611f3184828501611efb565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f604082019050611f7a5f8301856119eb565b611f876020830184611af6565b939250505056fea2646970667358221220432b30673e00c0eb009e1718c271f4cfdfbeded17345829703b06d322360990164736f6c63430008160033").into() } -fn contract_storage() -> anyhow::Result { - create_account_storage(&[ - ( - U256::zero(), - sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), - ), - ( - U256::one(), - sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), - ), - ( - sd2u("6"), - sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), - ), - ( - sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), - sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), - ), - ( - sh2u("0x118c1ea466562cb796e30ef705e4db752f5c39d773d22c5efd8d46f67194e78a"), - sd2u("1"), - ), - ]) +fn contract_storage() -> HashMap { + let mut storage = HashMap::new(); + storage.insert( + U256::zero(), + sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), + ); + storage.insert( + U256::one(), + sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), + ); + storage.insert( + sd2u("6"), + sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), + ); + storage.insert( + sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), + sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), + ); + storage.insert( + sh2u("0x118c1ea466562cb796e30ef705e4db752f5c39d773d22c5efd8d46f67194e78a"), + sd2u("1"), + ); + storage } -fn contract_storage_after() -> anyhow::Result { - create_account_storage(&[ - ( - U256::zero(), - sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), - ), - ( - U256::one(), - sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), - ), - ( - sd2u("6"), - sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), - ), - ( - sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), - sh2u("0xab8483f64d9c6d1ecf9b849ae677dd3315835cb2"), - ), - ( - sh2u("0xf3aa6a8a9f7e3707e36cc99c499a27514922afe861ec3d80a1a314409cba92f9"), - sd2u("1"), - ), - ]) +fn contract_storage_after() -> HashMap { + let mut storage = HashMap::new(); + storage.insert( + U256::zero(), + sh2u("0x54657374546f6b656e0000000000000000000000000000000000000000000012"), + ); + storage.insert( + U256::one(), + sh2u("0x5445535400000000000000000000000000000000000000000000000000000008"), + ); + storage.insert( + sd2u("6"), + sh2u("0x5b38da6a701c568545dcfcb03fcb875f56beddc4"), + ); + storage.insert( + sh2u("0x343ff8127bd64f680be4e996254dc3528603c6ecd54364b4cf956ebdd28f0028"), + sh2u("0xab8483f64d9c6d1ecf9b849ae677dd3315835cb2"), + ); + storage.insert( + sh2u("0xf3aa6a8a9f7e3707e36cc99c499a27514922afe861ec3d80a1a314409cba92f9"), + sd2u("1"), + ); + storage } fn owner_account() -> AccountRlp { AccountRlp { nonce: 2.into(), balance: 0x1000000.into(), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), + ..Default::default() } } @@ -274,8 +261,8 @@ fn contract_account() -> anyhow::Result { Ok(AccountRlp { nonce: 0.into(), balance: 0.into(), - storage_root: contract_storage()?.hash(), - code_hash: keccak(contract_bytecode()), + code_hash: hash_bytecode_u256(contract_bytecode()), + code_length: contract_bytecode().len().into(), }) } @@ -303,3 +290,18 @@ fn add_to_bloom(bloom: &mut [u8; 256], bloom_entry: &[u8]) { bloom[byte_index as usize] |= bit_value; } } + +fn set_account( + smt: &mut Smt, + addr: Address, + account: &AccountRlp, + storage: &HashMap, +) { + smt.set(key_balance(addr), account.balance); + smt.set(key_nonce(addr), account.nonce); + smt.set(key_code(addr), account.code_hash); + smt.set(key_code_length(addr), account.code_length); + for (&k, &v) in storage { + smt.set(key_storage(addr, k), v); + } +} diff --git a/evm_arithmetization/tests/global_exit_root.rs b/evm_arithmetization/tests/global_exit_root.rs index 507ffe0f7..2b5617627 100644 --- a/evm_arithmetization/tests/global_exit_root.rs +++ b/evm_arithmetization/tests/global_exit_root.rs @@ -1,23 +1,22 @@ use std::collections::HashMap; use std::time::Duration; -use ethereum_types::{H256, U256}; +use ethereum_types::{BigEndianHash, H256, U256}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, - ger_contract_from_storage, init_logger, preinitialized_state_and_storage_tries, - update_beacon_roots_account_storage, update_ger_account_storage, + init_logger, preinitialized_state, preinitialized_state_with_updated_storage, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; -use keccak_hash::keccak; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; use rand::random; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -36,42 +35,20 @@ fn test_global_exit_root() -> anyhow::Result<()> { ..BlockMetadata::default() }; - let (state_trie_before, storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - let mut ger_account_storage = storage_tries[1].1.clone(); + let state_smt = preinitialized_state(); let transactions_trie = HashedPartialTrie::from(Node::Empty); let receipts_trie = HashedPartialTrie::from(Node::Empty); let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); let global_exit_roots = vec![(U256(random()), H256(random()))]; - let state_trie_after = { - let mut trie = HashedPartialTrie::from(Node::Empty); - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); - for &(timestamp, root) in &global_exit_roots { - update_ger_account_storage(&mut ger_account_storage, root, timestamp)?; - } - let ger_account = ger_contract_from_storage(&ger_account_storage); - - trie.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - trie.insert(ger_account_nibbles(), rlp::encode(&ger_account).to_vec())?; - - trie - }; + let expected_smt_after = + preinitialized_state_with_updated_storage(&block_metadata, &global_exit_roots); let trie_roots_after = TrieRoots { - state_root: state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -81,10 +58,9 @@ fn test_global_exit_root() -> anyhow::Result<()> { withdrawals: vec![], global_exit_roots, tries: TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt.serialize(), transactions_trie, receipts_trie, - storage_tries, }, trie_roots_after, contract_code, diff --git a/evm_arithmetization/tests/log_opcode.rs b/evm_arithmetization/tests/log_opcode.rs index 8cd5c57c0..4975fdee5 100644 --- a/evm_arithmetization/tests/log_opcode.rs +++ b/evm_arithmetization/tests/log_opcode.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use std::time::Duration; use bytes::Bytes; -use ethereum_types::{Address, BigEndianHash, H256}; +use ethereum_types::{Address, BigEndianHash, H160, H256}; use evm_arithmetization::generation::mpt::transaction_testing::{ AddressOption, LegacyTransactionRlp, }; @@ -12,19 +12,18 @@ use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, - init_logger, preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, - GLOBAL_EXIT_ROOT_ACCOUNT, + init_logger, preinitialized_state, preinitialized_state_with_updated_storage, set_account, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; use hex_literal::hex; -use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -43,14 +42,6 @@ fn test_log_opcodes() -> anyhow::Result<()> { // Private key: DCDFF53B4F013DBCDC717F89FE3BF4D8B10512AAE282B48E01D7530470382701 let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); - let beneficiary_state_key = keccak(beneficiary); - let sender_state_key = keccak(sender); - let to_hashed = keccak(to); - - let beneficiary_nibbles = Nibbles::from_bytes_be(beneficiary_state_key.as_bytes()).unwrap(); - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_hashed.as_bytes()).unwrap(); - // For the first code transaction code, we consider two LOG opcodes. The first // deals with 0 topics and empty data. The second deals with two topics, and // data of length 5, stored in memory. @@ -60,14 +51,13 @@ fn test_log_opcodes() -> anyhow::Result<()> { 0x60, 99, 0x60, 98, 0x60, 5, 0x60, 27, 0xA2, // LOG2(27, 5, 98, 99) 0x00, ]; - let code_gas = 3 + 3 + 3 // PUSHs and MSTORE + 3 + 3 + 375 // PUSHs and LOG0 + 3 + 3 + 3 + 3 + 375 + 375*2 + 8*5 + 3// PUSHs, LOG2 and memory expansion ; let gas_used = 21_000 + code_gas; - let code_hash = keccak(code); + let code_hash = hash_bytecode_u256(code.to_vec()); // Set accounts before the transaction. let beneficiary_account_before = AccountRlp { @@ -87,16 +77,25 @@ fn test_log_opcodes() -> anyhow::Result<()> { }; // Initialize the state trie with three accounts. - let (mut state_trie_before, mut storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_before).to_vec(), - )?; - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec())?; - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec())?; - - storage_tries.push((to_hashed, Node::Empty.into())); + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(beneficiary), + &beneficiary_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); // We now add two receipts with logs and data. This updates the receipt trie as // well. @@ -128,10 +127,9 @@ fn test_log_opcodes() -> anyhow::Result<()> { )?; let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: Node::Empty.into(), receipts_trie: receipts_trie.clone(), - storage_tries, }; // Prove a transaction which carries out two LOG opcodes. @@ -151,35 +149,9 @@ fn test_log_opcodes() -> anyhow::Result<()> { }; let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); contract_code.insert(code_hash, code.to_vec()); - // Update the state and receipt tries after the transaction, so that we have the - // correct expected tries: Update accounts - let beneficiary_account_after = AccountRlp { - nonce: 1.into(), - ..AccountRlp::default() - }; - - let sender_balance_after = sender_balance_before - gas_used * txn_gas_price; - let sender_account_after = AccountRlp { - balance: sender_balance_after.into(), - nonce: 1.into(), - ..AccountRlp::default() - }; - let to_account_after = AccountRlp { - balance: 9000000000u64.into(), - code_hash, - ..AccountRlp::default() - }; - - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = beacon_roots_contract_from_storage(&beacon_roots_account_storage); - // Update the receipt trie. let first_log = LogRlp { address: to.into(), @@ -208,22 +180,39 @@ fn test_log_opcodes() -> anyhow::Result<()> { receipts_trie.insert(receipt_nibbles, rlp::encode(&receipt).to_vec())?; // Update the state trie. - let mut expected_state_trie_after = HashedPartialTrie::from(Node::Empty); - expected_state_trie_after.insert( - beneficiary_nibbles, - rlp::encode(&beneficiary_account_after).to_vec(), - )?; - expected_state_trie_after - .insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec())?; - expected_state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec())?; - expected_state_trie_after.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - expected_state_trie_after.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); + let beneficiary_account_after = AccountRlp { + nonce: 1.into(), + ..AccountRlp::default() + }; + let sender_account_after = AccountRlp { + balance: (sender_balance_before - gas_used * txn_gas_price).into(), + nonce: 1.into(), + ..AccountRlp::default() + }; + let to_account_after = AccountRlp { + balance: 9000000000u64.into(), + code_hash, + ..AccountRlp::default() + }; + + set_account( + &mut smt, + H160(beneficiary), + &beneficiary_account_after, + &HashMap::new(), + ); + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account(&mut smt, H160(to), &to_account_after, &HashMap::new()); + + smt + }; let transactions_trie: HashedPartialTrie = Node::Leaf { nibbles: Nibbles::from_str("0x80").unwrap(), @@ -232,7 +221,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -260,17 +249,6 @@ fn test_log_opcodes() -> anyhow::Result<()> { let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; timing.filter(Duration::from_millis(100)).print(); - // Assert that the proof leads to the correct state and receipt roots. - assert_eq!( - proof.public_values.trie_roots_after.state_root, - expected_state_trie_after.hash() - ); - - assert_eq!( - proof.public_values.trie_roots_after.receipts_root, - receipts_trie.hash() - ); - verify_proof(&all_stark, proof, &config) } diff --git a/evm_arithmetization/tests/selfdestruct.rs b/evm_arithmetization/tests/selfdestruct.rs index 708646e16..570e556da 100644 --- a/evm_arithmetization/tests/selfdestruct.rs +++ b/evm_arithmetization/tests/selfdestruct.rs @@ -1,25 +1,26 @@ +use std::collections::HashMap; use std::str::FromStr; use std::time::Duration; -use ethereum_types::{Address, BigEndianHash, H256}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, eth_to_wei, - ger_account_nibbles, init_logger, preinitialized_state_and_storage_tries, - update_beacon_roots_account_storage, GLOBAL_EXIT_ROOT_ACCOUNT, + eth_to_wei, init_logger, preinitialized_state, preinitialized_state_with_updated_storage, + set_account, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; use hex_literal::hex; -use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -37,17 +38,10 @@ fn test_selfdestruct() -> anyhow::Result<()> { let sender = hex!("5eb96AA102a29fAB267E12A40a5bc6E9aC088759"); let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - let sender_account_before = AccountRlp { nonce: 5.into(), balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), + ..Default::default() }; let code = vec![ 0x32, // ORIGIN @@ -56,20 +50,28 @@ fn test_selfdestruct() -> anyhow::Result<()> { let to_account_before = AccountRlp { nonce: 12.into(), balance: eth_to_wei(10_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak(&code), + code_hash: hash_bytecode_u256(code.clone()), + ..Default::default() }; - let (mut state_trie_before, storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec())?; - state_trie_before.insert(to_nibbles, rlp::encode(&to_account_before).to_vec())?; + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: HashedPartialTrie::from(Node::Empty), receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries, }; // Generated using a little py-evm script. @@ -88,46 +90,31 @@ fn test_selfdestruct() -> anyhow::Result<()> { ..Default::default() }; - let contract_code = [(keccak(&code), code.clone()), (keccak([]), vec![])].into(); - - let expected_state_trie_after: HashedPartialTrie = { - let mut state_trie_after = HashedPartialTrie::from(Node::Empty); - - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); + let contract_code = [ + (hash_bytecode_u256(code.clone()), code), + (hash_bytecode_u256(vec![]), vec![]), + ] + .into(); + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let sender_account_after = AccountRlp { nonce: 6.into(), balance: eth_to_wei(110_000.into()) - 26_002 * 0xa, - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), + ..Default::default() }; - state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec())?; - - // EIP-6780: The account won't be deleted because it wasn't created during this - // transaction. - let to_account_before = AccountRlp { - nonce: 12.into(), - balance: 0.into(), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak(&code), + let to_account_after = AccountRlp { + balance: U256::zero(), + ..to_account_before }; - state_trie_after.insert(to_nibbles, rlp::encode(&to_account_before).to_vec())?; - state_trie_after.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - state_trie_after.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; - - state_trie_after + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account(&mut smt, H160(to), &to_account_after, &HashMap::new()); + smt }; let receipt_0 = LegacyReceiptRlp { @@ -148,7 +135,7 @@ fn test_selfdestruct() -> anyhow::Result<()> { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -176,3 +163,161 @@ fn test_selfdestruct() -> anyhow::Result<()> { verify_proof(&all_stark, proof, &config) } + +#[test] +fn test_selfdestruct_with_storage() -> anyhow::Result<()> { + init_logger(); + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + + let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); + let sender = hex!("5eb96AA102a29fAB267E12A40a5bc6E9aC088759"); + let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); + + let sender_account_before = AccountRlp { + nonce: 5.into(), + balance: eth_to_wei(100_000.into()), + ..Default::default() + }; + let initcode = vec![ + 0x42, // TIMESTAMP + 0x60, 0x01, // PUSH1 1 + 0x80, // DUP1 + 0x55, // SSTORE + 0x32, // ORIGIN + 0xFF, // SELFDESTRUCT + ]; + let code = [ + vec![ + 0x66, // PUSH7 + ], + initcode, + vec![ + 0x5F, // PUSH0 + 0x52, // MSTORE + 0x60, 0x07, // PUSH1 7 + 0x60, 0x19, // PUSH1 25 + 0x5F, // PUSH0 + 0xF0, // CREATE + ], + ] + .concat(); + let to_account_before = AccountRlp { + nonce: 12.into(), + balance: eth_to_wei(10_000.into()), + code_hash: hash_bytecode_u256(code.clone()), + ..Default::default() + }; + + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); + + let tries_before = TrieInputs { + state_smt: state_smt_before.serialize(), + transactions_trie: HashedPartialTrie::from(Node::Empty), + receipts_trie: HashedPartialTrie::from(Node::Empty), + }; + + // Generated using a little py-evm script. + let txn = hex!("f868050a831e848094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0880de0b6b3a76400008025a09bab8db7d72e4b42cba8b117883e16872966bae8e4570582de6ed0065e8c36a1a01256d44d982c75e0ab7a19f61ab78afa9e089d51c8686fdfbee085a5ed5d8ff8"); + + let block_metadata = BlockMetadata { + block_beneficiary: Address::from(beneficiary), + block_timestamp: 0x03e8.into(), + block_number: 1.into(), + block_difficulty: 0x020000.into(), + block_random: H256::from_uint(&0x020000.into()), + block_gaslimit: 0xff112233u32.into(), + block_chain_id: 1.into(), + block_base_fee: 0xa.into(), + block_gas_used: 80131.into(), + ..Default::default() + }; + + let contract_code = [ + (hash_bytecode_u256(code.clone()), code), + (hash_bytecode_u256(vec![]), vec![]), + ] + .into(); + + let value = eth_to_wei(1.into()); + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); + let sender_account_after = AccountRlp { + nonce: sender_account_before.nonce + 1, + balance: sender_account_before.balance - 80131 * 0xa - value, + ..sender_account_before + }; + let to_account_after = AccountRlp { + nonce: to_account_before.nonce + 1, + balance: to_account_before.balance + value, + ..to_account_before + }; + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account(&mut smt, H160(to), &to_account_after, &HashMap::new()); + smt + }; + + let receipt_0 = LegacyReceiptRlp { + status: true, + cum_gas_used: 80131.into(), + bloom: vec![0; 256].into(), + logs: vec![], + }; + let mut receipts_trie = HashedPartialTrie::from(Node::Empty); + receipts_trie.insert( + Nibbles::from_str("0x80").unwrap(), + rlp::encode(&receipt_0).to_vec(), + )?; + let transactions_trie: HashedPartialTrie = Node::Leaf { + nibbles: Nibbles::from_str("0x80").unwrap(), + value: txn.to_vec(), + } + .into(); + + let trie_roots_after = TrieRoots { + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), + transactions_root: transactions_trie.hash(), + receipts_root: receipts_trie.hash(), + }; + let inputs = GenerationInputs { + signed_txn: Some(txn.to_vec()), + withdrawals: vec![], + tries: tries_before, + trie_roots_after, + contract_code, + global_exit_roots: vec![], + checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + block_metadata, + txn_number_before: 0.into(), + gas_used_before: 0.into(), + gas_used_after: 80131.into(), + block_hashes: BlockHashes { + prev_hashes: vec![H256::default(); 256], + cur_hash: H256::default(), + }, + }; + + let mut timing = TimingTree::new("prove", log::Level::Debug); + let proof = prove::(&all_stark, &config, inputs, &mut timing, None)?; + timing.filter(Duration::from_millis(100)).print(); + + verify_proof(&all_stark, proof, &config) +} diff --git a/evm_arithmetization/tests/simple_transfer.rs b/evm_arithmetization/tests/simple_transfer.rs index 030b2c3e1..4c4017f84 100644 --- a/evm_arithmetization/tests/simple_transfer.rs +++ b/evm_arithmetization/tests/simple_transfer.rs @@ -2,25 +2,25 @@ use std::collections::HashMap; use std::str::FromStr; use std::time::Duration; -use ethereum_types::{Address, BigEndianHash, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H160, H256, U256}; use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, eth_to_wei, - ger_account_nibbles, init_logger, preinitialized_state_and_storage_tries, - update_beacon_roots_account_storage, GLOBAL_EXIT_ROOT_ACCOUNT, + eth_to_wei, init_logger, preinitialized_state, preinitialized_state_with_updated_storage, + set_account, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; use hex_literal::hex; -use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -38,29 +38,32 @@ fn test_simple_transfer() -> anyhow::Result<()> { let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - let sender_account_before = AccountRlp { nonce: 5.into(), balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), + ..Default::default() }; + let to_account_before = AccountRlp::default(); - let (mut state_trie_before, storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); - state_trie_before.insert(sender_nibbles, rlp::encode(&sender_account_before).to_vec())?; + let mut state_smt_before = preinitialized_state(); + set_account( + &mut state_smt_before, + H160(sender), + &sender_account_before, + &HashMap::new(), + ); + set_account( + &mut state_smt_before, + H160(to), + &to_account_before, + &HashMap::new(), + ); let tries_before = TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie: HashedPartialTrie::from(Node::Empty), receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries, }; // Generated using a little py-evm script. @@ -81,22 +84,13 @@ fn test_simple_transfer() -> anyhow::Result<()> { }; let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); - - let expected_state_trie_after: HashedPartialTrie = { - let mut state_trie_after = HashedPartialTrie::from(Node::Empty); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); + let expected_state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let txdata_gas = 2 * 16; let gas_used = 21_000 + txdata_gas; - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); - let sender_account_after = AccountRlp { balance: sender_account_before.balance - value - gas_used * 10, nonce: sender_account_before.nonce + 1, @@ -107,19 +101,15 @@ fn test_simple_transfer() -> anyhow::Result<()> { ..to_account_before }; - state_trie_after.insert(sender_nibbles, rlp::encode(&sender_account_after).to_vec())?; - state_trie_after.insert(to_nibbles, rlp::encode(&to_account_after).to_vec())?; - - state_trie_after.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - state_trie_after.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; + set_account( + &mut smt, + H160(sender), + &sender_account_after, + &HashMap::new(), + ); + set_account(&mut smt, H160(to), &to_account_after, &HashMap::new()); - state_trie_after + smt }; let receipt_0 = LegacyReceiptRlp { @@ -140,7 +130,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { .into(); let trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(expected_state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index fe479bf65..8ff790006 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -1,83 +1,45 @@ use std::collections::HashMap; -use std::str::FromStr; -use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV}; -use ethereum_types::{Address, BigEndianHash, H256, U256}; +use ethereum_types::{Address, BigEndianHash, H256}; use evm_arithmetization::fixed_recursive_verifier::{ extract_block_public_values, extract_two_to_one_block_hash, }; -use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockMetadata, PublicValues, TrieRoots}; +use evm_arithmetization::testing_utils::{ + init_logger, preinitialized_state, preinitialized_state_with_updated_storage, +}; use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; use hex_literal::hex; -use keccak_hash::keccak; -use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::hash::poseidon::PoseidonHash; use plonky2::plonk::config::{Hasher, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::util::timing::TimingTree; +use smt_trie::db::MemoryDb; +use smt_trie::smt::Smt; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; type C = PoseidonGoldilocksConfig; -fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} - -fn init_logger() { - let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); -} - -/// Get `GenerationInputs` for a simple token transfer txn, where the block has -/// the given timestamp. -fn empty_transfer(timestamp: u64) -> anyhow::Result { +/// Get `GenerationInputs` for an empty block with the given timestamp. +fn empty_transfer(timestamp: u64) -> anyhow::Result<(GenerationInputs, Smt)> { init_logger(); let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); - let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23"); - let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0"); - - let sender_state_key = keccak(sender); - let to_state_key = keccak(to); - - let sender_nibbles = Nibbles::from_bytes_be(sender_state_key.as_bytes()).unwrap(); - let to_nibbles = Nibbles::from_bytes_be(to_state_key.as_bytes()).unwrap(); - let sender_account_before = AccountRlp { - nonce: 5.into(), - balance: eth_to_wei(100_000.into()), - storage_root: HashedPartialTrie::from(Node::Empty).hash(), - code_hash: keccak([]), - }; - let to_account_before = AccountRlp::default(); - - let state_trie_before: HashedPartialTrie = Node::Leaf { - nibbles: sender_nibbles, - value: rlp::encode(&sender_account_before).to_vec(), - } - .into(); - let checkpoint_state_trie_root = state_trie_before.hash(); - assert_eq!( - checkpoint_state_trie_root, - hex!("ef46022eafbc33d70e6ea9c6aef1074c1ff7ad36417ffbc64307ad3a8c274b75").into() - ); + let state_smt_before = preinitialized_state(); + let checkpoint_state_trie_root = H256::from_uint(&hashout2u(state_smt_before.root)); let tries_before = TrieInputs { - state_trie: HashedPartialTrie::from(Node::Empty), + state_smt: state_smt_before.serialize(), transactions_trie: HashedPartialTrie::from(Node::Empty), receipts_trie: HashedPartialTrie::from(Node::Empty), - storage_tries: vec![], }; - // Generated using a little py-evm script. - let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd"); - let value = U256::from(100u32); - let block_metadata = BlockMetadata { block_beneficiary: Address::from(beneficiary), block_timestamp: timestamp.into(), @@ -92,76 +54,24 @@ fn empty_transfer(timestamp: u64) -> anyhow::Result { let contract_code = HashMap::new(); - let expected_state_trie_after: HashedPartialTrie = { - let txdata_gas = 2 * 16; - let gas_used = 21_000 + txdata_gas; - - let sender_account_after = AccountRlp { - balance: sender_account_before.balance - value - gas_used * 10, - nonce: sender_account_before.nonce + 1, - ..sender_account_before - }; - let to_account_after = AccountRlp { - balance: value, - ..to_account_before - }; - - let mut children = core::array::from_fn(|_| Node::Empty.into()); - children[sender_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: sender_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&sender_account_after).to_vec(), - } - .into(); - children[to_nibbles.get_nibble(0) as usize] = Node::Leaf { - nibbles: to_nibbles.truncate_n_nibbles_front(1), - value: rlp::encode(&to_account_after).to_vec(), - } - .into(); - Node::Branch { - children, - value: vec![], - } - .into() - }; - - let receipt_0 = LegacyReceiptRlp { - status: true, - cum_gas_used: 21032.into(), - bloom: vec![0; 256].into(), - logs: vec![], - }; - let mut receipts_trie = HashedPartialTrie::from(Node::Empty); - receipts_trie.insert( - Nibbles::from_str("0x80").unwrap(), - rlp::encode(&receipt_0).to_vec(), - )?; - let transactions_trie: HashedPartialTrie = Node::Leaf { - nibbles: Nibbles::from_str("0x80").unwrap(), - value: txn.to_vec(), - } - .into(); - - let _trie_roots_after = TrieRoots { - state_root: expected_state_trie_after.hash(), - transactions_root: transactions_trie.hash(), - receipts_root: receipts_trie.hash(), - }; + let expected_smt_after = preinitialized_state_with_updated_storage(&block_metadata, &[]); let trie_roots_after = TrieRoots { - state_root: tries_before.state_trie.hash(), - transactions_root: tries_before.transactions_trie.hash(), - receipts_root: tries_before.receipts_trie.hash(), + state_root: H256::from_uint(&hashout2u(expected_smt_after.root)), + transactions_root: HashedPartialTrie::from(Node::Empty).hash(), + receipts_root: HashedPartialTrie::from(Node::Empty).hash(), }; + let inputs = GenerationInputs { - tries: tries_before.clone(), + tries: tries_before, trie_roots_after, contract_code, - checkpoint_state_trie_root: tries_before.state_trie.hash(), + checkpoint_state_trie_root, block_metadata, ..Default::default() }; - Ok(inputs) + Ok((inputs, expected_smt_after)) } fn get_test_block_proof( @@ -170,7 +80,7 @@ fn get_test_block_proof( all_stark: &AllStark, config: &StarkConfig, ) -> anyhow::Result> { - let inputs0 = empty_transfer(timestamp)?; + let (inputs0, state_smt) = empty_transfer(timestamp)?; let inputs = inputs0.clone(); let dummy0 = GenerationInputs { txn_number_before: inputs.txn_number_before, @@ -180,14 +90,13 @@ fn get_test_block_proof( global_exit_roots: vec![], withdrawals: vec![], tries: TrieInputs { - state_trie: HashedPartialTrie::from(Node::Hash(inputs.trie_roots_after.state_root)), + state_smt: state_smt.serialize(), transactions_trie: HashedPartialTrie::from(Node::Hash( inputs.trie_roots_after.transactions_root, )), receipts_trie: HashedPartialTrie::from(Node::Hash( inputs.trie_roots_after.receipts_root, )), - storage_tries: vec![], }, trie_roots_after: inputs.trie_roots_after, checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, @@ -244,7 +153,7 @@ fn test_two_to_one_block_aggregation() -> anyhow::Result<()> { let config = StarkConfig::standard_fast_config(); let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 9..15, 12..18, 14..15, 9..10, 12..13, 17..20], + &[16..17, 8..9, 13..14, 14..15, 10..11, 12..13, 17..18, 6..7], &config, ); diff --git a/evm_arithmetization/tests/withdrawals.rs b/evm_arithmetization/tests/withdrawals.rs index e17b775b1..4074da599 100644 --- a/evm_arithmetization/tests/withdrawals.rs +++ b/evm_arithmetization/tests/withdrawals.rs @@ -1,25 +1,23 @@ use std::collections::HashMap; use std::time::Duration; -use ethereum_types::{H160, H256, U256}; +use ethereum_types::{BigEndianHash, H160, H256, U256}; use evm_arithmetization::generation::mpt::AccountRlp; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::prove; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, - init_logger, preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, - GLOBAL_EXIT_ROOT_ACCOUNT, + init_logger, preinitialized_state, preinitialized_state_with_updated_storage, set_account, }; use evm_arithmetization::verifier::verify_proof; use evm_arithmetization::{AllStark, Node, StarkConfig}; -use keccak_hash::keccak; -use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; use rand::random; +use smt_trie::code::hash_bytecode_u256; +use smt_trie::utils::hashout2u; type F = GoldilocksField; const D: usize = 2; @@ -38,48 +36,28 @@ fn test_withdrawals() -> anyhow::Result<()> { ..BlockMetadata::default() }; - let (state_trie_before, storage_tries) = preinitialized_state_and_storage_tries()?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); + let state_smt_before = preinitialized_state(); let transactions_trie = HashedPartialTrie::from(Node::Empty); let receipts_trie = HashedPartialTrie::from(Node::Empty); let mut contract_code = HashMap::new(); - contract_code.insert(keccak(vec![]), vec![]); + contract_code.insert(hash_bytecode_u256(vec![]), vec![]); // Just one withdrawal. let withdrawals = vec![(H160(random()), U256(random()))]; - let state_trie_after = { - let mut trie = HashedPartialTrie::from(Node::Empty); - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); - - let addr_state_key = keccak(withdrawals[0].0); - let addr_nibbles = Nibbles::from_bytes_be(addr_state_key.as_bytes()).unwrap(); + let state_smt_after = { + let mut smt = preinitialized_state_with_updated_storage(&block_metadata, &[]); let account = AccountRlp { balance: withdrawals[0].1, ..AccountRlp::default() }; - trie.insert(addr_nibbles, rlp::encode(&account).to_vec())?; - trie.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; - trie.insert( - ger_account_nibbles(), - rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), - )?; - - trie + set_account(&mut smt, withdrawals[0].0, &account, &HashMap::new()); + smt }; let trie_roots_after = TrieRoots { - state_root: state_trie_after.hash(), + state_root: H256::from_uint(&hashout2u(state_smt_after.root)), transactions_root: transactions_trie.hash(), receipts_root: receipts_trie.hash(), }; @@ -89,10 +67,9 @@ fn test_withdrawals() -> anyhow::Result<()> { withdrawals, global_exit_roots: vec![], tries: TrieInputs { - state_trie: state_trie_before, + state_smt: state_smt_before.serialize(), transactions_trie, receipts_trie, - storage_tries, }, trie_roots_after, contract_code, diff --git a/proof_gen/Cargo.toml b/proof_gen/Cargo.toml index 785fbe8b0..fc380fba1 100644 --- a/proof_gen/Cargo.toml +++ b/proof_gen/Cargo.toml @@ -16,4 +16,5 @@ plonky2 = { workspace = true } serde = { workspace = true } # Local dependencies +# trace_decoder = "0.2.0" # TODO: adapt with type2 and bring back paths evm_arithmetization = { workspace = true } diff --git a/proof_gen/src/constants.rs b/proof_gen/src/constants.rs index 808f9f2b7..fb26de4f2 100644 --- a/proof_gen/src/constants.rs +++ b/proof_gen/src/constants.rs @@ -16,3 +16,5 @@ pub(crate) const DEFAULT_KECCAK_SPONGE_RANGE: Range = 9..25; pub(crate) const DEFAULT_LOGIC_RANGE: Range = 12..28; /// Default range to be used for the `MemoryStark` table. pub(crate) const DEFAULT_MEMORY_RANGE: Range = 17..30; +/// Default range to be used for the `PoseidonStark` table. +pub(crate) const DEFAULT_POSEIDON_RANGE: Range = 4..25; diff --git a/proof_gen/src/proof_gen.rs b/proof_gen/src/proof_gen.rs index e2a1f0daa..2b1dbda1f 100644 --- a/proof_gen/src/proof_gen.rs +++ b/proof_gen/src/proof_gen.rs @@ -11,6 +11,8 @@ use plonky2::{ util::timing::TimingTree, }; +// TODO: bring back import from trace_decoder once SMT logic is implemented +// use trace_decoder::types::TxnProofGenIR; use crate::{ proof_types::{ AggregatableBlockProof, AggregatableProof, GeneratedAggBlockProof, GeneratedAggProof, diff --git a/proof_gen/src/prover_state.rs b/proof_gen/src/prover_state.rs index 338cb52c7..8f8fc2f39 100644 --- a/proof_gen/src/prover_state.rs +++ b/proof_gen/src/prover_state.rs @@ -29,6 +29,7 @@ pub struct ProverStateBuilder { pub(crate) keccak_sponge_circuit_size: Range, pub(crate) logic_circuit_size: Range, pub(crate) memory_circuit_size: Range, + pub(crate) poseidon_circuit_size: Range, } impl Default for ProverStateBuilder { @@ -48,6 +49,7 @@ impl Default for ProverStateBuilder { keccak_sponge_circuit_size: DEFAULT_KECCAK_SPONGE_RANGE, logic_circuit_size: DEFAULT_LOGIC_RANGE, memory_circuit_size: DEFAULT_MEMORY_RANGE, + poseidon_circuit_size: DEFAULT_POSEIDON_RANGE, } } } @@ -73,6 +75,7 @@ impl ProverStateBuilder { define_set_circuit_size_method!(keccak_sponge); define_set_circuit_size_method!(logic); define_set_circuit_size_method!(memory); + define_set_circuit_size_method!(poseidon); // TODO: Consider adding async version? /// Instantiate the prover state from the builder. Note that this is a very @@ -90,6 +93,7 @@ impl ProverStateBuilder { self.keccak_sponge_circuit_size, self.logic_circuit_size, self.memory_circuit_size, + self.poseidon_circuit_size, ], &StarkConfig::standard_fast_config(), ); diff --git a/trace_decoder/Cargo.toml b/trace_decoder/Cargo.toml index f7a07bb7b..cc63ad44c 100644 --- a/trace_decoder/Cargo.toml +++ b/trace_decoder/Cargo.toml @@ -19,22 +19,25 @@ ciborium-io = { workspace = true } either = { workspace = true } enum-as-inner = { workspace = true } ethereum-types = { workspace = true } -evm_arithmetization = { workspace = true } hex = { workspace = true } hex-literal = { workspace = true } itertools.workspace = true keccak-hash = { workspace = true } log = { workspace = true } -mpt_trie = { workspace = true } nunny = { workspace = true, features = ["serde"] } plonky2 = { workspace = true } rlp = { workspace = true } serde = { workspace = true } -smt_trie = { workspace = true } thiserror = { workspace = true } u4 = { workspace = true } winnow = { workspace = true } +# Local dependencies +smt_trie = { workspace = true } +# TODO: update decoder to take local versions again +mpt_trie = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +evm_arithmetization = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } + [dev-dependencies] criterion = { workspace = true } pretty_env_logger = { workspace = true } diff --git a/zero_bin/common/Cargo.toml b/zero_bin/common/Cargo.toml index 9ee2ac5fe..01e32b96f 100644 --- a/zero_bin/common/Cargo.toml +++ b/zero_bin/common/Cargo.toml @@ -11,9 +11,7 @@ categories.workspace = true [dependencies] thiserror = { workspace = true } tracing = { workspace = true } -proof_gen = { workspace = true } plonky2 = { workspace = true } -evm_arithmetization = { workspace = true } clap = { workspace = true } anyhow = { workspace = true } serde = { workspace = true } @@ -22,3 +20,8 @@ futures = { workspace = true } tokio = { workspace = true } alloy = { workspace = true } async-stream = { workspace = true } + +# Local dependencies +# TODO: update deps to take local versions again +proof_gen = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +evm_arithmetization = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } \ No newline at end of file diff --git a/zero_bin/leader/Cargo.toml b/zero_bin/leader/Cargo.toml index dfb0070b4..3c442ccca 100644 --- a/zero_bin/leader/Cargo.toml +++ b/zero_bin/leader/Cargo.toml @@ -17,7 +17,6 @@ anyhow = { workspace = true } serde = { workspace = true } dotenvy = { workspace = true } tokio = { workspace = true } -proof_gen = { workspace = true } serde_json = { workspace = true } serde_path_to_error = { workspace = true } futures = { workspace = true } @@ -29,7 +28,9 @@ toml = { workspace = true } ops = { workspace = true } prover = { workspace = true } rpc = { workspace = true } -zero_bin_common = { workspace = true } +zero_bin_common ={ workspace = true } +# TODO: update deps to take local versions again +proof_gen = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } [features] default = [] diff --git a/zero_bin/ops/Cargo.toml b/zero_bin/ops/Cargo.toml index 05cfecb2e..85a253609 100644 --- a/zero_bin/ops/Cargo.toml +++ b/zero_bin/ops/Cargo.toml @@ -11,12 +11,14 @@ categories.workspace = true [dependencies] paladin-core = { workspace = true } serde = { workspace = true } -evm_arithmetization = { workspace = true } -proof_gen = { workspace = true } tracing = { workspace = true } keccak-hash = { workspace = true } -zero_bin_common = { path = "../common" } +# Local dependencies +# TODO: update deps to take local versions again +proof_gen = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +evm_arithmetization = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +zero_bin_common = { workspace = true } [features] default = [] diff --git a/zero_bin/prover/Cargo.toml b/zero_bin/prover/Cargo.toml index d1eaab7b3..ba216daab 100644 --- a/zero_bin/prover/Cargo.toml +++ b/zero_bin/prover/Cargo.toml @@ -10,8 +10,6 @@ categories.workspace = true [dependencies] serde = { workspace = true } -proof_gen = { workspace = true } -trace_decoder = { workspace = true } tracing = { workspace = true } paladin-core = { workspace = true } anyhow = { workspace = true } @@ -21,9 +19,14 @@ tokio = { workspace = true } serde_json = { workspace = true } ruint = { workspace = true, features = ["num-traits", "primitive-types"] } ops = { workspace = true } -zero_bin_common = { workspace = true } num-traits = { workspace = true } +# Local dependencies +# TODO: update deps to take local versions again +proof_gen = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +trace_decoder = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +zero_bin_common ={ workspace = true } + [features] default = [] test_only = ["ops/test_only"] diff --git a/zero_bin/rpc/Cargo.toml b/zero_bin/rpc/Cargo.toml index 6724c16fe..fe6970d96 100644 --- a/zero_bin/rpc/Cargo.toml +++ b/zero_bin/rpc/Cargo.toml @@ -13,16 +13,13 @@ __compat_primitive_types = { workspace = true } alloy.workspace = true anyhow = { workspace = true } clap = { workspace = true } -evm_arithmetization = { workspace = true } futures = { workspace = true } hex = { workspace = true } lru = { workspace = true } -mpt_trie = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } tower = { workspace = true, features = ["retry"] } -trace_decoder = { workspace = true } tracing-subscriber = { workspace = true } url = { workspace = true } @@ -30,3 +27,7 @@ url = { workspace = true } compat = { workspace = true } zero_bin_common = { workspace = true } prover = { workspace = true } +# TODO: update deps to take local versions again +mpt_trie = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +evm_arithmetization = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +trace_decoder = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } diff --git a/zero_bin/verifier/Cargo.toml b/zero_bin/verifier/Cargo.toml index 39968b4a6..b9f15ae4a 100644 --- a/zero_bin/verifier/Cargo.toml +++ b/zero_bin/verifier/Cargo.toml @@ -12,7 +12,8 @@ dotenvy = { workspace = true } anyhow = { workspace = true } serde_json = { workspace = true } serde_path_to_error = { workspace = true } -proof_gen = { workspace = true } # Local dependencies -zero_bin_common = { path = "../common" } +# TODO: update deps to take local versions again +proof_gen = { git = "https://github.com/0xPolygonZero/zk_evm", branch = "develop" } +zero_bin_common ={ path = "../common" }