From 77799227e0a96ee5f788edea0b8e0fa67ccc64b3 Mon Sep 17 00:00:00 2001 From: Danil Date: Sat, 23 Dec 2023 14:21:45 +0100 Subject: [PATCH 1/2] feat(cheatcodes): fork cheatcode (#205) Signed-off-by: Danil Co-authored-by: Nisheeth Barthwal --- Cargo.lock | 225 +++++----------- Cargo.toml | 15 +- crates/anvil/Cargo.toml | 1 + crates/era-cheatcodes/Cargo.toml | 2 + crates/era-cheatcodes/src/cheatcodes.rs | 255 +++++++++++++++++- crates/era-cheatcodes/tests/foundry.toml | 3 +- .../tests/src/cheatcodes/Addr.t.sol | 1 - .../tests/src/cheatcodes/Deal.t.sol | 1 - .../tests/src/cheatcodes/Etch.t.sol | 1 - .../tests/src/cheatcodes/ExpectCall.t.sol | 2 +- .../tests/src/cheatcodes/Ffi.t.sol | 2 - .../tests/src/cheatcodes/Fork.t.sol | 86 ++++++ .../tests/src/cheatcodes/Fs.t.sol | 2 - .../tests/src/cheatcodes/GetNonce.t.sol | 2 - .../tests/src/cheatcodes/ReadCallers.t.sol | 4 - .../tests/src/cheatcodes/Roll.t.sol | 1 - .../tests/src/cheatcodes/Serialize.t.sol | 3 - .../tests/src/cheatcodes/SetNonce.t.sol | 1 - .../tests/src/cheatcodes/StartPrank.t.sol | 3 - .../tests/src/cheatcodes/Store.t.sol | 1 - .../tests/src/cheatcodes/ToString.t.sol | 6 - .../tests/src/cheatcodes/Warp.t.sol | 1 - crates/evm/core/Cargo.toml | 5 +- crates/evm/core/src/backend/error.rs | 3 + crates/evm/core/src/backend/fuzz.rs | 5 +- crates/evm/core/src/backend/mod.rs | 8 +- crates/evm/core/src/era_revm/db.rs | 10 +- crates/evm/core/src/era_revm/transactions.rs | 68 +++-- crates/evm/core/src/fork/backend.rs | 70 ++++- crates/evm/core/src/fork/mod.rs | 1 + crates/evm/core/src/fork/multi.rs | 26 +- crates/evm/core/src/fork/zksync_provider.rs | 8 + crates/evm/evm/Cargo.toml | 1 - crates/evm/evm/src/inspectors/stack.rs | 9 +- crates/zkforge/src/runner.rs | 1 + smoke-test/src/Counter.sol | 4 +- 36 files changed, 584 insertions(+), 253 deletions(-) create mode 100644 crates/era-cheatcodes/tests/src/cheatcodes/Fork.t.sol create mode 100644 crates/evm/core/src/fork/zksync_provider.rs diff --git a/Cargo.lock b/Cargo.lock index 14339a143..4e574b669 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -637,6 +637,7 @@ dependencies = [ "trie-db", "vergen", "yansi 0.5.1", + "zksync_types", ] [[package]] @@ -2293,12 +2294,6 @@ dependencies = [ "serde", ] -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - [[package]] name = "const-oid" version = "0.9.5" @@ -2638,16 +2633,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array 0.14.7", - "subtle", -] - [[package]] name = "crypto-bigint" version = "0.4.9" @@ -2856,24 +2841,13 @@ dependencies = [ "uuid 1.5.0", ] -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid 0.7.1", - "crypto-bigint 0.3.2", - "pem-rfc7468", -] - [[package]] name = "der" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" dependencies = [ - "const-oid 0.9.5", + "const-oid", "zeroize", ] @@ -2883,7 +2857,8 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ - "const-oid 0.9.5", + "const-oid", + "pem-rfc7468", "zeroize", ] @@ -2974,7 +2949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", - "const-oid 0.9.5", + "const-oid", "crypto-common", "subtle", ] @@ -3300,7 +3275,9 @@ dependencies = [ "ethabi 18.0.0", "ethers", "eyre", + "foundry-cheatcodes", "foundry-cheatcodes-spec", + "foundry-common", "foundry-evm-core", "hashbrown 0.14.2", "hex", @@ -3320,8 +3297,8 @@ dependencies = [ [[package]] name = "era_test_node" -version = "0.1.0-alpha.12" -source = "git+https://github.com/matter-labs/era-test-node.git?rev=21b48af90a9f9d98ec38cb93a32c119a3266f401#21b48af90a9f9d98ec38cb93a32c119a3266f401" +version = "0.1.0-alpha.14" +source = "git+https://github.com/matter-labs/era-test-node.git?rev=6ee7d29e876b75506f58355218e1ea755a315d17#6ee7d29e876b75506f58355218e1ea755a315d17" dependencies = [ "anyhow", "bigdecimal", @@ -4428,7 +4405,6 @@ dependencies = [ "revm", "thiserror", "tracing", - "zksync_state", ] [[package]] @@ -4439,6 +4415,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-types", + "async-trait", "auto_impl", "const-hex", "derive_more", @@ -5198,9 +5175,9 @@ dependencies = [ [[package]] name = "google-cloud-auth" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f40175857d0b8d7b6cad6cd9594284da5041387fa2ddff30ab6d8faef65eb" +checksum = "af1087f1fbd2dd3f58c17c7574ddd99cd61cbbbc2c4dc81114b8687209b196cb" dependencies = [ "async-trait", "base64 0.21.4", @@ -5220,9 +5197,9 @@ dependencies = [ [[package]] name = "google-cloud-metadata" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e4ad0802d3f416f62e7ce01ac1460898ee0efc98f8b45cd4aab7611607012f" +checksum = "cc279bfb50487d7bcd900e8688406475fc750fe474a835b2ab9ade9eb1fc90e2" dependencies = [ "reqwest", "thiserror", @@ -5231,11 +5208,12 @@ dependencies = [ [[package]] name = "google-cloud-storage" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "215abab97e07d144428425509c1dad07e57ea72b84b21bcdb6a8a5f12a5c4932" +checksum = "ac04b29849ebdeb9fb008988cc1c4d1f0c9d121b4c7f1ddeb8061df124580e93" dependencies = [ "async-stream", + "async-trait", "base64 0.21.4", "bytes 1.5.0", "futures-util", @@ -5245,10 +5223,10 @@ dependencies = [ "hex", "once_cell", "percent-encoding", + "pkcs8 0.10.2", "regex", "reqwest", - "ring 0.16.20", - "rsa", + "ring 0.17.5", "serde", "serde_json", "sha2 0.10.8", @@ -7000,7 +6978,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "multivm" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "hex", @@ -7210,23 +7188,6 @@ dependencies = [ "serde", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-complex" version = "0.3.1" @@ -7597,7 +7558,7 @@ dependencies = [ [[package]] name = "pairing_ce" version = "0.28.5" -source = "git+https://github.com/matter-labs/pairing.git?rev=f55393f#f55393fd366596eac792d78525d26e9c4d6ed1ca" +source = "git+https://github.com/matter-labs/pairing.git?rev=f55393fd366596eac792d78525d26e9c4d6ed1ca#f55393fd366596eac792d78525d26e9c4d6ed1ca" dependencies = [ "byteorder", "cfg-if 1.0.0", @@ -7916,9 +7877,9 @@ dependencies = [ [[package]] name = "pem-rfc7468" -version = "0.3.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] @@ -8116,28 +8077,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" -dependencies = [ - "der 0.5.1", - "pkcs8 0.8.0", - "zeroize", -] - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der 0.5.1", - "spki 0.5.4", - "zeroize", -] - [[package]] name = "pkcs8" version = "0.9.0" @@ -8409,7 +8348,7 @@ dependencies = [ [[package]] name = "prometheus_exporter" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "metrics", @@ -9287,26 +9226,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rsa" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" -dependencies = [ - "byteorder", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8 0.8.0", - "rand_core 0.6.4", - "smallvec", - "subtle", - "zeroize", -] - [[package]] name = "rtoolbox" version = "0.0.1" @@ -10491,16 +10410,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der 0.5.1", -] - [[package]] name = "spki" version = "0.6.0" @@ -11865,7 +11774,7 @@ dependencies = [ [[package]] name = "vlog" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "chrono", "sentry", @@ -12857,7 +12766,7 @@ dependencies = [ [[package]] name = "zksync_basic_types" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "serde", "serde_json", @@ -12867,7 +12776,7 @@ dependencies = [ [[package]] name = "zksync_circuit_breaker" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "async-trait", @@ -12890,7 +12799,7 @@ dependencies = [ [[package]] name = "zksync_commitment_utils" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "zkevm_test_harness 1.4.0", "zksync_types", @@ -12900,7 +12809,7 @@ dependencies = [ [[package]] name = "zksync_concurrency" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "once_cell", @@ -12918,7 +12827,7 @@ dependencies = [ [[package]] name = "zksync_config" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "serde", @@ -12928,9 +12837,10 @@ dependencies = [ [[package]] name = "zksync_consensus_bft" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", + "async-trait", "once_cell", "rand 0.8.5", "thiserror", @@ -12948,14 +12858,14 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "blst", "ed25519-dalek", "ff_ce", "hex", - "pairing_ce 0.28.5 (git+https://github.com/matter-labs/pairing.git?rev=f55393f)", + "pairing_ce 0.28.5 (git+https://github.com/matter-labs/pairing.git?rev=f55393fd366596eac792d78525d26e9c4d6ed1ca)", "rand 0.4.6", "rand 0.8.5", "sha3 0.10.8", @@ -12966,7 +12876,7 @@ dependencies = [ [[package]] name = "zksync_consensus_executor" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "prost", @@ -12988,7 +12898,7 @@ dependencies = [ [[package]] name = "zksync_consensus_network" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "async-trait", @@ -13012,7 +12922,7 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "bit-vec", @@ -13020,6 +12930,7 @@ dependencies = [ "prost", "rand 0.8.5", "serde", + "thiserror", "tracing", "zksync_concurrency", "zksync_consensus_crypto", @@ -13031,7 +12942,7 @@ dependencies = [ [[package]] name = "zksync_consensus_storage" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "async-trait", @@ -13048,7 +12959,7 @@ dependencies = [ [[package]] name = "zksync_consensus_sync_blocks" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "thiserror", @@ -13063,7 +12974,7 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "thiserror", "zksync_concurrency", @@ -13072,7 +12983,7 @@ dependencies = [ [[package]] name = "zksync_contracts" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "envy", "ethabi 18.0.0", @@ -13086,7 +12997,7 @@ dependencies = [ [[package]] name = "zksync_core" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "actix-cors", "actix-rt", @@ -13108,6 +13019,7 @@ dependencies = [ "jsonrpc-http-server", "jsonrpc-pubsub", "jsonrpc-ws-server", + "lru", "metrics", "multivm", "num 0.3.1", @@ -13129,6 +13041,7 @@ dependencies = [ "zksync_commitment_utils", "zksync_concurrency", "zksync_config", + "zksync_consensus_bft", "zksync_consensus_executor", "zksync_consensus_roles", "zksync_consensus_storage", @@ -13157,7 +13070,7 @@ dependencies = [ [[package]] name = "zksync_crypto" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "base64 0.13.1", "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -13172,7 +13085,7 @@ dependencies = [ [[package]] name = "zksync_dal" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "bigdecimal", @@ -13181,6 +13094,7 @@ dependencies = [ "itertools 0.10.5", "num 0.3.1", "once_cell", + "prost", "rand 0.8.5", "serde", "serde_json", @@ -13191,8 +13105,12 @@ dependencies = [ "tracing", "url", "vise", + "zksync_consensus_roles", + "zksync_consensus_storage", "zksync_contracts", "zksync_health_check", + "zksync_protobuf", + "zksync_protobuf_build", "zksync_system_constants", "zksync_types", "zksync_utils", @@ -13201,7 +13119,7 @@ dependencies = [ [[package]] name = "zksync_eth_client" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "async-trait", @@ -13221,7 +13139,7 @@ dependencies = [ [[package]] name = "zksync_eth_signer" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "async-trait", "hex", @@ -13240,7 +13158,7 @@ dependencies = [ [[package]] name = "zksync_health_check" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "async-trait", "futures 0.3.28", @@ -13253,7 +13171,7 @@ dependencies = [ [[package]] name = "zksync_mempool" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "tracing", "zksync_types", @@ -13262,7 +13180,7 @@ dependencies = [ [[package]] name = "zksync_merkle_tree" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "leb128", "once_cell", @@ -13279,7 +13197,7 @@ dependencies = [ [[package]] name = "zksync_mini_merkle_tree" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "once_cell", "zksync_basic_types", @@ -13289,14 +13207,16 @@ dependencies = [ [[package]] name = "zksync_object_store" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "async-trait", "bincode", + "flate2", "google-cloud-auth", "google-cloud-storage", "http", + "serde_json", "tokio", "tracing", "vise", @@ -13307,7 +13227,7 @@ dependencies = [ [[package]] name = "zksync_protobuf" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "bit-vec", @@ -13325,7 +13245,7 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" version = "0.1.0" -source = "git+https://github.com/matter-labs/era-consensus.git?rev=ed71b2e817c980a2daffef6a01885219e1dc6fa0#ed71b2e817c980a2daffef6a01885219e1dc6fa0" +source = "git+https://github.com/matter-labs/era-consensus.git?rev=49b1a98f80d0e9f74fdceadece4283e745c71599#49b1a98f80d0e9f74fdceadece4283e745c71599" dependencies = [ "anyhow", "heck 0.4.1", @@ -13341,7 +13261,7 @@ dependencies = [ [[package]] name = "zksync_prover_utils" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "async-trait", @@ -13361,7 +13281,7 @@ dependencies = [ [[package]] name = "zksync_queued_job_processor" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "async-trait", @@ -13374,7 +13294,7 @@ dependencies = [ [[package]] name = "zksync_state" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "itertools 0.10.5", @@ -13391,7 +13311,7 @@ dependencies = [ [[package]] name = "zksync_storage" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "num_cpus", "once_cell", @@ -13403,7 +13323,7 @@ dependencies = [ [[package]] name = "zksync_system_constants" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "bigdecimal", @@ -13421,7 +13341,7 @@ dependencies = [ [[package]] name = "zksync_types" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "blake2 0.10.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -13433,7 +13353,6 @@ dependencies = [ "num_enum 0.6.1", "once_cell", "parity-crypto", - "prost", "rlp", "serde", "serde_json", @@ -13456,7 +13375,7 @@ dependencies = [ [[package]] name = "zksync_utils" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "bigdecimal", @@ -13478,7 +13397,7 @@ dependencies = [ [[package]] name = "zksync_verification_key_generator_and_server" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "anyhow", "bincode", @@ -13498,7 +13417,7 @@ dependencies = [ [[package]] name = "zksync_web3_decl" version = "0.1.0" -source = "git+https://github.com/matter-labs/zksync-era.git?rev=bd268ac02bc3530c1d3247cb9496c3e13c2e52d9#bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" +source = "git+https://github.com/matter-labs/zksync-era.git?rev=3c669e7b0caf6515ad865f4cba5ea6fb36c33811#3c669e7b0caf6515ad865f4cba5ea6fb36c33811" dependencies = [ "bigdecimal", "chrono", diff --git a/Cargo.toml b/Cargo.toml index cbbcc771d..20bea1b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -159,13 +159,13 @@ alloy-rlp = "0.3.3" solang-parser = "=0.3.3" ## zksync -era_test_node = { git = "https://github.com/matter-labs/era-test-node.git", rev = "21b48af90a9f9d98ec38cb93a32c119a3266f401" } -zksync_basic_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" } -zksync_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" } -zksync_state = { git = "https://github.com/matter-labs/zksync-era.git", rev = "bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" } -multivm = { git = "https://github.com/matter-labs/zksync-era.git", rev = "bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" } -zksync_web3_decl = { git = "https://github.com/matter-labs/zksync-era.git", rev = "bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" } -zksync_utils = { git = "https://github.com/matter-labs/zksync-era.git", rev = "bd268ac02bc3530c1d3247cb9496c3e13c2e52d9" } +era_test_node = { git="https://github.com/matter-labs/era-test-node.git" , rev = "6ee7d29e876b75506f58355218e1ea755a315d17" } +zksync_basic_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "3c669e7b0caf6515ad865f4cba5ea6fb36c33811" } +zksync_types = { git = "https://github.com/matter-labs/zksync-era.git", rev = "3c669e7b0caf6515ad865f4cba5ea6fb36c33811" } +zksync_state = { git = "https://github.com/matter-labs/zksync-era.git", rev = "3c669e7b0caf6515ad865f4cba5ea6fb36c33811" } +multivm = { git = "https://github.com/matter-labs/zksync-era.git", rev = "3c669e7b0caf6515ad865f4cba5ea6fb36c33811" } +zksync_web3_decl = { git = "https://github.com/matter-labs/zksync-era.git", rev = "3c669e7b0caf6515ad865f4cba5ea6fb36c33811" } +zksync_utils = { git = "https://github.com/matter-labs/zksync-era.git", rev = "3c669e7b0caf6515ad865f4cba5ea6fb36c33811" } ## misc chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } @@ -175,6 +175,7 @@ eyre = "0.6" hex = { package = "const-hex", version = "1.6", features = ["hex"] } itertools = "0.11" jsonpath_lib = "0.3" +maplit = "1" pretty_assertions = "1.4" protobuf = "=3.2.0" rand = "0.8" diff --git a/crates/anvil/Cargo.toml b/crates/anvil/Cargo.toml index dbced4c69..efc9d5974 100644 --- a/crates/anvil/Cargo.toml +++ b/crates/anvil/Cargo.toml @@ -26,6 +26,7 @@ anvil-server = { path = "server" } foundry-common.workspace = true foundry-config.workspace = true foundry-evm.workspace = true +zksync_types.workspace = true # evm support bytes = "1.4.0" diff --git a/crates/era-cheatcodes/Cargo.toml b/crates/era-cheatcodes/Cargo.toml index 71cdb9f5c..5676b13e4 100644 --- a/crates/era-cheatcodes/Cargo.toml +++ b/crates/era-cheatcodes/Cargo.toml @@ -20,7 +20,9 @@ multivm.workspace = true zksync_web3_decl.workspace = true zksync_utils.workspace = true foundry-evm-core.workspace = true +foundry-common.workspace = true foundry-cheatcodes-spec.workspace = true +foundry-cheatcodes.workspace = true alloy-sol-types = { workspace = true, features = ["json"] } ethabi = "18.0.0" diff --git a/crates/era-cheatcodes/src/cheatcodes.rs b/crates/era-cheatcodes/src/cheatcodes.rs index 3a59a8785..68bca7e01 100644 --- a/crates/era-cheatcodes/src/cheatcodes.rs +++ b/crates/era-cheatcodes/src/cheatcodes.rs @@ -1,14 +1,25 @@ use crate::utils::{ToH160, ToH256, ToU256}; use alloy_sol_types::{SolInterface, SolValue}; -use era_test_node::{fork::ForkStorage, utils::bytecode_to_factory_dep}; +use era_test_node::{ + deps::storage_view::StorageView, fork::ForkStorage, utils::bytecode_to_factory_dep, +}; use ethers::utils::to_checksum; +use foundry_cheatcodes::CheatsConfig; use foundry_cheatcodes_spec::Vm; -use foundry_evm_core::{backend::DatabaseExt, era_revm::db::RevmDatabaseForEra}; +use foundry_evm_core::{ + backend::DatabaseExt, + era_revm::{db::RevmDatabaseForEra, transactions::storage_to_state}, + fork::CreateFork, + opts::EvmOpts, +}; use itertools::Itertools; use multivm::{ - interface::{dyn_tracers::vm_1_3_3::DynTracer, tracer::TracerExecutionStatus}, - vm_refunds_enhancement::{BootloaderState, HistoryMode, SimpleMemory, VmTracer, ZkSyncVmState}, - zk_evm_1_3_3::{ + interface::{dyn_tracers::vm_1_4_0::DynTracer, tracer::TracerExecutionStatus}, + vm_latest::{ + BootloaderState, HistoryMode, L1BatchEnv, SimpleMemory, SystemEnv, VmTracer, ZkSyncVmState, + }, + zk_evm_1_4_0::{ + reference_impls::event_sink::EventMessage, tracing::{AfterExecutionData, VmLocalStateData}, vm_state::PrimitiveValue, zkevm_opcode_defs::{ @@ -17,21 +28,26 @@ use multivm::{ }, }, }; +use revm::{ + primitives::{BlockEnv, CfgEnv, Env, SpecId, U256 as rU256}, + JournaledState, +}; use serde::Serialize; use std::{ - cell::RefMut, + cell::{OnceCell, RefMut}, collections::{hash_map::Entry, HashMap, HashSet}, fmt::Debug, fs, process::Command, + sync::Arc, }; use zksync_basic_types::{AccountTreeId, H160, H256, U256}; -use zksync_state::{ReadStorage, StoragePtr, StorageView, WriteStorage}; +use zksync_state::{ReadStorage, StoragePtr, WriteStorage}; use zksync_types::{ block::{pack_block_info, unpack_block_info}, get_code_key, get_nonce_key, utils::{decompose_full_nonce, nonces_to_full_nonce, storage_key_for_eth_balance}, - EventMessage, LogQuery, StorageKey, Timestamp, + LogQuery, StorageKey, Timestamp, }; use zksync_utils::{h256_to_u256, u256_to_h256}; @@ -69,6 +85,12 @@ const INTERNAL_CONTRACT_ADDRESSES: [H160; 20] = [ H160::zero(), ]; +#[derive(Debug, Clone)] +struct EraEnv { + l1_batch_env: L1BatchEnv, + system_env: SystemEnv, +} + /// Represents the state of a foundry test function, i.e. functions /// prefixed with "testXXX" #[derive(Debug, Clone, Default, PartialEq, Eq)] @@ -90,6 +112,8 @@ pub struct CheatcodeTracer { return_ptr: Option, near_calls: usize, serialized_objects: HashMap, + env: OnceCell, + config: Arc, recorded_logs: HashSet, recording_logs: bool, recording_timestamp: u32, @@ -114,6 +138,9 @@ impl LogEntry { enum FinishCycleOneTimeActions { StorageWrite { key: StorageKey, read_value: H256, write_value: H256 }, StoreFactoryDep { hash: U256, bytecode: Vec }, + CreateSelectFork { url_or_alias: String, block_number: Option }, + CreateFork { url_or_alias: String, block_number: Option }, + SelectFork { fork_id: U256 }, } #[derive(Debug, Default, Clone)] @@ -251,7 +278,7 @@ impl DynTracer, SimpleMemory> tracing::error!("cheatcode triggered, but no calldata or ergs available"); return } - tracing::info!("near call: cheatcode triggered"); + tracing::info!("far call: cheatcode triggered"); let calldata = get_calldata(&state, memory); // try to dispatch the cheatcode @@ -268,10 +295,22 @@ impl DynTracer, SimpleMemory> } impl VmTracer, H> for CheatcodeTracer { + fn initialize_tracer( + &mut self, + _state: &mut ZkSyncVmState, H>, + l1_batch_env: &L1BatchEnv, + system_env: &SystemEnv, + ) { + self.env + .set(EraEnv { l1_batch_env: l1_batch_env.clone(), system_env: system_env.clone() }) + .unwrap(); + } + fn finish_cycle( &mut self, state: &mut ZkSyncVmState, H>, - _bootloader_state: &mut BootloaderState, + bootloader_state: &mut BootloaderState, + storage: StoragePtr>, ) -> TracerExecutionStatus { let emitter = state.local_state.callstack.current.this_address; if self.recording_logs { @@ -310,6 +349,104 @@ impl VmTracer, H> for CheatcodeT FinishCycleOneTimeActions::StoreFactoryDep { hash, bytecode } => state .decommittment_processor .populate(vec![(hash, bytecode)], Timestamp(state.local_state.timestamp)), + FinishCycleOneTimeActions::CreateSelectFork { url_or_alias, block_number } => { + let modified_storage: HashMap = storage + .borrow_mut() + .modified_storage_keys() + .clone() + .into_iter() + .filter(|(key, _)| key.address() != &zksync_types::SYSTEM_CONTEXT_ADDRESS) + .collect(); + storage.borrow_mut().clean_cache(); + let fork_id = { + let handle: &ForkStorage> = + &storage.borrow_mut().storage_handle; + let mut fork_storage = handle.inner.write().unwrap(); + fork_storage.value_read_cache.clear(); + let era_db = fork_storage.fork.as_ref().unwrap().fork_source.clone(); + let bytecodes = bootloader_state + .get_last_tx_compressed_bytecodes() + .iter() + .map(|b| bytecode_to_factory_dep(b.original.clone())) + .collect(); + + let mut journaled_state = JournaledState::new(SpecId::LATEST, vec![]); + let state = storage_to_state(&era_db, &modified_storage, bytecodes); + *journaled_state.state() = state; + + let mut db = era_db.db.lock().unwrap(); + let era_env = self.env.get().unwrap(); + let mut env = into_revm_env(era_env); + db.create_select_fork( + create_fork_request( + era_env, + self.config.clone(), + block_number, + &url_or_alias, + ), + &mut env, + &mut journaled_state, + ) + }; + storage.borrow_mut().modified_storage_keys = modified_storage; + + self.return_data = Some(vec![fork_id.unwrap().to_u256()]); + } + FinishCycleOneTimeActions::CreateFork { url_or_alias, block_number } => { + let handle: &ForkStorage> = + &storage.borrow_mut().storage_handle; + let era_db = + handle.inner.write().unwrap().fork.as_ref().unwrap().fork_source.clone(); + + let mut db = era_db.db.lock().unwrap(); + let era_env = self.env.get().unwrap(); + let fork_id = db.create_fork(create_fork_request( + era_env, + self.config.clone(), + block_number, + &url_or_alias, + )); + self.return_data = Some(vec![fork_id.unwrap().to_u256()]); + } + FinishCycleOneTimeActions::SelectFork { fork_id } => { + let modified_storage: HashMap = storage + .borrow_mut() + .modified_storage_keys() + .clone() + .into_iter() + .filter(|(key, _)| key.address() != &zksync_types::SYSTEM_CONTEXT_ADDRESS) + .collect(); + { + storage.borrow_mut().clean_cache(); + let handle: &ForkStorage> = + &storage.borrow_mut().storage_handle; + let mut fork_storage = handle.inner.write().unwrap(); + fork_storage.value_read_cache.clear(); + let era_db = fork_storage.fork.as_ref().unwrap().fork_source.clone(); + let bytecodes = bootloader_state + .get_last_tx_compressed_bytecodes() + .iter() + .map(|b| bytecode_to_factory_dep(b.original.clone())) + .collect(); + + let mut journaled_state = JournaledState::new(SpecId::LATEST, vec![]); + let state = storage_to_state(&era_db, &modified_storage, bytecodes); + *journaled_state.state() = state; + + let mut db = era_db.db.lock().unwrap(); + let era_env = self.env.get().unwrap(); + let mut env = into_revm_env(era_env); + db.select_fork( + rU256::from(fork_id.as_u128()), + &mut env, + &mut journaled_state, + ) + .unwrap(); + } + storage.borrow_mut().modified_storage_keys = modified_storage; + + self.return_data = Some(vec![fork_id]); + } } } @@ -341,8 +478,8 @@ impl VmTracer, H> for CheatcodeT } impl CheatcodeTracer { - pub fn new() -> Self { - CheatcodeTracer::default() + pub fn new(cheatcodes_config: Arc) -> Self { + Self { config: cheatcodes_config, ..Default::default() } } /// Resets the test state to [TestStatus::NotStarted] @@ -821,6 +958,48 @@ impl CheatcodeTracer { &mut storage, ); } + createSelectFork_0(createSelectFork_0Call { urlOrAlias }) => { + tracing::info!("👷 Creating and selecting fork {}", urlOrAlias,); + + self.one_time_actions.push(FinishCycleOneTimeActions::CreateSelectFork { + url_or_alias: urlOrAlias, + block_number: None, + }); + } + createSelectFork_1(createSelectFork_1Call { urlOrAlias, blockNumber }) => { + let block_number = blockNumber.to_u256().as_u64(); + tracing::info!( + "👷 Creating and selecting fork {} for block number {}", + urlOrAlias, + block_number + ); + self.one_time_actions.push(FinishCycleOneTimeActions::CreateSelectFork { + url_or_alias: urlOrAlias, + block_number: Some(block_number), + }); + } + createFork_0(createFork_0Call { urlOrAlias }) => { + tracing::info!("👷 Creating fork {}", urlOrAlias,); + + self.one_time_actions.push(FinishCycleOneTimeActions::CreateFork { + url_or_alias: urlOrAlias, + block_number: None, + }); + } + createFork_1(createFork_1Call { urlOrAlias, blockNumber }) => { + let block_number = blockNumber.to_u256().as_u64(); + tracing::info!("👷 Creating fork {} for block number {}", urlOrAlias, block_number); + self.one_time_actions.push(FinishCycleOneTimeActions::CreateFork { + url_or_alias: urlOrAlias, + block_number: Some(block_number), + }); + } + selectFork(selectForkCall { forkId }) => { + tracing::info!("👷 Selecting fork {}", forkId); + + self.one_time_actions + .push(FinishCycleOneTimeActions::SelectFork { fork_id: forkId.to_u256() }); + } writeFile(writeFileCall { path, data }) => { tracing::info!("👷 Writing data to file in path {}", path); if fs::write(path, data).is_err() { @@ -966,6 +1145,58 @@ fn transform_to_logs(events: Vec, emitter: H160) -> Vec .collect() } +fn into_revm_env(env: &EraEnv) -> Env { + use foundry_common::zk_utils::conversion_utils::h160_to_address; + use revm::primitives::U256; + let block = BlockEnv { + number: U256::from(env.l1_batch_env.first_l2_block.number), + coinbase: h160_to_address(env.l1_batch_env.fee_account), + timestamp: U256::from(env.l1_batch_env.first_l2_block.timestamp), + gas_limit: U256::from(env.system_env.gas_limit), + basefee: U256::from(env.l1_batch_env.base_fee()), + ..Default::default() + }; + + let mut cfg = CfgEnv::default(); + cfg.chain_id = env.system_env.chain_id.as_u64(); + + Env { block, cfg, ..Default::default() } +} + +fn create_fork_request( + env: &EraEnv, + config: Arc, + block_number: Option, + url_or_alias: &str, +) -> CreateFork { + use foundry_evm_core::opts::Env; + use revm::primitives::Address as revmAddress; + + let url = config.rpc_url(url_or_alias).unwrap(); + let env = into_revm_env(env); + let opts_env = Env { + gas_limit: u64::MAX, + chain_id: None, + tx_origin: revmAddress::ZERO, + block_number: 0, + block_timestamp: 0, + ..Default::default() + }; + let evm_opts = EvmOpts { + env: opts_env, + fork_url: Some(url.clone()), + fork_block_number: block_number, + ..Default::default() + }; + + CreateFork { + enable_caching: config.rpc_storage_caching.enable_for_endpoint(&url), + url, + env, + evm_opts, + } +} + fn get_calldata(state: &VmLocalStateData<'_>, memory: &SimpleMemory) -> Vec { let ptr = state.vm_local_state.registers[CALL_IMPLICIT_CALLDATA_FAT_PTR_REGISTER as usize]; assert!(ptr.is_pointer); diff --git a/crates/era-cheatcodes/tests/foundry.toml b/crates/era-cheatcodes/tests/foundry.toml index ea122abec..c8d63ecd6 100644 --- a/crates/era-cheatcodes/tests/foundry.toml +++ b/crates/era-cheatcodes/tests/foundry.toml @@ -5,5 +5,6 @@ libs = ['lib'] [rpc_endpoints] local = "${ERA_TEST_NODE_RPC_URL}" - +mainnet = "https://mainnet.era.zksync.io:443" +testnet = "https://testnet.era.zksync.dev:443" # See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Addr.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Addr.t.sol index e6c0418d7..74b2f6397 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Addr.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Addr.t.sol @@ -15,6 +15,5 @@ contract AddrTest is Test { require(success, "addr failed"); address addr = abi.decode(data, (address)); assertEq(addr, expected, "expected address did not match"); - console.log("failed?", failed()); } } \ No newline at end of file diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Deal.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Deal.t.sol index 0b83e3218..4d8064bfc 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Deal.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Deal.t.sol @@ -25,6 +25,5 @@ contract CheatcodeDealTest is Test { require(balanceAfter == NEW_BALANCE, "balance mismatch"); require(balanceAfter != balanceBefore, "balance unchanged"); require(success, "deal failed"); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Etch.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Etch.t.sol index 199e2118a..4dccad7c1 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Etch.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Etch.t.sol @@ -23,6 +23,5 @@ contract CheatcodeEtchTest is Test { abi.encodeWithSignature("setGreeting(string)", "hello world") ); require(success, "setGreeting failed"); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/ExpectCall.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/ExpectCall.t.sol index defc0c56d..64cac8608 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/ExpectCall.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/ExpectCall.t.sol @@ -732,7 +732,7 @@ contract ExpectCallMixedTest is Test { uint256 times ) public pure { for (uint256 i = 0; i < times; i++) { - target.add(1, 2); + target.add(a, b); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Ffi.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Ffi.t.sol index a70ba44e8..1323df547 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Ffi.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Ffi.t.sol @@ -25,8 +25,6 @@ contract FfiTest is Test { keccak256(bytes(output)) == keccak256(bytes("ffi works")), "ffi failed" ); - - console.log("failed?", failed()); } function testFfiString() public { diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Fork.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Fork.t.sol new file mode 100644 index 000000000..35a632be6 --- /dev/null +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Fork.t.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console2 as console} from "../../lib/forge-std/src/Test.sol"; +import {Constants} from "./Constants.sol"; + +contract ForkTest is Test { + /// USDC TOKEN + address constant TOKEN_ADDRESS = 0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4; + uint256 constant TOKEN_DECIMALS = 6; + uint256 constant FORK_BLOCK = 19579636; + + function setUp() public { + /// USDC TOKEN doesn't exists locally + (bool success, bytes memory data) = TOKEN_ADDRESS.call( + abi.encodeWithSignature("decimals()") + ); + require(success, "decimals() failed"); + uint256 decimals_before = uint256(bytes32(data)); + require( + block.number < 1000, + "Local node doesn't have blocks above 1000" + ); + (bool success2, ) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature( + "createSelectFork(string,uint256)", + "mainnet", + FORK_BLOCK + ) + ); + require(decimals_before == 0, "Contract exists locally"); + require(success2, "fork failed"); + } + + function testFork() public { + /// After createSelect fork the decimals should exist + (bool success, bytes memory data2) = TOKEN_ADDRESS.call( + abi.encodeWithSignature("decimals()") + ); + require(success, "decimals() failed"); + uint256 decimals_after = uint256(bytes32(data2)); + console.log("decimals_after", decimals_after); + require( + decimals_after == TOKEN_DECIMALS, + "Contract dosent exists in fork" + ); + require( + block.number == FORK_BLOCK + 1, + "ENV for blocks is not set correctly" + ); + } + + function testCreateSelectFork() public { + (bool success, bytes memory data) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature( + "createFork(string,uint256)", + "mainnet", + FORK_BLOCK + 100 + ) + ); + require(success, "fork failed"); + + uint256 forkId = uint256(bytes32(data)); + (bool success1, ) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature("selectFork(uint256)", forkId) + ); + require(success1, "select fork failed"); + + /// After createSelect fork the decimals should exist + (bool success2, bytes memory data2) = TOKEN_ADDRESS.call( + abi.encodeWithSignature("decimals()") + ); + require(success2, "decimals() failed"); + uint256 decimals_after = uint256(bytes32(data2)); + console.log("decimals_after", decimals_after); + console.log("block ", block.number); + require( + decimals_after == TOKEN_DECIMALS, + "Contract dosent exists in fork" + ); + require( + block.number == FORK_BLOCK + 100, + "ENV for blocks is not set correctly" + ); + } +} diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Fs.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Fs.t.sol index 5f6cb6b79..5f32c3836 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Fs.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Fs.t.sol @@ -21,7 +21,6 @@ contract FsTest is Test { keccak256("hello readable world\nthis is the second line!\n"), "read data did not match expected data" ); - console.log("failed?", failed()); } function testWriteFile() public { @@ -45,6 +44,5 @@ contract FsTest is Test { keccak256(readData) == keccak256(bytes(writeData)), "read data did not match write data" ); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/GetNonce.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/GetNonce.t.sol index 12c0f9f10..b7618aa3c 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/GetNonce.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/GetNonce.t.sol @@ -17,7 +17,6 @@ contract CheatcodeSetNonceTest is Test { ) ); require(success, "setNonce failed"); - console.log("failed?", failed()); //test getNonce (bool success2, bytes memory data2) = Constants.CHEATCODE_ADDRESS.call( @@ -27,7 +26,6 @@ contract CheatcodeSetNonceTest is Test { uint256 nonce = abi.decode(data2, (uint256)); console.log("nonce: 0x", nonce); require(nonce == NEW_NONCE, "nonce was not changed"); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/ReadCallers.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/ReadCallers.t.sol index e63c72d9f..8d73d33ef 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/ReadCallers.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/ReadCallers.t.sol @@ -41,8 +41,6 @@ contract CheatcodeReadCallers is Test { require(mode == 4, "recurrent prank call mode"); require(sender == TEST_ADDRESS, "sender overridden"); require(origin == tx.origin, "origin not overridden"); - - console.log("failed?", failed()); } function testFullyPrankedReadCallers() public { @@ -60,7 +58,5 @@ contract CheatcodeReadCallers is Test { require(mode == 4, "recurrent prank call mode"); require(sender == TEST_ADDRESS, "sender overridden"); require(origin == TEST_ORIGIN, "origin overridden"); - - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Roll.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Roll.t.sol index 9a706cc93..c26381931 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Roll.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Roll.t.sol @@ -28,6 +28,5 @@ contract CheatcodeRollTest is Test { finalBlockNumber == NEW_BLOCK_NUMBER, "block number was not changed" ); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Serialize.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Serialize.t.sol index e2109f41f..6ee5069f7 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Serialize.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Serialize.t.sol @@ -25,7 +25,6 @@ contract CheatcodeSerializeTest is Test { keccak256(bytes("0x6Eb28604685b1F182dAB800A1Bfa4BaFdBA8a79a")), "serializeAddress mismatch" ); - console.log("failed?", failed()); } function testSerializeBool() external { @@ -44,7 +43,6 @@ contract CheatcodeSerializeTest is Test { keccak256(bytes(testString)) == keccak256(bytes("true")), "serializeBool mismatch" ); - console.log("failed?", failed()); } function testSerializeUint() external { @@ -63,6 +61,5 @@ contract CheatcodeSerializeTest is Test { keccak256(bytes(testString)) == keccak256(bytes("99")), "serializeUint mismatch" ); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/SetNonce.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/SetNonce.t.sol index 29a99ee0e..c6bb4ff63 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/SetNonce.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/SetNonce.t.sol @@ -17,6 +17,5 @@ contract CheatcodeSetNonceTest is Test { ) ); require(success, "setNonce failed"); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/StartPrank.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/StartPrank.t.sol index 3b5a73b82..b527b7f58 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/StartPrank.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/StartPrank.t.sol @@ -64,7 +64,6 @@ contract CheatcodeStartPrankTest is Test { "startPrank failed: victim.assertCallerAndOrigin failed" ); - console.log("failed?", failed()); } function testStartPrankWithOrigin() external { @@ -126,8 +125,6 @@ contract CheatcodeStartPrankTest is Test { original_tx_origin, "startPrank failed: victim.assertCallerAndOrigin failed" ); - - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Store.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Store.t.sol index 7e22a7e41..e620d0f5d 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Store.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Store.t.sol @@ -32,6 +32,5 @@ contract StoreTest is Test { require(success, "store failed"); assertEq(store.slot0(), 1, "store failed"); assertEq(store.slot1(), 20, "store failed"); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/ToString.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/ToString.t.sol index e5f78f2fc..01cc8f0ca 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/ToString.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/ToString.t.sol @@ -19,7 +19,6 @@ contract CheatcodeToStringTest is Test { keccak256(bytes("0x413D15117be7a498e68A64FcfdB22C6e2AaE1808")), "toString mismatch" ); - console.log("failed?", failed()); } function testToStringFromBool() external { @@ -44,7 +43,6 @@ contract CheatcodeToStringTest is Test { keccak256(bytes(testString)) == keccak256(bytes("true")), "toString mismatch" ); - console.log("failed?", failed()); } function testToStringFromUint256() external { @@ -60,7 +58,6 @@ contract CheatcodeToStringTest is Test { keccak256(bytes(testString)) == keccak256(bytes(stringValue)), "toString mismatch" ); - console.log("failed?", failed()); } function testToStringFromInt256() external { @@ -76,7 +73,6 @@ contract CheatcodeToStringTest is Test { keccak256(bytes(testString)) == keccak256(bytes(stringValue)), "toString mismatch" ); - console.log("failed?", failed()); } function testToStringFromBytes32() external { @@ -96,7 +92,6 @@ contract CheatcodeToStringTest is Test { ), "toString mismatch" ); - console.log("failed?", failed()); } function testToStringFromBytes() external { @@ -117,6 +112,5 @@ contract CheatcodeToStringTest is Test { ), "toString mismatch" ); - console.log("failed?", failed()); } } diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Warp.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Warp.t.sol index 336dec06c..1d66940e7 100644 --- a/crates/era-cheatcodes/tests/src/cheatcodes/Warp.t.sol +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Warp.t.sol @@ -26,6 +26,5 @@ contract CheatcodeWarpTest is Test { finalTimestamp == NEW_BLOCK_TIMESTAMP, "timestamp was not changed" ); - console.log("failed?", failed()); } } diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 82e6ee021..fb8f93e14 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -40,7 +40,6 @@ zksync_types.workspace = true era_test_node.workspace = true zksync_utils.workspace = true zksync_web3_decl.workspace = true -zksync_state.workspace = true derive_more.workspace = true eyre = "0.6" @@ -56,6 +55,8 @@ tokio = { version = "1", features = ["time", "macros"] } tracing = "0.1" url = "2" auto_impl = "1" +async-trait = "0.1" [dev-dependencies] -maplit = "1" +zksync_state.workspace = true +maplit.workspace = true \ No newline at end of file diff --git a/crates/evm/core/src/backend/error.rs b/crates/evm/core/src/backend/error.rs index b22c768d4..8634ba4f9 100644 --- a/crates/evm/core/src/backend/error.rs +++ b/crates/evm/core/src/backend/error.rs @@ -33,6 +33,8 @@ pub enum DatabaseError { GetBlockHash(u64, Arc), #[error("failed to get full block for {0:?}: {1}")] GetFullBlock(BlockId, Arc), + #[error("failed to get bytecode for {0:?}: {1}")] + GetBytecode(B256, Arc), #[error("block {0:?} does not exist")] BlockNotFound(BlockId), #[error("failed to get transaction {0}: {1}")] @@ -66,6 +68,7 @@ impl DatabaseError { Self::GetBlockHash(_, err) => Some(err), Self::GetFullBlock(_, err) => Some(err), Self::GetTransaction(_, err) => Some(err), + Self::GetBytecode(_, err) => Some(err), // Enumerate explicitly to make sure errors are updated if a new one is added. Self::NoCheats(_) | Self::MissingAccount(_) | diff --git a/crates/evm/core/src/backend/fuzz.rs b/crates/evm/core/src/backend/fuzz.rs index c31205784..613d7fad0 100644 --- a/crates/evm/core/src/backend/fuzz.rs +++ b/crates/evm/core/src/backend/fuzz.rs @@ -16,9 +16,8 @@ use revm::{ use std::{borrow::Cow, collections::HashMap}; use crate::era_revm::db::RevmDatabaseForEra; -use era_test_node::fork::ForkStorage; -use multivm::vm_refunds_enhancement::{HistoryDisabled, ToTracerPointer}; -use zksync_state::StorageView; +use era_test_node::{deps::storage_view::StorageView, fork::ForkStorage}; +use multivm::vm_latest::{HistoryDisabled, ToTracerPointer}; /// A wrapper around `Backend` that ensures only `revm::DatabaseRef` functions are called. /// diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index 394910a34..a0169ba80 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -12,6 +12,7 @@ use ethers_core::{ utils::GenesisAccount, }; use foundry_common::{ + conversion_utils::h160_to_address, is_known_system_sender, types::{ToAlloy, ToEthers}, SYSTEM_TRANSACTION_TYPE, @@ -29,9 +30,8 @@ use revm::{ use std::collections::{HashMap, HashSet}; use crate::era_revm::db::RevmDatabaseForEra; -use era_test_node::fork::ForkStorage; -use multivm::vm_refunds_enhancement::{HistoryDisabled, ToTracerPointer}; -use zksync_state::StorageView; +use era_test_node::{deps::storage_view::StorageView, fork::ForkStorage}; +use multivm::vm_latest::{HistoryDisabled, ToTracerPointer}; mod diagnostic; pub use diagnostic::RevertDiagnostic; @@ -1799,6 +1799,8 @@ pub(crate) fn merge_account_data( } *active_journaled_state = target_fork.journaled_state.clone(); + + target_fork.db.accounts.remove(&h160_to_address(zksync_types::SYSTEM_CONTEXT_ADDRESS)); } /// Clones the account data from the `active_journaled_state` into the `fork_journaled_state` diff --git a/crates/evm/core/src/era_revm/db.rs b/crates/evm/core/src/era_revm/db.rs index 27d258e55..94ea5adb2 100644 --- a/crates/evm/core/src/era_revm/db.rs +++ b/crates/evm/core/src/era_revm/db.rs @@ -25,7 +25,7 @@ use zksync_basic_types::{ use zksync_types::{ api::{BlockIdVariant, Transaction, TransactionDetails}, StorageKey, ACCOUNT_CODE_STORAGE_ADDRESS, L2_ETH_TOKEN_ADDRESS, NONCE_HOLDER_ADDRESS, - SYSTEM_CONTEXT_ADDRESS, + SYSTEM_CONTEXT_ADDRESS, SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, }; use zksync_utils::{address_to_h256, h256_to_u256, u256_to_h256}; @@ -55,7 +55,7 @@ impl RevmDatabaseForEra where ::Error: Debug, { - /// Create a new instance of [RevmDatabaseForEra] caching the current l2 block. + /// Create a new instance of [RevmDatabaseForEra]. pub fn new(db: Arc>>) -> Self { let db_inner = db.clone(); let current_block = { @@ -86,7 +86,10 @@ where /// Returns the current L2 block number and timestamp from the database. /// Reads it directly from the SYSTEM_CONTEXT storage. pub fn get_l2_block_number_and_timestamp(&self) -> (u64, u64) { - let num_and_ts = self.read_storage_internal(SYSTEM_CONTEXT_ADDRESS, U256::from(9)); + let num_and_ts = self.read_storage_internal( + SYSTEM_CONTEXT_ADDRESS, + h256_to_u256(SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION), + ); let num_and_ts_bytes = num_and_ts.as_fixed_bytes(); let num: [u8; 8] = num_and_ts_bytes[24..32].try_into().unwrap(); let ts: [u8; 8] = num_and_ts_bytes[8..16].try_into().unwrap(); @@ -194,6 +197,7 @@ where _ => eyre::bail!("Only fetching most recent block is implemented"), } } + let mut result = self.read_storage_internal(address, idx); if L2_ETH_TOKEN_ADDRESS == address && result.is_zero() { diff --git a/crates/evm/core/src/era_revm/transactions.rs b/crates/evm/core/src/era_revm/transactions.rs index 0509634e0..af34adc31 100644 --- a/crates/evm/core/src/era_revm/transactions.rs +++ b/crates/evm/core/src/era_revm/transactions.rs @@ -1,4 +1,5 @@ use era_test_node::{ + deps::storage_view::StorageView, fork::{ForkDetails, ForkStorage}, node::{ InMemoryNode, InMemoryNodeConfig, ShowCalls, ShowGasDetails, ShowStorageLogs, ShowVMDetails, @@ -9,7 +10,7 @@ use ethers_core::abi::ethabi::{self, ParamType}; use itertools::Itertools; use multivm::{ interface::VmExecutionResultAndLogs, - vm_refunds_enhancement::{HistoryDisabled, ToTracerPointer}, + vm_latest::{HistoryDisabled, ToTracerPointer}, }; use revm::primitives::{ Account, AccountInfo, Address, Bytes, EVMResult, Env, Eval, Halt, HashMap as rHashMap, @@ -22,17 +23,14 @@ use std::{ sync::{Arc, Mutex}, }; use zksync_basic_types::{web3::signing::keccak256, L1BatchNumber, L2ChainId, H160, H256, U256}; -use zksync_state::StorageView; use zksync_types::{ api::Block, fee::Fee, l2::L2Tx, transaction_request::PaymasterParams, PackedEthSignature, - StorageKey, StorageLogQueryType, ACCOUNT_CODE_STORAGE_ADDRESS, + StorageKey, StorageLogQueryType, StorageValue, ACCOUNT_CODE_STORAGE_ADDRESS, }; use zksync_utils::{h256_to_account_address, u256_to_h256}; use foundry_common::zk_utils::{ - conversion_utils::{ - address_to_h160, h160_to_address, h256_to_h160, h256_to_revm_u256, revm_u256_to_u256, - }, + conversion_utils::{h160_to_address, h256_to_h160, h256_to_revm_u256, revm_u256_to_u256}, factory_deps::PackedEraBytecode, }; @@ -137,13 +135,18 @@ where INSP: ToTracerPointer>>, HistoryDisabled>, { let era_db = RevmDatabaseForEra::new(Arc::new(Mutex::new(Box::new(db)))); - let nonce = era_db.get_nonce_for_address(address_to_h160(env.tx.caller)); let (num, ts) = era_db.get_l2_block_number_and_timestamp(); + let l1_num = num; + let nonce = era_db.get_nonce_for_address(H160::from_slice(env.tx.caller.as_slice())); - debug!("Starting ERA transaction: block={:?} timestamp={:?} nonce={:?}", num, ts, nonce); + info!( + "Starting ERA transaction: block={:?} timestamp={:?} nonce={:?} | l1_block={}", + num, ts, nonce, l1_num + ); // Update the environment timestamp and block number. // Check if this should be done at the end? + // In general, we do not rely on env as it's consistently maintained in foundry env.block.number = env.block.number.saturating_add(rU256::from(1)); env.block.timestamp = env.block.timestamp.saturating_add(rU256::from(1)); @@ -154,7 +157,6 @@ where 31337 }; - let l1_num = num / 2; let fork_details = ForkDetails { fork_source: era_db.clone(), l1_block: L1BatchNumber(l1_num as u32), @@ -186,7 +188,12 @@ where } let tracer = inspector.into_tracer_pointer(); let era_execution_result = node - .run_l2_tx_raw(l2_tx, multivm::interface::TxExecutionMode::VerifyExecute, vec![tracer]) + .run_l2_tx_raw( + l2_tx, + multivm::interface::TxExecutionMode::VerifyExecute, + vec![tracer], + false, + ) .unwrap(); let (modified_keys, tx_result, _call_traces, _block, bytecodes, _block_ctx) = @@ -245,6 +252,30 @@ where } }; + Ok(ResultAndState { + result: execution_result, + state: storage_to_state(&era_db, &modified_keys, bytecodes), + }) +} + +fn decode_l2_tx_result(output: Vec) -> Vec { + ethabi::decode(&[ParamType::Bytes], &output) + .ok() + .and_then(|result| result.first().cloned()) + .and_then(|result| result.into_bytes()) + .unwrap_or_default() +} + +/// Converts the zksync era's modified keys to the revm state. +pub fn storage_to_state( + era_db: &RevmDatabaseForEra, + modified_keys: &HashMap, + bytecodes: HashMap>, +) -> rHashMap +where + DB: DatabaseExt + Send, + ::Error: Debug, +{ let account_to_keys: HashMap> = modified_keys.iter().fold(HashMap::new(), |mut acc, (storage_key, value)| { acc.entry(*storage_key.address()).or_default().insert(*storage_key, *value); @@ -293,7 +324,7 @@ where .collect() }); - let account_code = era_db.fetch_account_code(*account, &modified_keys, &bytecodes); + let account_code = era_db.fetch_account_code(*account, modified_keys, &bytecodes); let (code_hash, code) = account_code .map(|(hash, bytecode)| (B256::from(&hash.0), Some(bytecode))) @@ -317,24 +348,15 @@ where ) }) .collect(); - - Ok(ResultAndState { result: execution_result, state }) -} - -fn decode_l2_tx_result(output: Vec) -> Vec { - ethabi::decode(&[ParamType::Bytes], &output) - .ok() - .and_then(|result| result.first().cloned()) - .and_then(|result| result.into_bytes()) - .unwrap_or_default() + state } #[cfg(test)] mod tests { use core::marker::PhantomData; use multivm::{ - interface::dyn_tracers::vm_1_3_3::DynTracer, - vm_refunds_enhancement::{HistoryMode, SimpleMemory, VmTracer}, + interface::dyn_tracers::vm_1_4_0::DynTracer, + vm_latest::{HistoryMode, SimpleMemory, VmTracer}, }; use zksync_state::WriteStorage; diff --git a/crates/evm/core/src/fork/backend.rs b/crates/evm/core/src/fork/backend.rs index ed708bc01..cdf1c5aa2 100644 --- a/crates/evm/core/src/fork/backend.rs +++ b/crates/evm/core/src/fork/backend.rs @@ -8,11 +8,11 @@ use ethers_core::{ abi::ethereum_types::BigEndianHash, types::{Block, BlockId, NameOrAddress, Transaction}, }; -use ethers_providers::Middleware; use foundry_common::{ types::{ToAlloy, ToEthers}, NON_ARCHIVE_NODE_WARNING, }; + use futures::{ channel::mpsc::{channel, Receiver, Sender}, stream::Stream, @@ -32,6 +32,8 @@ use std::{ }, }; +use super::zksync_provider::ZkSyncMiddleware; + // Various future/request type aliases type AccountFuture = @@ -48,10 +50,14 @@ type TransactionFuture = Pin< Box, Err>, B256)> + Send>, >; +type BytecodeHashFuture = + Pin, Err>, B256)> + Send>>; + type AccountInfoSender = OneshotSender>; type StorageSender = OneshotSender>; type BlockHashSender = OneshotSender>; type FullBlockSender = OneshotSender>>; +type ByteCodeHashSender = OneshotSender>; type TransactionSender = OneshotSender>; /// Request variants that are executed by the provider @@ -61,6 +67,7 @@ enum ProviderRequest { BlockHash(BlockHashFuture), FullBlock(FullBlockFuture), Transaction(TransactionFuture), + ByteCodeHash(BytecodeHashFuture), } /// The Request type the Backend listens for @@ -78,6 +85,8 @@ enum BackendRequest { Transaction(B256, TransactionSender), /// Sets the pinned block to fetch data from SetPinnedBlock(BlockId), + /// Get the bytecode for the given hash + ByteCodeHash(B256, ByteCodeHashSender), } /// Handles an internal provider and listens for requests. @@ -85,7 +94,7 @@ enum BackendRequest { /// This handler will remain active as long as it is reachable (request channel still open) and /// requests are in progress. #[must_use = "futures do nothing unless polled"] -pub struct BackendHandler { +pub struct BackendHandler { provider: M, /// Stores all the data. db: BlockchainDb, @@ -108,7 +117,7 @@ pub struct BackendHandler { impl BackendHandler where - M: Middleware + Clone + 'static, + M: ZkSyncMiddleware + Clone + 'static, { fn new( provider: M, @@ -174,6 +183,9 @@ where BackendRequest::SetPinnedBlock(block_id) => { self.block_id = Some(block_id); } + BackendRequest::ByteCodeHash(code_hash, sender) => { + self.request_bytecode_by_hash(code_hash, sender); + } } } @@ -181,6 +193,7 @@ where fn request_account_storage(&mut self, address: Address, idx: U256, listener: StorageSender) { match self.storage_requests.entry((address, idx)) { Entry::Occupied(mut entry) => { + println!("occupied {:?}, {:?}, {:?}", &address, &idx, &self.block_id); entry.get_mut().push(listener); } Entry::Vacant(entry) => { @@ -220,6 +233,7 @@ where let resp = tokio::try_join!(balance, nonce, code).map(|(balance, nonce, code)| { (balance.to_alloy(), nonce.to_alloy(), Bytes::from(code.0)) }); + (resp, address) }); ProviderRequest::Account(fut) @@ -238,6 +252,15 @@ where } } + fn request_bytecode_by_hash(&mut self, code_hash: B256, sender: ByteCodeHashSender) { + let provider = self.provider.clone(); + let fut = Box::pin(async move { + let bytecode = provider.get_bytecode_by_hash(code_hash).await; + (sender, bytecode, code_hash) + }); + self.pending_requests.push(ProviderRequest::ByteCodeHash(fut)); + } + /// process a request for an entire block fn request_full_block(&mut self, number: BlockId, sender: FullBlockSender) { let provider = self.provider.clone(); @@ -298,7 +321,7 @@ where impl Future for BackendHandler where - M: Middleware + Clone + Unpin + 'static, + M: ZkSyncMiddleware + Clone + Unpin + 'static, { type Output = (); @@ -465,6 +488,20 @@ where continue } } + ProviderRequest::ByteCodeHash(fut) => { + if let Poll::Ready((sender, bytecode, code_hash)) = fut.poll_unpin(cx) { + let msg = match bytecode { + Ok(Some(bytecode)) => Ok(bytecode), + Ok(None) => Err(DatabaseError::MissingCode(code_hash)), + Err(err) => { + let err = Arc::new(eyre::Error::new(err)); + Err(DatabaseError::GetBytecode(code_hash, err)) + } + }; + let _ = sender.send(msg); + continue + } + } } // not ready, insert and poll again pin.pending_requests.push(request); @@ -528,7 +565,7 @@ impl SharedBackend { /// NOTE: this should be called with `Arc` pub async fn spawn_backend(provider: M, db: BlockchainDb, pin_block: Option) -> Self where - M: Middleware + Unpin + 'static + Clone, + M: ZkSyncMiddleware + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); // spawn the provider handler to a task @@ -545,7 +582,7 @@ impl SharedBackend { pin_block: Option, ) -> Self where - M: Middleware + Unpin + 'static + Clone, + M: ZkSyncMiddleware + Unpin + 'static + Clone, { let (shared, handler) = Self::new(provider, db, pin_block); @@ -574,7 +611,7 @@ impl SharedBackend { pin_block: Option, ) -> (Self, BackendHandler) where - M: Middleware + Unpin + 'static + Clone, + M: ZkSyncMiddleware + Unpin + 'static + Clone, { let (backend, backend_rx) = channel(1); let cache = Arc::new(FlushJsonBlockCacheDB(Arc::clone(db.cache()))); @@ -635,6 +672,15 @@ impl SharedBackend { }) } + fn do_get_bytecode(&self, hash: B256) -> DatabaseResult { + tokio::task::block_in_place(|| { + let (sender, rx) = oneshot_channel(); + let req = BackendRequest::ByteCodeHash(hash, sender); + self.backend.clone().try_send(req)?; + rx.recv()? + }) + } + /// Flushes the DB to disk if caching is enabled pub(crate) fn flush_cache(&self) { self.cache.0.flush(); @@ -656,7 +702,14 @@ impl DatabaseRef for SharedBackend { } fn code_by_hash_ref(&self, hash: B256) -> Result { - Err(DatabaseError::MissingCode(hash)) + trace!(target: "sharedbackend", %hash, "request codehash"); + self.do_get_bytecode(hash).map_err(|err| { + error!(target: "sharedbackend", %err, %hash, "Failed to send/recv `code_by_hash`"); + if err.is_possibly_non_archive_node_error() { + error!(target: "sharedbackend", "{NON_ARCHIVE_NODE_WARNING}"); + } + err + }) } fn storage_ref(&self, address: Address, index: U256) -> Result { @@ -701,6 +754,7 @@ mod tests { fork::{BlockchainDbMeta, CreateFork, JsonBlockCacheDB}, opts::EvmOpts, }; + use ethers_providers::Middleware; use foundry_common::get_http_provider; use foundry_config::{Config, NamedChain}; use std::{collections::BTreeSet, path::PathBuf, sync::Arc}; diff --git a/crates/evm/core/src/fork/mod.rs b/crates/evm/core/src/fork/mod.rs index f9042ac91..250c3aac8 100644 --- a/crates/evm/core/src/fork/mod.rs +++ b/crates/evm/core/src/fork/mod.rs @@ -11,6 +11,7 @@ mod cache; pub use cache::{BlockchainDb, BlockchainDbMeta, JsonBlockCacheDB, MemDb}; pub mod database; +pub mod zksync_provider; mod multi; pub use multi::{ForkId, MultiFork, MultiForkHandler}; diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index f00d5ffed..b96392715 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -4,8 +4,9 @@ //! concurrently active pairs at once. use crate::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}; +use alloy_primitives::Bytes; use ethers_core::types::{BlockId, BlockNumber}; -use ethers_providers::Provider; +use ethers_providers::{JsonRpcClient, Provider}; use foundry_common::{runtime_client::RuntimeClient, types::ToEthers, ProviderBuilder}; use foundry_config::Config; use futures::{ @@ -26,6 +27,8 @@ use std::{ time::Duration, }; +use super::zksync_provider::ZkSyncMiddleware; + /// The identifier for a specific fork, this could be the name of the network a custom descriptive /// name. #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -479,3 +482,24 @@ async fn create_fork(mut fork: CreateFork) -> eyre::Result<(CreatedFork, Handler let fork = CreatedFork::new(fork, backend); Ok((fork, handler)) } + +#[async_trait::async_trait] +impl ZkSyncMiddleware for Provider

{ + async fn get_bytecode_by_hash( + &self, + hash: alloy_primitives::B256, + ) -> Result, Self::Error> { + let bytecode: Option = self.request("zks_getBytecodeByHash", vec![hash]).await?; + Ok(bytecode.map(revm::primitives::Bytecode::new_raw)) + } +} + +#[async_trait::async_trait] +impl ZkSyncMiddleware for Arc> { + async fn get_bytecode_by_hash( + &self, + hash: alloy_primitives::B256, + ) -> Result, Self::Error> { + self.as_ref().get_bytecode_by_hash(hash).await + } +} diff --git a/crates/evm/core/src/fork/zksync_provider.rs b/crates/evm/core/src/fork/zksync_provider.rs new file mode 100644 index 000000000..6d142ac2c --- /dev/null +++ b/crates/evm/core/src/fork/zksync_provider.rs @@ -0,0 +1,8 @@ +use alloy_primitives::B256; +use ethers_providers::Middleware; +use revm::primitives::Bytecode; + +#[async_trait::async_trait] +pub trait ZkSyncMiddleware: Middleware { + async fn get_bytecode_by_hash(&self, hash: B256) -> Result, Self::Error>; +} diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index ac1a99108..ac8efbdb2 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -48,5 +48,4 @@ tracing = "0.1" # zksync multivm.workspace = true era_cheatcodes.workspace = true -zksync_state.workspace = true era_test_node.workspace = true diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index be1d3a885..8f4c2b987 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -574,8 +574,8 @@ use era_test_node::fork::ForkStorage; use foundry_evm_core::era_revm::db::RevmDatabaseForEra; use era_cheatcodes::cheatcodes::CheatcodeTracer; -use multivm::vm_refunds_enhancement::{HistoryDisabled, ToTracerPointer}; -use zksync_state::StorageView; +use era_test_node::deps::storage_view::StorageView; +use multivm::vm_latest::{HistoryDisabled, ToTracerPointer}; impl ToTracerPointer>>, HistoryDisabled> @@ -583,10 +583,11 @@ impl { fn into_tracer_pointer( self, - ) -> multivm::vm_refunds_enhancement::TracerPointer< + ) -> multivm::vm_latest::TracerPointer< StorageView>>, HistoryDisabled, > { - CheatcodeTracer::new().into_tracer_pointer() + CheatcodeTracer::new(self.cheatcodes.as_ref().map(|c| c.config.clone()).unwrap_or_default()) + .into_tracer_pointer() } } diff --git a/crates/zkforge/src/runner.rs b/crates/zkforge/src/runner.rs index 400f655c6..a30a9b8a4 100644 --- a/crates/zkforge/src/runner.rs +++ b/crates/zkforge/src/runner.rs @@ -354,6 +354,7 @@ impl<'a> ContractRunner<'a> { /// similar to `eth_call`. #[instrument(name = "test", skip_all, fields(name = %func.signature(), %should_fail))] pub fn run_test(&self, func: &Function, should_fail: bool, setup: TestSetup) -> TestResult { + trace!("executing test {:?}", func.name); let TestSetup { address, mut logs, mut traces, mut labeled_addresses, mut coverage, .. } = setup; diff --git a/smoke-test/src/Counter.sol b/smoke-test/src/Counter.sol index d29bbe2ed..a36fe0794 100644 --- a/smoke-test/src/Counter.sol +++ b/smoke-test/src/Counter.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import {Test, console} from "forge-std/Test.sol"; +import {Test, console2 as console} from "forge-std/Test.sol"; contract Counter { uint256 public number = 0; @@ -12,7 +12,7 @@ contract Counter { } function incrementBy(uint8 amount) public { - console.log("incrementBy"); + console.log("incrementBy", amount); number += uint256(amount); } } From f63c56a06b3bcf1f15414f540d6bf23fb36183d4 Mon Sep 17 00:00:00 2001 From: Agustin Aon <21188659+aon@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:31:12 -0300 Subject: [PATCH 2/2] feat: add snapshot cheatcodes (#215) --- crates/era-cheatcodes/src/cheatcodes.rs | 112 +++++++++++++++++- .../tests/src/cheatcodes/Snapshot.t.sol | 97 +++++++++++++++ 2 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 crates/era-cheatcodes/tests/src/cheatcodes/Snapshot.t.sol diff --git a/crates/era-cheatcodes/src/cheatcodes.rs b/crates/era-cheatcodes/src/cheatcodes.rs index 68bca7e01..775407349 100644 --- a/crates/era-cheatcodes/src/cheatcodes.rs +++ b/crates/era-cheatcodes/src/cheatcodes.rs @@ -29,7 +29,7 @@ use multivm::{ }, }; use revm::{ - primitives::{BlockEnv, CfgEnv, Env, SpecId, U256 as rU256}, + primitives::{ruint::Uint, BlockEnv, CfgEnv, Env, SpecId, U256 as rU256}, JournaledState, }; use serde::Serialize; @@ -119,6 +119,12 @@ pub struct CheatcodeTracer { recording_timestamp: u32, expected_calls: ExpectedCallsTracker, test_status: FoundryTestState, + saved_snapshots: HashMap, +} + +#[derive(Debug, Clone)] +pub struct SavedSnapshot { + modified_storage: HashMap, } #[derive(Debug, Clone, Serialize, Eq, Hash, PartialEq)] @@ -141,6 +147,8 @@ enum FinishCycleOneTimeActions { CreateSelectFork { url_or_alias: String, block_number: Option }, CreateFork { url_or_alias: String, block_number: Option }, SelectFork { fork_id: U256 }, + RevertToSnapshot { snapshot_id: U256 }, + Snapshot, } #[derive(Debug, Default, Clone)] @@ -447,6 +455,84 @@ impl VmTracer, H> for CheatcodeT self.return_data = Some(vec![fork_id]); } + FinishCycleOneTimeActions::RevertToSnapshot { snapshot_id } => { + let mut storage = storage.borrow_mut(); + + let modified_storage: HashMap = storage + .modified_storage_keys() + .clone() + .into_iter() + .filter(|(key, _)| key.address() != &zksync_types::SYSTEM_CONTEXT_ADDRESS) + .collect(); + + storage.clean_cache(); + + { + let handle: &ForkStorage> = &storage.storage_handle; + let mut fork_storage = handle.inner.write().unwrap(); + fork_storage.value_read_cache.clear(); + let era_db = fork_storage.fork.as_ref().unwrap().fork_source.clone(); + let bytecodes = bootloader_state + .get_last_tx_compressed_bytecodes() + .iter() + .map(|b| bytecode_to_factory_dep(b.original.clone())) + .collect(); + + let mut journaled_state = JournaledState::new(SpecId::LATEST, vec![]); + let state = storage_to_state(&era_db, &modified_storage, bytecodes); + *journaled_state.state() = state; + + let mut db = era_db.db.lock().unwrap(); + let era_env = self.env.get().unwrap(); + let mut env = into_revm_env(era_env); + db.revert(Uint::from_limbs(snapshot_id.0), &journaled_state, &mut env); + } + + storage.modified_storage_keys = + self.saved_snapshots.remove(&snapshot_id).unwrap().modified_storage; + } + FinishCycleOneTimeActions::Snapshot => { + let mut storage = storage.borrow_mut(); + + let modified_storage: HashMap = storage + .modified_storage_keys() + .clone() + .into_iter() + .filter(|(key, _)| key.address() != &zksync_types::SYSTEM_CONTEXT_ADDRESS) + .collect(); + + storage.clean_cache(); + + let snapshot_id = { + let handle: &ForkStorage> = &storage.storage_handle; + let mut fork_storage = handle.inner.write().unwrap(); + fork_storage.value_read_cache.clear(); + let era_db = fork_storage.fork.as_ref().unwrap().fork_source.clone(); + let bytecodes = bootloader_state + .get_last_tx_compressed_bytecodes() + .iter() + .map(|b| bytecode_to_factory_dep(b.original.clone())) + .collect(); + + let mut journaled_state = JournaledState::new(SpecId::LATEST, vec![]); + let state = storage_to_state(&era_db, &modified_storage, bytecodes); + *journaled_state.state() = state; + + let mut db = era_db.db.lock().unwrap(); + let era_env = self.env.get().unwrap(); + let env = into_revm_env(era_env); + let snapshot_id = db.snapshot(&journaled_state, &env); + + self.saved_snapshots.insert( + snapshot_id.to_u256(), + SavedSnapshot { modified_storage: modified_storage.clone() }, + ); + snapshot_id + }; + + storage.modified_storage_keys = modified_storage; + self.return_data = Some(vec![snapshot_id.to_u256()]); + } } } @@ -598,8 +684,11 @@ impl CheatcodeTracer { tracing::error!("Failed to run ffi: no args"); return }; - // TODO: set directory to root - let Ok(output) = Command::new(first_arg).args(&command_input[1..]).output() else { + let Ok(output) = Command::new(first_arg) + .args(&command_input[1..]) + .current_dir(&self.config.root) + .output() + else { tracing::error!("Failed to run ffi"); return }; @@ -722,6 +811,12 @@ impl CheatcodeTracer { }; self.add_trimmed_return_data(&data); } + revertTo(revertToCall { snapshotId }) => { + tracing::info!("👷 Reverting to snapshot {}", snapshotId); + self.one_time_actions.push(FinishCycleOneTimeActions::RevertToSnapshot { + snapshot_id: snapshotId.to_u256(), + }); + } roll(rollCall { newHeight: new_height }) => { tracing::info!("👷 Setting block number to {}", new_height); let key = StorageKey::new( @@ -831,6 +926,10 @@ impl CheatcodeTracer { ); self.write_storage(nonce_key, u256_to_h256(enforced_full_nonce), &mut storage); } + snapshot(snapshotCall {}) => { + tracing::info!("👷 Creating snapshot"); + self.one_time_actions.push(FinishCycleOneTimeActions::Snapshot); + } startPrank_0(startPrank_0Call { msgSender: msg_sender }) => { tracing::info!("👷 Starting prank to {msg_sender:?}"); self.permanent_actions.start_prank = @@ -913,8 +1012,11 @@ impl CheatcodeTracer { tracing::error!("Failed to run ffi: no args"); return }; - // TODO: set directory to root - let Ok(output) = Command::new(first_arg).args(&command_input[1..]).output() else { + let Ok(output) = Command::new(first_arg) + .args(&command_input[1..]) + .current_dir(&self.config.root) + .output() + else { tracing::error!("Failed to run ffi"); return }; diff --git a/crates/era-cheatcodes/tests/src/cheatcodes/Snapshot.t.sol b/crates/era-cheatcodes/tests/src/cheatcodes/Snapshot.t.sol new file mode 100644 index 000000000..59948965d --- /dev/null +++ b/crates/era-cheatcodes/tests/src/cheatcodes/Snapshot.t.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, Vm, console2 as console} from "../../lib/forge-std/src/Test.sol"; +import {Constants} from "./Constants.sol"; +import {Utils} from "./Utils.sol"; + +struct Storage { + uint256 slot0; + uint256 slot1; +} + +contract SnapshotTest is Test { + Storage store; + + function setUp() public { + store.slot0 = 10; + store.slot1 = 20; + } + + function testSnapshot() public { + console.log("calling snapshot"); + + store.slot0 = 10; + store.slot1 = 20; + + (bool success, bytes memory data) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature("snapshot()") + ); + require(success, "snapshot failed"); + + uint256 snapshot = abi.decode(data, (uint256)); + + console.log("snapshot ", snapshot); + + console.log("store values: ", store.slot0, store.slot1); + + store.slot0 = 300; + store.slot1 = 400; + + assertEq(store.slot0, 300); + assertEq(store.slot1, 400); + + console.log("store values: ", store.slot0, store.slot1); + console.log("calling revertTo"); + + (success, ) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature("revertTo(uint256)", snapshot) + ); + require(success, "revertTo failed"); + + console.log("store values: ", store.slot0, store.slot1); + + assertEq(store.slot0, 10, "snapshot revert for slot 0 unsuccessful"); + assertEq(store.slot1, 20, "snapshot revert for slot 1 unsuccessful"); + } + + function testBlockValues() public { + uint256 num = block.number; + uint256 time = block.timestamp; + + (bool success, bytes memory data) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature("snapshot()") + ); + require(success, "snapshot failed"); + + uint256 snapshot = abi.decode(data, (uint256)); + + (success, ) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature("warp(uint256)", 1337) + ); + require(success, "warp failed"); + assertEq(block.timestamp, 1337); + + (success, ) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature("roll(uint256)", 99) + ); + require(success, "roll failed"); + assertEq(block.number, 99); + + (success, ) = Constants.CHEATCODE_ADDRESS.call( + abi.encodeWithSignature("revertTo(uint256)", snapshot) + ); + require(success, "revertTo failed"); + + assertEq( + block.number, + num, + "snapshot revert for block.number unsuccessful" + ); + assertEq( + block.timestamp, + time, + "snapshot revert for block.timestamp unsuccessful" + ); + } +}