diff --git a/Cargo.lock b/Cargo.lock index 76f38653bbef2..283894ee8f705 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,12 +269,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - [[package]] name = "arrayvec" version = "0.7.2" @@ -447,12 +441,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base16ct" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" - [[package]] name = "base16ct" version = "0.2.0" @@ -531,6 +519,7 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium 0.7.0", + "serde", "tap", "wyz", ] @@ -972,7 +961,7 @@ dependencies = [ "digest 0.10.6", "getrandom 0.2.9", "hmac 0.12.1", - "k256 0.13.1", + "k256", "lazy_static", "serde", "sha2 0.10.6", @@ -1331,18 +1320,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.5.1" @@ -1489,15 +1466,6 @@ dependencies = [ "parking_lot_core", ] -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", -] - [[package]] name = "der" version = "0.7.3" @@ -1643,29 +1611,17 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" -[[package]] -name = "ecdsa" -version = "0.14.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" -dependencies = [ - "der 0.6.1", - "elliptic-curve 0.12.3", - "rfc6979 0.3.1", - "signature 1.6.4", -] - [[package]] name = "ecdsa" version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" dependencies = [ - "der 0.7.3", + "der", "digest 0.10.6", - "elliptic-curve 0.13.4", - "rfc6979 0.4.0", - "signature 2.1.0", + "elliptic-curve", + "rfc6979", + "signature", ] [[package]] @@ -1686,40 +1642,21 @@ dependencies = [ "serde_json", ] -[[package]] -name = "elliptic-curve" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.4.9", - "der 0.6.1", - "digest 0.10.6", - "ff 0.12.1", - "generic-array", - "group 0.12.1", - "rand_core 0.6.4", - "sec1 0.3.0", - "subtle", - "zeroize", -] - [[package]] name = "elliptic-curve" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.1", + "base16ct", + "crypto-bigint", "digest 0.10.6", - "ff 0.13.0", + "ff", "generic-array", - "group 0.13.0", + "group", "pkcs8", "rand_core 0.6.4", - "sec1 0.7.2", + "sec1", "subtle", "zeroize", ] @@ -1763,7 +1700,7 @@ dependencies = [ "base64 0.13.1", "bytes", "hex", - "k256 0.13.1", + "k256", "log", "rand 0.8.5", "rlp", @@ -1772,6 +1709,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enumn" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -1901,9 +1849,9 @@ dependencies = [ [[package]] name = "ethers" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356eaf2e3947efa975c3f953e33f73193e5e21b9d8bab26c3ca532676931696f" +checksum = "697aba1bec98cb86e7bebd69f9bb365218871464137af9e93e7a72bd6dc421d0" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -1929,9 +1877,9 @@ dependencies = [ [[package]] name = "ethers-contract" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a328cb42014ac0ac577a8dac32eb658ee0f32b5a9a5317a0329ac1d4201f1c6" +checksum = "b4e8ed7c2b2a22e07b65ae0eb426c948a7448f1be15c66e4813e02c423751fc9" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1948,9 +1896,9 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96813e4b58b6c6b817367380db900dddbd67bfe27610ec89fd3263778d5a4aa" +checksum = "bf0984f4ec4e267fd27b7c9fa2f73e72c5c98491a73f777290654154d104f723" dependencies = [ "Inflector", "dunce", @@ -1966,7 +1914,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.15", + "syn 1.0.109", "tokio", "toml 0.7.3", "url", @@ -1975,18 +1923,16 @@ dependencies = [ [[package]] name = "ethers-contract-derive" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373068bb24b4dea8fe0d1758aadab2dd4ec9de1d2c28316439cadcda3ed48eae" +checksum = "914e9211077a1b590af1ee6b8dfbd54515c808119546c95da69479908dc3d4de" dependencies = [ - "Inflector", "ethers-contract-abigen", "ethers-core", "hex", "proc-macro2", "quote", - "serde_json", - "syn 2.0.15", + "syn 1.0.109", ] [[package]] @@ -1999,13 +1945,13 @@ dependencies = [ "bytes", "cargo_metadata", "chrono", - "elliptic-curve 0.13.4", + "elliptic-curve", "ethabi", "generic-array", "getrandom 0.2.9", "hex", - "k256 0.13.1", - "num_enum 0.6.1", + "k256", + "num_enum", "once_cell", "open-fastrlp", "rand 0.8.5", @@ -2022,9 +1968,9 @@ dependencies = [ [[package]] name = "ethers-etherscan" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cfc3d36be9e16bac241e1d59ec9ace00e8e4241c09f604a0f65158eb37d4878" +checksum = "8920b59cf81e357df2c8102d6a9dc81c2d68f7409543ff3b6868851ecf007807" dependencies = [ "ethers-core", "ethers-solc", @@ -2039,9 +1985,9 @@ dependencies = [ [[package]] name = "ethers-middleware" -version = "2.0.3" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6f3543b4f6679b2558901c4b323cecb06b19239439d2588fa8b489bac9675d" +checksum = "c54b30f67c1883ed68bd38aedbdd321831382c12e1b95089c8261c79bb85e4da" dependencies = [ "async-trait", "auto_impl", @@ -2113,7 +2059,7 @@ dependencies = [ "coins-bip32", "coins-bip39", "coins-ledger", - "elliptic-curve 0.13.4", + "elliptic-curve", "eth-keystore", "ethers-core", "futures-executor", @@ -2248,16 +2194,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ff" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "ff" version = "0.13.0" @@ -3098,33 +3034,22 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "group" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" -dependencies = [ - "ff 0.12.1", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff 0.13.0", + "ff", "rand_core 0.6.4", "subtle", ] [[package]] name = "h2" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b91535aa35fea1523ad1b86cb6b53c28e0ae566ba4a460f4457e936cad7c6f" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -3266,6 +3191,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hidapi-rusb" version = "1.3.2" @@ -3680,19 +3611,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "k256" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" -dependencies = [ - "cfg-if", - "ecdsa 0.14.8", - "elliptic-curve 0.12.3", - "sha2 0.10.6", - "sha3", -] - [[package]] name = "k256" version = "0.13.1" @@ -3700,11 +3618,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.6", - "elliptic-curve 0.13.4", + "ecdsa", + "elliptic-curve", "once_cell", "sha2 0.10.6", - "signature 2.1.0", + "signature", ] [[package]] @@ -4263,34 +4181,13 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - [[package]] name = "num_enum" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ - "num_enum_derive 0.6.1", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", + "num_enum_derive", ] [[package]] @@ -4816,7 +4713,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.3", + "der", "spki", ] @@ -4886,12 +4783,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.4" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ "proc-macro2", - "syn 2.0.15", + "syn 1.0.109", ] [[package]] @@ -5272,35 +5169,40 @@ dependencies = [ [[package]] name = "revm" -version = "2.3.1" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73d84c8f9836efb0f5f5f8de4700a953c4e1f3119e5cfcb0aad8e5be73daf991" +checksum = "c0dde669837bf6b0ca1b9c6ea02e89f951496a321bfebbba14322e3f0ae80985" dependencies = [ - "arrayref", "auto_impl", - "bytes", - "hashbrown 0.13.2", - "hex", - "num_enum 0.5.11", - "primitive-types", - "revm_precompiles", - "rlp", + "revm-interpreter", + "revm-precompile", + "serde", + "serde_json", +] + +[[package]] +name = "revm-interpreter" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f408c834f251dc33ca424b027c132f2cfe541335c5d9895e04d2987f9cfd071" +dependencies = [ + "derive_more", + "enumn", + "revm-primitives", "serde", "sha3", ] [[package]] -name = "revm_precompiles" -version = "1.1.2" +name = "revm-precompile" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0353d456ef3e989dc9190f42c6020f09bc2025930c37895826029304413204b5" +checksum = "10a3eabf08ea9e4063f5531b8735e29344d9d6eaebaa314c58253f6c17fcdf2d" dependencies = [ - "bytes", - "hashbrown 0.13.2", - "k256 0.11.6", + "k256", "num", "once_cell", - "primitive-types", + "revm-primitives", "ripemd", "secp256k1", "sha2 0.10.6", @@ -5309,14 +5211,25 @@ dependencies = [ ] [[package]] -name = "rfc6979" -version = "0.3.1" +name = "revm-primitives" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +checksum = "180427e1169b860ab63eaa5bcff158010073646abf3312aed11a1d7aa1aa8291" dependencies = [ - "crypto-bigint 0.4.9", - "hmac 0.12.1", - "zeroize", + "auto_impl", + "bitvec 1.0.1", + "bytes", + "derive_more", + "enumn", + "fixed-hash", + "hashbrown 0.13.2", + "hex", + "hex-literal", + "primitive-types", + "rlp", + "ruint", + "serde", + "sha3", ] [[package]] @@ -5396,6 +5309,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "ruint" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad3a104dc8c3867f653b0fec89c65e00b0ceb752718ad282177a7e0f33257ac" +dependencies = [ + "derive_more", + "primitive-types", + "rlp", + "ruint-macro", + "rustc_version", + "serde", + "thiserror", +] + +[[package]] +name = "ruint-macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc5760263ea229d367e7dff3c0cbf09e4797a125bd87059a6c095804f3b2d1" + [[package]] name = "rusb" version = "0.9.2" @@ -5491,9 +5425,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hex" @@ -5695,27 +5629,14 @@ dependencies = [ "untrusted", ] -[[package]] -name = "sec1" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" -dependencies = [ - "base16ct 0.1.1", - "der 0.6.1", - "generic-array", - "subtle", - "zeroize", -] - [[package]] name = "sec1" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" dependencies = [ - "base16ct 0.2.0", - "der 0.7.3", + "base16ct", + "der", "generic-array", "pkcs8", "subtle", @@ -5724,18 +5645,18 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.24.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" dependencies = [ "cc", ] @@ -5985,16 +5906,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" -dependencies = [ - "digest 0.10.6", - "rand_core 0.6.4", -] - [[package]] name = "signature" version = "2.1.0" @@ -6069,7 +5980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" dependencies = [ "base64ct", - "der 0.7.3", + "der", ] [[package]] diff --git a/anvil/core/Cargo.toml b/anvil/core/Cargo.toml index 06184933b91ee..951e3fb692fc7 100644 --- a/anvil/core/Cargo.toml +++ b/anvil/core/Cargo.toml @@ -7,10 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] # foundry internal foundry-evm = { path = "../../evm" } -revm = { version = "2.3", default-features = false, features = [ +revm = { version = "3.1.1", default-features = false, features = [ "std", - "k256", - "with-serde", + "serde", "memory_limit", ] } diff --git a/anvil/core/src/eth/proof.rs b/anvil/core/src/eth/proof.rs index a545ce6f3b348..a1a2f3b5a3c24 100644 --- a/anvil/core/src/eth/proof.rs +++ b/anvil/core/src/eth/proof.rs @@ -5,7 +5,7 @@ use ethers_core::{ types::{H256, U256}, utils::rlp, }; -use revm::KECCAK_EMPTY; +use revm::primitives::KECCAK_EMPTY; // reexport for convenience pub use ethers_core::types::{EIP1186ProofResponse as AccountProof, StorageProof}; @@ -28,7 +28,7 @@ impl Default for BasicAccount { BasicAccount { balance: 0.into(), nonce: 0.into(), - code_hash: KECCAK_EMPTY, + code_hash: KECCAK_EMPTY.into(), storage_root: KECCAK_NULL_RLP, } } diff --git a/anvil/core/src/eth/receipt.rs b/anvil/core/src/eth/receipt.rs index e529d088ebe81..e663b33329086 100644 --- a/anvil/core/src/eth/receipt.rs +++ b/anvil/core/src/eth/receipt.rs @@ -6,6 +6,7 @@ use ethers_core::{ rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}, }, }; +use foundry_evm::utils::{b256_to_h256, h256_to_b256}; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "fastrlp", derive(open_fastrlp::RlpEncodable, open_fastrlp::RlpDecodable))] @@ -16,17 +17,25 @@ pub struct Log { pub data: Bytes, } -impl From for Log { - fn from(log: revm::Log) -> Self { - let revm::Log { address, topics, data } = log; - Log { address, topics, data: data.into() } +impl From for Log { + fn from(log: revm::primitives::Log) -> Self { + let revm::primitives::Log { address, topics, data } = log; + Log { + address: address.into(), + topics: topics.into_iter().map(b256_to_h256).collect(), + data: data.into(), + } } } -impl From for revm::Log { +impl From for revm::primitives::Log { fn from(log: Log) -> Self { let Log { address, topics, data } = log; - revm::Log { address, topics, data: data.0 } + revm::primitives::Log { + address: address.into(), + topics: topics.into_iter().map(h256_to_b256).collect(), + data: data.0, + } } } diff --git a/anvil/core/src/eth/transaction/mod.rs b/anvil/core/src/eth/transaction/mod.rs index f5aa62cc1a7ef..b204cfc30cb9d 100644 --- a/anvil/core/src/eth/transaction/mod.rs +++ b/anvil/core/src/eth/transaction/mod.rs @@ -2,7 +2,7 @@ use crate::eth::{ receipt::Log, - utils::{enveloped, to_access_list}, + utils::{enveloped, to_revm_access_list}, }; use ethers_core::{ types::{ @@ -15,7 +15,10 @@ use ethers_core::{ }, }; use foundry_evm::trace::CallTraceArena; -use revm::{CreateScheme, Return, TransactTo, TxEnv}; +use revm::{ + interpreter::InstructionResult, + primitives::{CreateScheme, TransactTo, TxEnv}, +}; use std::ops::Deref; /// compatibility with `ethers-rs` types @@ -1177,7 +1180,7 @@ impl PendingTransaction { pub fn to_revm_tx_env(&self) -> TxEnv { fn transact_to(kind: &TransactionKind) -> TransactTo { match kind { - TransactionKind::Call(c) => TransactTo::Call(*c), + TransactionKind::Call(c) => TransactTo::Call((*c).into()), TransactionKind::Create => TransactTo::Create(CreateScheme::Create), } } @@ -1188,13 +1191,13 @@ impl PendingTransaction { let chain_id = tx.chain_id(); let LegacyTransaction { nonce, gas_price, gas_limit, value, kind, input, .. } = tx; TxEnv { - caller, + caller: caller.into(), transact_to: transact_to(kind), data: input.0.clone(), chain_id, nonce: Some(nonce.as_u64()), - value: *value, - gas_price: *gas_price, + value: (*value).into(), + gas_price: (*gas_price).into(), gas_priority_fee: None, gas_limit: gas_limit.as_u64(), access_list: vec![], @@ -1213,16 +1216,16 @@ impl PendingTransaction { .. } = tx; TxEnv { - caller, + caller: caller.into(), transact_to: transact_to(kind), data: input.0.clone(), chain_id: Some(*chain_id), nonce: Some(nonce.as_u64()), - value: *value, - gas_price: *gas_price, + value: (*value).into(), + gas_price: (*gas_price).into(), gas_priority_fee: None, gas_limit: gas_limit.as_u64(), - access_list: to_access_list(access_list.0.clone()), + access_list: to_revm_access_list(access_list.0.clone()), } } TypedTransaction::EIP1559(tx) => { @@ -1239,16 +1242,16 @@ impl PendingTransaction { .. } = tx; TxEnv { - caller, + caller: caller.into(), transact_to: transact_to(kind), data: input.0.clone(), chain_id: Some(*chain_id), nonce: Some(nonce.as_u64()), - value: *value, - gas_price: *max_fee_per_gas, - gas_priority_fee: Some(*max_priority_fee_per_gas), + value: (*value).into(), + gas_price: (*max_fee_per_gas).into(), + gas_priority_fee: Some((*max_priority_fee_per_gas).into()), gas_limit: gas_limit.as_u64(), - access_list: to_access_list(access_list.0.clone()), + access_list: to_revm_access_list(access_list.0.clone()), } } } @@ -1266,7 +1269,7 @@ pub struct TransactionInfo { pub logs: Vec, pub logs_bloom: Bloom, pub traces: CallTraceArena, - pub exit: Return, + pub exit: InstructionResult, pub out: Option, } diff --git a/anvil/core/src/eth/utils.rs b/anvil/core/src/eth/utils.rs index eee15fccb97c2..f6a0f6f412b28 100644 --- a/anvil/core/src/eth/utils.rs +++ b/anvil/core/src/eth/utils.rs @@ -5,7 +5,8 @@ use ethers_core::{ rlp::{Encodable, RlpStream}, }, }; -use foundry_evm::utils::h256_to_u256_be; +use foundry_evm::utils::{h160_to_b160, h256_to_u256_be, u256_to_ru256}; +use revm::primitives::{B160, U256 as rU256}; pub fn enveloped(id: u8, v: &T, s: &mut RlpStream) { let encoded = rlp::encode(v); @@ -20,3 +21,14 @@ pub fn to_access_list(list: Vec) -> Vec<(Address, Vec)> { .map(|item| (item.address, item.storage_keys.into_iter().map(h256_to_u256_be).collect())) .collect() } + +pub fn to_revm_access_list(list: Vec) -> Vec<(B160, Vec)> { + list.into_iter() + .map(|item| { + ( + h160_to_b160(item.address), + item.storage_keys.into_iter().map(h256_to_u256_be).map(u256_to_ru256).collect(), + ) + }) + .collect() +} diff --git a/anvil/core/src/types.rs b/anvil/core/src/types.rs index 75ad4885775e2..8725b9f4a384d 100644 --- a/anvil/core/src/types.rs +++ b/anvil/core/src/types.rs @@ -1,5 +1,5 @@ use ethers_core::types::{H256, U256, U64}; -use revm::SpecId; +use revm::primitives::SpecId; #[cfg(feature = "serde")] use serde::{de::Error, Deserializer, Serializer}; diff --git a/anvil/src/config.rs b/anvil/src/config.rs index 3571a690146c6..806d2dec001e5 100644 --- a/anvil/src/config.rs +++ b/anvil/src/config.rs @@ -27,12 +27,13 @@ use ethers::{ types::BlockNumber, utils::{format_ether, hex, to_checksum, WEI_IN_ETHER}, }; +use forge::utils::{h256_to_b256, u256_to_ru256}; use foundry_common::{ProviderBuilder, ALCHEMY_FREE_TIER_CUPS, REQUEST_TIMEOUT}; use foundry_config::Config; use foundry_evm::{ executor::fork::{BlockchainDb, BlockchainDbMeta, SharedBackend}, revm, - revm::{BlockEnv, CfgEnv, SpecId, TxEnv}, + revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv, U256 as rU256}, utils::apply_chain_and_block_specific_env_changes, }; use parking_lot::RwLock; @@ -748,10 +749,10 @@ impl NodeConfig { /// *Note*: only memory based backend for now pub(crate) async fn setup(&mut self) -> mem::Backend { // configure the revm environment - let mut env = revm::Env { + let mut env = revm::primitives::Env { cfg: CfgEnv { spec_id: self.get_hardfork().into(), - chain_id: self.get_chain_id().into(), + chain_id: rU256::from(self.get_chain_id()), limit_contract_code_size: self.code_size_limit, // EIP-3607 rejects transactions from senders with deployed code. // If EIP-3607 is enabled it can cause issues during fuzz/invariant tests if the @@ -761,8 +762,8 @@ impl NodeConfig { ..Default::default() }, block: BlockEnv { - gas_limit: self.gas_limit, - basefee: self.get_base_fee(), + gas_limit: self.gas_limit.into(), + basefee: self.get_base_fee().into(), ..Default::default() }, tx: TxEnv { chain_id: self.get_chain_id().into(), ..Default::default() }, @@ -835,15 +836,18 @@ impl NodeConfig { // we only use the gas limit value of the block if it is non-zero, since there are networks where this is not used and is always `0x0` which would inevitably result in `OutOfGas` errors as soon as the evm is about to record gas, See also - let gas_limit = - if block.gas_limit.is_zero() { env.block.gas_limit } else { block.gas_limit }; + let gas_limit = if block.gas_limit.is_zero() { + env.block.gas_limit + } else { + u256_to_ru256(block.gas_limit) + }; env.block = BlockEnv { - number: fork_block_number.into(), - timestamp: block.timestamp, - difficulty: block.difficulty, + number: rU256::from(fork_block_number), + timestamp: block.timestamp.into(), + difficulty: block.difficulty.into(), // ensures prevrandao is set - prevrandao: Some(block.mix_hash.unwrap_or_default()), + prevrandao: Some(block.mix_hash.unwrap_or_default()).map(h256_to_b256), gas_limit, // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, @@ -857,7 +861,7 @@ impl NodeConfig { if self.base_fee.is_none() { if let Some(base_fee) = block.base_fee_per_gas { self.base_fee = Some(base_fee); - env.block.basefee = base_fee; + env.block.basefee = u256_to_ru256(base_fee); // this is the base fee of the current block, but we need the base fee of // the next block let next_block_base_fee = fees.get_next_block_base_fee_per_gas( @@ -892,7 +896,7 @@ impl NodeConfig { // need to update the dev signers and env with the chain id self.set_chain_id(Some(chain_id)); - env.cfg.chain_id = chain_id.into(); + env.cfg.chain_id = rU256::from(chain_id); env.tx.chain_id = chain_id.into(); chain_id }; @@ -946,7 +950,7 @@ impl NodeConfig { let genesis = GenesisConfig { timestamp: self.get_genesis_timestamp(), - balance: self.genesis_balance, + balance: self.genesis_balance.into(), accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(), fork_genesis_account_infos: Arc::new(Default::default()), genesis_init: self.genesis.clone(), diff --git a/anvil/src/eth/api.rs b/anvil/src/eth/api.rs index faa3bc0421f85..744064a7e63f0 100644 --- a/anvil/src/eth/api.rs +++ b/anvil/src/eth/api.rs @@ -23,7 +23,7 @@ use crate::{ }, filter::{EthFilter, Filters, LogsFilter}, mem::transaction_build, - revm::TransactOut, + revm::primitives::Output, ClientFork, LoggingManager, Miner, MiningMode, StorageInfo, }; use anvil_core::{ @@ -55,11 +55,11 @@ use ethers::{ }, utils::rlp, }; -use forge::{executor::DatabaseRef, revm::BlockEnv}; +use forge::{executor::DatabaseRef, revm::primitives::BlockEnv}; use foundry_common::ProviderBuilder; use foundry_evm::{ executor::backend::DatabaseError, - revm::{return_ok, return_revert, Return}, + revm::interpreter::{return_ok, return_revert, InstructionResult}, }; use futures::channel::mpsc::Receiver; use parking_lot::RwLock; @@ -1990,7 +1990,7 @@ impl EthApi { // get the highest possible gas limit, either the request's set value or the currently // configured gas limit - let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit); + let mut highest_gas_limit = request.gas.unwrap_or(block_env.gas_limit.into()); // check with the funds of the sender if let Some(from) = request.from { @@ -2029,7 +2029,7 @@ impl EthApi { return_ok!() => { // succeeded } - Return::OutOfGas | Return::LackOfFundForGasLimit | Return::OutOfFund => { + InstructionResult::OutOfGas | InstructionResult::OutOfFund => { return Err(InvalidTransactionError::OutOfGas(gas_limit).into()) } // need to check if the revert was due to lack of gas or unrelated reason @@ -2098,10 +2098,9 @@ impl EthApi { } last_highest_gas_limit = highest_gas_limit; } - Return::Revert | - Return::OutOfGas | - Return::LackOfFundForGasLimit | - Return::OutOfFund => { + InstructionResult::Revert | + InstructionResult::OutOfGas | + InstructionResult::OutOfFund => { lowest_gas_limit = mid_gas_limit; } reason => { @@ -2339,16 +2338,16 @@ fn required_marker(provided_nonce: U256, on_chain_nonce: U256, from: Address) -> } } -fn convert_transact_out(out: &TransactOut) -> Bytes { +fn convert_transact_out(out: &Option) -> Bytes { match out { - TransactOut::None => Default::default(), - TransactOut::Call(out) => out.to_vec().into(), - TransactOut::Create(out, _) => out.to_vec().into(), + None => Default::default(), + Some(Output::Call(out)) => out.to_vec().into(), + Some(Output::Create(out, _)) => out.to_vec().into(), } } /// Returns an error if the `exit` code is _not_ ok -fn ensure_return_ok(exit: Return, out: &TransactOut) -> Result { +fn ensure_return_ok(exit: InstructionResult, out: &Option) -> Result { let out = convert_transact_out(out); match exit { return_ok!() => Ok(out), diff --git a/anvil/src/eth/backend/db.rs b/anvil/src/eth/backend/db.rs index 81657551ed516..7c8dc0f37c385 100644 --- a/anvil/src/eth/backend/db.rs +++ b/anvil/src/eth/backend/db.rs @@ -1,13 +1,13 @@ //! Helper types for working with [revm](foundry_evm::revm) -use crate::{mem::state::trie_hash_db, revm::AccountInfo, U256}; +use crate::{mem::state::trie_hash_db, revm::primitives::AccountInfo, U256}; use anvil_core::eth::trie::KeccakHasher; use ethers::{ - prelude::{Address, Bytes, H160}, + prelude::{Address, Bytes}, types::H256, utils::keccak256, }; -use forge::revm::KECCAK_EMPTY; +use forge::revm::primitives::{B160, B256, KECCAK_EMPTY, U256 as rU256}; use foundry_common::errors::FsPathError; use foundry_evm::{ executor::{ @@ -16,7 +16,8 @@ use foundry_evm::{ }, revm::{ db::{CacheDB, DbAccount}, - Bytecode, Database, DatabaseCommit, + primitives::Bytecode, + Database, DatabaseCommit, }, HashMap, }; @@ -84,7 +85,7 @@ pub trait Db: /// Sets the nonce of the given address fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<()> { - let mut info = self.basic(address)?.unwrap_or_default(); + let mut info = self.basic(address.into())?.unwrap_or_default(); info.nonce = nonce; self.insert_account(address, info); Ok(()) @@ -92,19 +93,19 @@ pub trait Db: /// Sets the balance of the given address fn set_balance(&mut self, address: Address, balance: U256) -> DatabaseResult<()> { - let mut info = self.basic(address)?.unwrap_or_default(); - info.balance = balance; + let mut info = self.basic(address.into())?.unwrap_or_default(); + info.balance = balance.into(); self.insert_account(address, info); Ok(()) } /// Sets the balance of the given address fn set_code(&mut self, address: Address, code: Bytes) -> DatabaseResult<()> { - let mut info = self.basic(address)?.unwrap_or_default(); + let mut info = self.basic(address.into())?.unwrap_or_default(); let code_hash = if code.as_ref().is_empty() { KECCAK_EMPTY } else { - H256::from_slice(&keccak256(code.as_ref())[..]) + B256::from_slice(&keccak256(code.as_ref())[..]) }; info.code_hash = code_hash; info.code = Some(Bytecode::new_raw(code.0).to_checked()); @@ -124,7 +125,7 @@ pub trait Db: /// Deserialize and add all chain data to the backend storage fn load_state(&mut self, state: SerializableState) -> DatabaseResult { for (addr, account) in state.accounts.into_iter() { - let old_account_nonce = DatabaseRef::basic(self, addr) + let old_account_nonce = DatabaseRef::basic(self, addr.into()) .ok() .and_then(|acc| acc.map(|acc| acc.nonce)) .unwrap_or_default(); @@ -135,7 +136,7 @@ pub trait Db: self.insert_account( addr, AccountInfo { - balance: account.balance, + balance: account.balance.into(), code_hash: KECCAK_EMPTY, // will be set automatically code: if account.code.0.is_empty() { None @@ -176,15 +177,15 @@ pub trait Db: /// [Backend::pending_block()](crate::eth::backend::mem::Backend::pending_block()) impl + Send + Sync + Clone + fmt::Debug> Db for CacheDB { fn insert_account(&mut self, address: Address, account: AccountInfo) { - self.insert_account_info(address, account) + self.insert_account_info(address.into(), account) } fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { - self.insert_account_storage(address, slot, val) + self.insert_account_storage(address.into(), slot.into(), val.into()) } fn insert_block_hash(&mut self, number: U256, hash: H256) { - self.block_hashes.insert(number, hash); + self.block_hashes.insert(number.into(), hash.into()); } fn dump_state(&self) -> DatabaseResult> { @@ -260,19 +261,19 @@ impl StateDb { impl DatabaseRef for StateDb { type Error = DatabaseError; - fn basic(&self, address: H160) -> DatabaseResult> { + fn basic(&self, address: B160) -> DatabaseResult> { self.0.basic(address) } - fn code_by_hash(&self, code_hash: H256) -> DatabaseResult { + fn code_by_hash(&self, code_hash: B256) -> DatabaseResult { self.0.code_by_hash(code_hash) } - fn storage(&self, address: H160, index: U256) -> DatabaseResult { + fn storage(&self, address: B160, index: rU256) -> DatabaseResult { self.0.storage(address, index) } - fn block_hash(&self, number: U256) -> DatabaseResult { + fn block_hash(&self, number: rU256) -> DatabaseResult { self.0.block_hash(number) } } diff --git a/anvil/src/eth/backend/executor.rs b/anvil/src/eth/backend/executor.rs index 0e8291ff32f5c..0ae5f5eb6ead9 100644 --- a/anvil/src/eth/backend/executor.rs +++ b/anvil/src/eth/backend/executor.rs @@ -17,21 +17,30 @@ use ethers::{ types::{Bloom, H256, U256}, utils::rlp, }; -use forge::revm::ExecutionResult; +use forge::{ + revm::primitives::ExecutionResult, + utils::{ + b160_to_h160, eval_to_instruction_result, h160_to_b160, halt_to_instruction_result, + ru256_to_u256, + }, +}; use foundry_evm::{ executor::backend::DatabaseError, revm, - revm::{BlockEnv, CfgEnv, Env, Return, SpecId, TransactOut}, + revm::{ + interpreter::InstructionResult, + primitives::{BlockEnv, CfgEnv, Env, Output, SpecId}, + }, trace::{node::CallTraceNode, CallTraceArena}, }; -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; use tracing::{trace, warn}; /// Represents an executed transaction (transacted on the DB) pub struct ExecutedTransaction { transaction: Arc, - exit_reason: Return, - out: TransactOut, + exit_reason: InstructionResult, + out: Option, gas_used: u64, logs: Vec, traces: Vec, @@ -48,7 +57,7 @@ impl ExecutedTransaction { let logs = self.logs.clone(); // successful return see [Return] - let status_code = u8::from(self.exit_reason as u8 <= Return::SelfDestruct as u8); + let status_code = u8::from(self.exit_reason as u8 <= InstructionResult::SelfDestruct as u8); match &self.transaction.pending_transaction.transaction.transaction { TypedTransaction::Legacy(_) => TypedReceipt::Legacy(EIP658Receipt { status_code, @@ -115,7 +124,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let block_number = self.block_env.number; let difficulty = self.block_env.difficulty; let beneficiary = self.block_env.coinbase; - let timestamp = self.block_env.timestamp.as_u64(); + let timestamp = ru256_to_u256(self.block_env.timestamp).as_u64(); let base_fee = if (self.cfg_env.spec_id as u8) >= (SpecId::LONDON as u8) { Some(self.block_env.basefee) } else { @@ -145,7 +154,7 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let ExecutedTransaction { transaction, logs, out, traces, exit_reason: exit, .. } = tx; logs_bloom(logs.clone(), &mut bloom); - let contract_address = if let TransactOut::Create(_, contract_address) = out { + let contract_address = if let Some(Output::Create(_, contract_address)) = out { trace!(target: "backend", "New contract deployed: at {:?}", contract_address); contract_address } else { @@ -158,14 +167,14 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' transaction_index, from: *transaction.pending_transaction.sender(), to: transaction.pending_transaction.transaction.to().copied(), - contract_address, + contract_address: contract_address.map(b160_to_h160), logs, logs_bloom: *receipt.logs_bloom(), traces: CallTraceArena { arena: traces }, exit, out: match out { - TransactOut::Call(b) => Some(b.into()), - TransactOut::Create(b, _) => Some(b.into()), + Some(Output::Call(b)) => Some(b.into()), + Some(Output::Create(b, _)) => Some(b.into()), _ => None, }, }; @@ -180,19 +189,19 @@ impl<'a, DB: Db + ?Sized, Validator: TransactionValidator> TransactionExecutor<' let partial_header = PartialHeader { parent_hash, - beneficiary, + beneficiary: b160_to_h160(beneficiary), state_root: self.db.maybe_state_root().unwrap_or_default(), receipts_root, logs_bloom: bloom, - difficulty, - number: block_number, - gas_limit, + difficulty: difficulty.into(), + number: block_number.into(), + gas_limit: gas_limit.into(), gas_used: cumulative_gas_used, timestamp, extra_data: Default::default(), mix_hash: Default::default(), nonce: Default::default(), - base_fee, + base_fee: base_fee.map(|x| x.into()), }; let block = Block::new(partial_header, transactions.clone(), ommers); @@ -225,14 +234,14 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator fn next(&mut self) -> Option { let transaction = self.pending.next()?; let sender = *transaction.pending_transaction.sender(); - let account = match self.db.basic(sender).map(|acc| acc.unwrap_or_default()) { + let account = match self.db.basic(h160_to_b160(sender)).map(|acc| acc.unwrap_or_default()) { Ok(account) => account, Err(err) => return Some(TransactionExecutionOutcome::DatabaseError(transaction, err)), }; let env = self.env_for(&transaction.pending_transaction); // check that we comply with the block's gas limit let max_gas = self.gas_used.saturating_add(U256::from(env.tx.gas_limit)); - if max_gas > env.block.gas_limit { + if max_gas > env.block.gas_limit.into() { return Some(TransactionExecutionOutcome::Exhausted(transaction)) } @@ -258,11 +267,31 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator trace!(target: "backend", "[{:?}] executing", transaction.hash()); // transact and commit the transaction - let ExecutionResult { exit_reason, out, gas_used, logs, .. } = - evm.inspect_commit(&mut inspector); + let exec_result = match evm.inspect_commit(&mut inspector) { + Ok(exec_result) => exec_result, + Err(err) => { + warn!(target: "backend", "[{:?}] failed to execute: {:?}", transaction.hash(), err); + return Some(TransactionExecutionOutcome::DatabaseError( + transaction, + DatabaseError::TransactionNotFound(H256::from_str("0x").unwrap()), + )) + } + }; inspector.print_logs(); - if exit_reason == Return::OutOfGas { + let (exit_reason, gas_used, out, logs) = match exec_result { + ExecutionResult::Success { reason, gas_used, logs, output, .. } => { + (eval_to_instruction_result(reason), gas_used, Some(output), Some(logs)) + } + ExecutionResult::Revert { gas_used, output } => { + (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None) + } + ExecutionResult::Halt { reason, gas_used } => { + (halt_to_instruction_result(reason), gas_used, None, None) + } + }; + + if exit_reason == InstructionResult::OutOfGas { // this currently useful for debugging estimations warn!(target: "backend", "[{:?}] executed with out of gas", transaction.hash()) } @@ -278,7 +307,7 @@ impl<'a, 'b, DB: Db + ?Sized, Validator: TransactionValidator> Iterator exit_reason, out, gas_used, - logs: logs.into_iter().map(Into::into).collect(), + logs: logs.unwrap_or_default().into_iter().map(Into::into).collect(), traces: inspector.tracer.unwrap_or_default().traces.arena, }; diff --git a/anvil/src/eth/backend/genesis.rs b/anvil/src/eth/backend/genesis.rs index 1a919afdabbd3..044e8d23209fe 100644 --- a/anvil/src/eth/backend/genesis.rs +++ b/anvil/src/eth/backend/genesis.rs @@ -6,15 +6,18 @@ use crate::{ }; use ethers::{ abi::ethereum_types::BigEndianHash, - types::{Address, H256, U256}, + types::{Address, H256}, +}; +use forge::{ + revm::primitives::{B160, B256, KECCAK_EMPTY, U256}, + utils::b160_to_h160, }; -use forge::revm::KECCAK_EMPTY; use foundry_evm::{ executor::{ backend::{snapshot::StateSnapshot, DatabaseError, DatabaseResult}, DatabaseRef, }, - revm::{AccountInfo, Bytecode}, + revm::primitives::{AccountInfo, Bytecode}, }; use parking_lot::Mutex; use std::{collections::HashMap, sync::Arc}; @@ -102,31 +105,34 @@ pub(crate) struct AtGenesisStateDb<'a> { impl<'a> DatabaseRef for AtGenesisStateDb<'a> { type Error = DatabaseError; - fn basic(&self, address: Address) -> DatabaseResult> { - if let Some(acc) = self.accounts.get(&address).cloned() { + fn basic(&self, address: B160) -> DatabaseResult> { + if let Some(acc) = self.accounts.get(&address.into()).cloned() { return Ok(Some(acc)) } self.db.basic(address) } - fn code_by_hash(&self, code_hash: H256) -> DatabaseResult { + fn code_by_hash(&self, code_hash: B256) -> DatabaseResult { if let Some((_, acc)) = self.accounts.iter().find(|(_, acc)| acc.code_hash == code_hash) { return Ok(acc.code.clone().unwrap_or_default()) } self.db.code_by_hash(code_hash) } - fn storage(&self, address: Address, index: U256) -> DatabaseResult { - if let Some(acc) = - self.genesis.as_ref().and_then(|genesis| genesis.alloc.accounts.get(&address)) + fn storage(&self, address: B160, index: U256) -> DatabaseResult { + if let Some(acc) = self + .genesis + .as_ref() + .and_then(|genesis| genesis.alloc.accounts.get(&b160_to_h160(address))) { - let value = acc.storage.get(&H256::from_uint(&index)).copied().unwrap_or_default(); - return Ok(value.into_uint()) + let value = + acc.storage.get(&H256::from_uint(&index.into())).copied().unwrap_or_default(); + return Ok(value.into_uint().into()) } self.db.storage(address, index) } - fn block_hash(&self, number: U256) -> DatabaseResult { + fn block_hash(&self, number: U256) -> DatabaseResult { self.db.block_hash(number) } } diff --git a/anvil/src/eth/backend/mem/fork_db.rs b/anvil/src/eth/backend/mem/fork_db.rs index d6a3d5825d64a..863f82f31b0e2 100644 --- a/anvil/src/eth/backend/mem/fork_db.rs +++ b/anvil/src/eth/backend/mem/fork_db.rs @@ -2,7 +2,7 @@ use crate::{ eth::backend::db::{ Db, MaybeHashDatabase, SerializableAccountRecord, SerializableState, StateDb, }, - revm::AccountInfo, + revm::primitives::AccountInfo, Address, U256, }; use ethers::prelude::H256; @@ -21,12 +21,12 @@ impl Db for ForkedDatabase { fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { // this ensures the account is loaded first - let _ = Database::basic(self, address)?; + let _ = Database::basic(self, address.into())?; self.database_mut().set_storage_at(address, slot, val) } fn insert_block_hash(&mut self, number: U256, hash: H256) { - self.inner().block_hashes().write().insert(number, hash); + self.inner().block_hashes().write().insert(number.into(), hash.into()); } fn dump_state(&self) -> DatabaseResult> { @@ -44,12 +44,16 @@ impl Db for ForkedDatabase { } .to_checked(); Ok(( - k, + k.into(), SerializableAccountRecord { nonce: v.info.nonce, - balance: v.info.balance, + balance: v.info.balance.into(), code: code.bytes()[..code.len()].to_vec().into(), - storage: v.storage.into_iter().collect(), + storage: v + .storage + .into_iter() + .map(|kv| (kv.0.into(), kv.1.into())) + .collect(), }, )) }) diff --git a/anvil/src/eth/backend/mem/in_memory_db.rs b/anvil/src/eth/backend/mem/in_memory_db.rs index 569f9e2856955..faad463488546 100644 --- a/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/anvil/src/eth/backend/mem/in_memory_db.rs @@ -5,10 +5,11 @@ use crate::{ AsHashDB, Db, MaybeHashDatabase, SerializableAccountRecord, SerializableState, StateDb, }, mem::state::{state_merkle_trie_root, trie_hash_db}, - revm::AccountInfo, + revm::primitives::AccountInfo, Address, U256, }; use ethers::prelude::H256; +use forge::utils::h160_to_b160; use tracing::{trace, warn}; // reexport for convenience @@ -18,15 +19,15 @@ pub use foundry_evm::executor::{backend::MemDb, DatabaseRef}; impl Db for MemDb { fn insert_account(&mut self, address: Address, account: AccountInfo) { - self.inner.insert_account_info(address, account) + self.inner.insert_account_info(address.into(), account) } fn set_storage_at(&mut self, address: Address, slot: U256, val: U256) -> DatabaseResult<()> { - self.inner.insert_account_storage(address, slot, val) + self.inner.insert_account_storage(address.into(), slot.into(), val.into()) } fn insert_block_hash(&mut self, number: U256, hash: H256) { - self.inner.block_hashes.insert(number, hash); + self.inner.block_hashes.insert(number.into(), hash.into()); } fn dump_state(&self) -> DatabaseResult> { @@ -43,12 +44,12 @@ impl Db for MemDb { } .to_checked(); Ok(( - k, + k.into(), SerializableAccountRecord { nonce: v.info.nonce, - balance: v.info.balance, + balance: v.info.balance.into(), code: code.bytes()[..code.len()].to_vec().into(), - storage: v.storage.into_iter().collect(), + storage: v.storage.into_iter().map(|k| (k.0.into(), k.1.into())).collect(), }, )) }) @@ -90,7 +91,7 @@ impl MaybeHashDatabase for MemDb { } fn maybe_account_db(&self, addr: Address) -> Option<(AsHashDB, H256)> { - if let Some(acc) = self.inner.accounts.get(&addr) { + if let Some(acc) = self.inner.accounts.get(&h160_to_b160(addr)) { Some(storage_trie_db(&acc.storage)) } else { Some(storage_trie_db(&Default::default())) @@ -114,11 +115,12 @@ impl MaybeHashDatabase for MemDb { mod tests { use crate::{ eth::backend::db::{Db, SerializableAccountRecord, SerializableState}, - revm::AccountInfo, + revm::primitives::AccountInfo, Address, }; use bytes::Bytes; - use forge::revm::{Bytecode, KECCAK_EMPTY}; + use ethers::types::U256; + use forge::revm::primitives::{Bytecode, KECCAK_EMPTY, U256 as rU256}; use foundry_evm::executor::{backend::MemDb, DatabaseRef}; use std::{collections::BTreeMap, str::FromStr}; @@ -137,7 +139,7 @@ mod tests { dump_db.insert_account( test_addr, AccountInfo { - balance: 123456.into(), + balance: rU256::from(123456), code_hash: KECCAK_EMPTY, code: Some(contract_code.clone()), nonce: 1234, @@ -152,12 +154,15 @@ mod tests { load_db.load_state(state).unwrap(); - let loaded_account = load_db.basic(test_addr).unwrap().unwrap(); + let loaded_account = load_db.basic(test_addr.into()).unwrap().unwrap(); - assert_eq!(loaded_account.balance, 123456.into()); + assert_eq!(loaded_account.balance, rU256::from(123456)); assert_eq!(load_db.code_by_hash(loaded_account.code_hash).unwrap(), contract_code); assert_eq!(loaded_account.nonce, 1234); - assert_eq!(load_db.storage(test_addr, "0x1234567".into()).unwrap(), "0x1".into()); + assert_eq!( + load_db.storage(test_addr.into(), Into::::into("0x1234567").into()).unwrap(), + Into::::into("0x1").into() + ); } // verifies that multiple accounts can be loaded at a time, and storage is merged within those @@ -177,7 +182,7 @@ mod tests { db.insert_account( test_addr, AccountInfo { - balance: 123456.into(), + balance: rU256::from(123456), code_hash: KECCAK_EMPTY, code: Some(contract_code.clone()), nonce: 1234, @@ -214,15 +219,21 @@ mod tests { db.load_state(new_state).unwrap(); - let loaded_account = db.basic(test_addr).unwrap().unwrap(); - let loaded_account2 = db.basic(test_addr2).unwrap().unwrap(); + let loaded_account = db.basic(test_addr.into()).unwrap().unwrap(); + let loaded_account2 = db.basic(test_addr2.into()).unwrap().unwrap(); assert_eq!(loaded_account2.nonce, 1); - assert_eq!(loaded_account.balance, 100100.into()); + assert_eq!(loaded_account.balance, rU256::from(100100)); assert_eq!(db.code_by_hash(loaded_account.code_hash).unwrap(), contract_code); assert_eq!(loaded_account.nonce, 1234); - assert_eq!(db.storage(test_addr, "0x1234567".into()).unwrap(), "0x1".into()); - assert_eq!(db.storage(test_addr, "0x1234568".into()).unwrap(), "0x5".into()); + assert_eq!( + db.storage(test_addr.into(), Into::::into("0x1234567").into()).unwrap(), + Into::::into("0x1").into() + ); + assert_eq!( + db.storage(test_addr.into(), Into::::into("0x1234568").into()).unwrap(), + Into::::into("0x5").into() + ); } } diff --git a/anvil/src/eth/backend/mem/inspector.rs b/anvil/src/eth/backend/mem/inspector.rs index cb45b30888a9c..d515b945c699f 100644 --- a/anvil/src/eth/backend/mem/inspector.rs +++ b/anvil/src/eth/backend/mem/inspector.rs @@ -1,17 +1,19 @@ //! Anvil specific [`revm::Inspector`] implementation -use crate::{ - eth::macros::node_info, - revm::{CreateInputs, Database, Interpreter}, -}; +use crate::{eth::macros::node_info, revm::Database}; use bytes::Bytes; -use ethers::types::{Address, Log, H256}; +use ethers::types::Log; +use forge::revm::primitives::{B160, B256}; use foundry_evm::{ call_inspectors, decode::decode_console_logs, executor::inspector::{LogCollector, Tracer}, revm, - revm::{CallInputs, EVMData, Gas, GasInspector, Return}, + revm::{ + inspectors::GasInspector, + interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, + EVMData, + }, }; use std::{cell::RefCell, rc::Rc}; @@ -60,13 +62,13 @@ impl revm::Inspector for Inspector { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { call_inspectors!( inspector, [&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer], { inspector.initialize_interp(interp, data, is_static) } ); - Return::Continue + InstructionResult::Continue } fn step( @@ -74,7 +76,7 @@ impl revm::Inspector for Inspector { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { call_inspectors!( inspector, [&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer], @@ -82,14 +84,14 @@ impl revm::Inspector for Inspector { inspector.step(interp, data, is_static); } ); - Return::Continue + InstructionResult::Continue } fn log( &mut self, evm_data: &mut EVMData<'_, DB>, - address: &Address, - topics: &[H256], + address: &B160, + topics: &[B256], data: &Bytes, ) { call_inspectors!( @@ -110,8 +112,8 @@ impl revm::Inspector for Inspector { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - eval: Return, - ) -> Return { + eval: InstructionResult, + ) -> InstructionResult { call_inspectors!( inspector, [&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer], @@ -127,7 +129,7 @@ impl revm::Inspector for Inspector { data: &mut EVMData<'_, DB>, call: &mut CallInputs, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { call_inspectors!( inspector, [ @@ -140,7 +142,7 @@ impl revm::Inspector for Inspector { } ); - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) } fn call_end( @@ -148,10 +150,10 @@ impl revm::Inspector for Inspector { data: &mut EVMData<'_, DB>, inputs: &CallInputs, remaining_gas: Gas, - ret: Return, + ret: InstructionResult, out: Bytes, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { call_inspectors!( inspector, [&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer], @@ -166,7 +168,7 @@ impl revm::Inspector for Inspector { &mut self, data: &mut EVMData<'_, DB>, call: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { call_inspectors!( inspector, [&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer], @@ -175,18 +177,18 @@ impl revm::Inspector for Inspector { } ); - (Return::Continue, None, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()) } fn create_end( &mut self, data: &mut EVMData<'_, DB>, inputs: &CreateInputs, - status: Return, - address: Option
, + status: InstructionResult, + address: Option, gas: Gas, retdata: Bytes, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { call_inspectors!( inspector, [&mut self.gas.as_deref().map(|gas| gas.borrow_mut()), &mut self.tracer], diff --git a/anvil/src/eth/backend/mem/mod.rs b/anvil/src/eth/backend/mem/mod.rs index cd06876abe0a2..f10f13d93ebb5 100644 --- a/anvil/src/eth/backend/mem/mod.rs +++ b/anvil/src/eth/backend/mem/mod.rs @@ -23,7 +23,10 @@ use crate::{ inspector::Inspector, storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome}, }, - revm::{db::DatabaseRef, AccountInfo}, + revm::{ + db::DatabaseRef, + primitives::{AccountInfo, U256 as rU256}, + }, }; use anvil_core::{ eth::{ @@ -36,7 +39,7 @@ use anvil_core::{ TransactionInfo, TypedTransaction, }, trie::RefTrieDB, - utils::to_access_list, + utils::to_revm_access_list, }, types::{Forking, Index}, }; @@ -47,21 +50,29 @@ use ethers::{ types::{ transaction::eip2930::AccessList, Address, Block as EthersBlock, BlockId, Bytes, DefaultFrame, Filter, FilteredParams, GethDebugTracingOptions, GethTrace, Log, OtherFields, - Trace, Transaction, TransactionReceipt, + Trace, Transaction, TransactionReceipt, H160, }, utils::{get_contract_address, hex, keccak256, rlp}, }; use forge::{ executor::inspector::AccessListTracer, - revm::{return_ok, BlockEnv, ExecutionResult, Return}, + hashbrown, + revm::{ + interpreter::{return_ok, InstructionResult}, + primitives::{BlockEnv, ExecutionResult}, + }, + utils::{ + eval_to_instruction_result, h256_to_b256, halt_to_instruction_result, ru256_to_u256, + u256_to_ru256, + }, }; use foundry_evm::{ decode::decode_revert, executor::backend::{DatabaseError, DatabaseResult}, revm, revm::{ - db::CacheDB, Account, CreateScheme, Env, SpecId, TransactOut, TransactTo, TxEnv, - KECCAK_EMPTY, + db::CacheDB, + primitives::{Account, CreateScheme, Env, Output, SpecId, TransactTo, TxEnv, KECCAK_EMPTY}, }, utils::u256_to_h256_be, }; @@ -238,7 +249,7 @@ impl Backend { // accounts concurrently by spawning the job to a new task genesis_accounts_futures.push(tokio::task::spawn(async move { let db = db.read().await; - let info = db.basic(address)?.unwrap_or_default(); + let info = db.basic(address.into())?.unwrap_or_default(); Ok::<_, DatabaseError>((address, info)) })); } @@ -313,7 +324,7 @@ impl Backend { /// Returns the `AccountInfo` from the database pub async fn get_account(&self, address: Address) -> DatabaseResult { - Ok(self.db.read().await.basic(address)?.unwrap_or_default()) + Ok(self.db.read().await.basic(address.into())?.unwrap_or_default()) } /// Whether we're forked off some remote client @@ -340,21 +351,21 @@ impl Backend { // update all settings related to the forked block { let mut env = self.env.write(); - env.cfg.chain_id = fork.chain_id().into(); + env.cfg.chain_id = rU256::from(fork.chain_id()); env.block = BlockEnv { - number: fork_block_number.into(), - timestamp: fork_block.timestamp, - gas_limit: fork_block.gas_limit, - difficulty: fork_block.difficulty, - prevrandao: fork_block.mix_hash, + number: rU256::from(fork_block_number), + timestamp: fork_block.timestamp.into(), + gas_limit: fork_block.gas_limit.into(), + difficulty: fork_block.difficulty.into(), + prevrandao: fork_block.mix_hash.map(h256_to_b256), // Keep previous `coinbase` and `basefee` value coinbase: env.block.coinbase, basefee: env.block.basefee, }; - self.time.reset(env.block.timestamp.as_u64()); - self.fees.set_base_fee(env.block.basefee); + self.time.reset(ru256_to_u256(env.block.timestamp).as_u64()); + self.fees.set_base_fee(env.block.basefee.into()); // also reset the total difficulty self.blockchain.storage.write().total_difficulty = fork.total_difficulty(); @@ -425,22 +436,22 @@ impl Backend { /// Sets the block number pub fn set_block_number(&self, number: U256) { let mut env = self.env.write(); - env.block.number = number; + env.block.number = number.into(); } /// Returns the client coinbase address. pub fn coinbase(&self) -> Address { - self.env.read().block.coinbase + self.env.read().block.coinbase.into() } /// Returns the client coinbase address. pub fn chain_id(&self) -> U256 { - self.env.read().cfg.chain_id + self.env.read().cfg.chain_id.into() } /// Returns balance of the given account. pub async fn current_balance(&self, address: Address) -> DatabaseResult { - Ok(self.get_account(address).await?.balance) + Ok(self.get_account(address).await?.balance.into()) } /// Returns balance of the given account. @@ -450,7 +461,7 @@ impl Backend { /// Sets the coinbase address pub fn set_coinbase(&self, address: Address) { - self.env.write().block.coinbase = address; + self.env.write().block.coinbase = address.into(); } /// Sets the nonce of the given address @@ -511,12 +522,12 @@ impl Backend { /// Returns the block gas limit pub fn gas_limit(&self) -> U256 { - self.env().read().block.gas_limit + self.env().read().block.gas_limit.into() } /// Sets the block gas limit pub fn set_gas_limit(&self, gas_limit: U256) { - self.env().write().block.gas_limit = gas_limit; + self.env().write().block.gas_limit = gas_limit.into(); } /// Returns the current base fee @@ -640,9 +651,9 @@ impl Backend { fn next_env(&self) -> Env { let mut env = self.env.read().clone(); // increase block number for this block - env.block.number = env.block.number.saturating_add(U256::one()); - env.block.basefee = self.base_fee(); - env.block.timestamp = self.time.current_call_timestamp().into(); + env.block.number = env.block.number.saturating_add(rU256::from(1)); + env.block.basefee = self.base_fee().into(); + env.block.timestamp = rU256::from(self.time.current_call_timestamp()); env } @@ -650,7 +661,10 @@ impl Backend { pub async fn inspect_tx( &self, tx: Arc, - ) -> (Return, TransactOut, u64, State, Vec) { + ) -> Result< + (InstructionResult, Option, u64, State, Vec), + BlockchainError, + > { let mut env = self.next_env(); env.tx = tx.pending_transaction.to_revm_tx_env(); let db = self.db.read().await; @@ -659,10 +673,28 @@ impl Backend { let mut evm = revm::EVM::new(); evm.env = env; evm.database(&*db); - let (ExecutionResult { exit_reason, out, gas_used, logs, .. }, state) = - evm.inspect_ref(&mut inspector); + let result_and_state = match evm.inspect_ref(&mut inspector) { + Ok(res) => res, + Err(_) => return Err(BlockchainError::EvmError(InstructionResult::FatalExternalError)), + }; + let state = result_and_state.state; + let state: hashbrown::HashMap = + state.into_iter().map(|kv| (kv.0.into(), kv.1)).collect(); + let (exit_reason, gas_used, out, logs) = match result_and_state.result { + ExecutionResult::Success { reason, gas_used, logs, output, .. } => { + (eval_to_instruction_result(reason), gas_used, Some(output), Some(logs)) + } + ExecutionResult::Revert { gas_used, output } => { + (InstructionResult::Revert, gas_used, Some(Output::Call(output)), None) + } + ExecutionResult::Halt { reason, gas_used } => { + (halt_to_instruction_result(reason), gas_used, None, None) + } + }; + inspector.print_logs(); - (exit_reason, out, gas_used, state, logs) + + Ok((exit_reason, out, gas_used, state, logs.unwrap_or_default())) } /// Creates the pending block @@ -728,9 +760,9 @@ impl Backend { let mut env = self.env.read().clone(); // increase block number for this block - env.block.number = env.block.number.saturating_add(U256::one()); - env.block.basefee = current_base_fee; - env.block.timestamp = self.time.next_timestamp().into(); + env.block.number = env.block.number.saturating_add(rU256::from(1)); + env.block.basefee = current_base_fee.into(); + env.block.timestamp = rU256::from(self.time.next_timestamp()); let best_hash = self.blockchain.storage.read().best_hash; @@ -766,7 +798,7 @@ impl Backend { let BlockInfo { block, transactions, receipts } = block; let header = block.header.clone(); - let block_number: U64 = env.block.number.as_u64().into(); + let block_number: U64 = ru256_to_u256(env.block.number).as_u64().into(); trace!( target: "backend", @@ -797,13 +829,13 @@ impl Backend { node_info!(" Gas used: {}", receipt.gas_used()); match info.exit { return_ok!() => (), - Return::OutOfFund => { + InstructionResult::OutOfFund => { node_info!(" Error: reverted due to running out of funds"); } - Return::CallTooDeep => { + InstructionResult::CallTooDeep => { node_info!(" Error: reverted with call too deep"); } - Return::Revert => { + InstructionResult::Revert => { if let Some(ref r) = info.out { if let Ok(reason) = decode_revert(r.as_ref(), None, None) { node_info!(" Error: reverted with '{}'", reason); @@ -817,7 +849,7 @@ impl Backend { node_info!(" Error: reverted without a reason"); } } - Return::OutOfGas => { + InstructionResult::OutOfGas => { node_info!(" Error: ran out of gas"); } reason => { @@ -853,7 +885,7 @@ impl Backend { } // we intentionally set the difficulty to `0` for newer blocks - env.block.difficulty = U256::zero(); + env.block.difficulty = rU256::from(0); // update env with new values *self.env.write() = env; @@ -894,9 +926,9 @@ impl Backend { fee_details: FeeDetails, block_request: Option, overrides: Option, - ) -> Result<(Return, TransactOut, u64, State), BlockchainError> { + ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> { self.with_database_at(block_request, |state, block| { - let block_number = block.number.as_u64(); + let block_number = ru256_to_u256(block.number).as_u64(); let (exit, out, gas, state) = match overrides { None => self.call_with_state(state, request, fee_details, block), Some(overrides) => { @@ -919,7 +951,7 @@ impl Backend { let FeeDetails { gas_price, max_fee_per_gas, max_priority_fee_per_gas } = fee_details; - let gas_limit = gas.unwrap_or(block_env.gas_limit); + let gas_limit = gas.unwrap_or(block_env.gas_limit.into()); let mut env = self.env.read().clone(); env.block = block_env; // we want to disable this in eth_call, since this is common practice used by other node @@ -927,26 +959,26 @@ impl Backend { env.cfg.disable_block_gas_limit = true; if let Some(base) = max_fee_per_gas { - env.block.basefee = base; + env.block.basefee = base.into(); } let gas_price = gas_price.or(max_fee_per_gas).unwrap_or_else(|| self.gas_price()); let caller = from.unwrap_or_default(); env.tx = TxEnv { - caller, + caller: caller.into(), gas_limit: gas_limit.as_u64(), - gas_price, - gas_priority_fee: max_priority_fee_per_gas, + gas_price: gas_price.into(), + gas_priority_fee: max_priority_fee_per_gas.map(u256_to_ru256), transact_to: match to { - Some(addr) => TransactTo::Call(addr), + Some(addr) => TransactTo::Call(addr.into()), None => TransactTo::Create(CreateScheme::Create), }, - value: value.unwrap_or_default(), + value: value.unwrap_or_default().into(), data: data.unwrap_or_default().to_vec().into(), chain_id: None, nonce: nonce.map(|n| n.as_u64()), - access_list: to_access_list(access_list.unwrap_or_default()), + access_list: to_revm_access_list(access_list.unwrap_or_default()), }; env } @@ -957,7 +989,7 @@ impl Backend { request: EthTransactionRequest, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(Return, TransactOut, u64, State), BlockchainError> + ) -> Result<(InstructionResult, Option, u64, State), BlockchainError> where D: DatabaseRef, { @@ -965,8 +997,24 @@ impl Backend { let mut evm = revm::EVM::new(); evm.env = self.build_call_env(request, fee_details, block_env); evm.database(state); - let (ExecutionResult { exit_reason, out, gas_used, .. }, state) = - evm.inspect_ref(&mut inspector); + let result_and_state = match evm.inspect_ref(&mut inspector) { + Ok(result_and_state) => result_and_state, + Err(_) => return Err(BlockchainError::EvmError(InstructionResult::FatalExternalError)), + }; + let state = result_and_state.state; + let state: hashbrown::HashMap = + state.into_iter().map(|kv| (kv.0.into(), kv.1)).collect(); + let (exit_reason, gas_used, out) = match result_and_state.result { + ExecutionResult::Success { reason, gas_used, output, .. } => { + (eval_to_instruction_result(reason), gas_used, Some(output)) + } + ExecutionResult::Revert { gas_used, output } => { + (InstructionResult::Revert, gas_used, Some(Output::Call(output))) + } + ExecutionResult::Halt { reason, gas_used } => { + (halt_to_instruction_result(reason), gas_used, None) + } + }; inspector.print_logs(); Ok((exit_reason, out, gas_used, state)) } @@ -984,8 +1032,22 @@ impl Backend { let mut evm = revm::EVM::new(); evm.env = self.build_call_env(request, fee_details, block); evm.database(state); - let (ExecutionResult { exit_reason, out, gas_used, .. }, _) = - evm.inspect_ref(&mut inspector); + let result_and_state = + match evm.inspect_ref(&mut inspector) { + Ok(result_and_state) => result_and_state, + Err(_) => return Err(BlockchainError::EvmError(InstructionResult::FatalExternalError)), + }; + let (exit_reason, gas_used, out, ) = match result_and_state.result { + ExecutionResult::Success { reason, gas_used, output, .. } => { + (eval_to_instruction_result(reason), gas_used, Some(output), ) + }, + ExecutionResult::Revert { gas_used, output} => { + (InstructionResult::Revert, gas_used, Some(Output::Call(output))) + }, + ExecutionResult::Halt { reason, gas_used } => { + (halt_to_instruction_result(reason), gas_used, None) + }, + }; let res = inspector.tracer.unwrap_or_default().traces.geth_trace(gas_used.into(), opts); trace!(target: "backend", "trace call return {:?} out: {:?} gas {} on block {}", exit_reason, out, gas_used, block_number); Ok(res) @@ -999,7 +1061,7 @@ impl Backend { request: EthTransactionRequest, fee_details: FeeDetails, block_env: BlockEnv, - ) -> Result<(Return, TransactOut, u64, AccessList), BlockchainError> + ) -> Result<(InstructionResult, Option, u64, AccessList), BlockchainError> where D: DatabaseRef, { @@ -1007,7 +1069,7 @@ impl Backend { let to = if let Some(to) = request.to { to } else { - let nonce = state.basic(from)?.unwrap_or_default().nonce; + let nonce = state.basic(from.into())?.unwrap_or_default().nonce; get_contract_address(from, nonce) }; @@ -1021,7 +1083,21 @@ impl Backend { let mut evm = revm::EVM::new(); evm.env = self.build_call_env(request, fee_details, block_env); evm.database(state); - let (ExecutionResult { exit_reason, out, gas_used, .. }, _) = evm.inspect_ref(&mut tracer); + let result_and_state = match evm.inspect_ref(&mut tracer) { + Ok(result_and_state) => result_and_state, + Err(_) => return Err(BlockchainError::EvmError(InstructionResult::FatalExternalError)), + }; + let (exit_reason, gas_used, out) = match result_and_state.result { + ExecutionResult::Success { reason, gas_used, output, .. } => { + (eval_to_instruction_result(reason), gas_used, Some(output)) + } + ExecutionResult::Revert { gas_used, output } => { + (InstructionResult::Revert, gas_used, Some(Output::Call(output))) + } + ExecutionResult::Halt { reason, gas_used } => { + (halt_to_instruction_result(reason), gas_used, None) + } + }; let access_list = tracer.access_list(); Ok((exit_reason, out, gas_used, access_list)) } @@ -1430,13 +1506,13 @@ impl Backend { .with_pending_block(pool_transactions, |state, block| { let block = block.block; let block = BlockEnv { - number: block.header.number, - coinbase: block.header.beneficiary, - timestamp: block.header.timestamp.into(), - difficulty: block.header.difficulty, - prevrandao: Some(block.header.mix_hash), - basefee: block.header.base_fee_per_gas.unwrap_or_default(), - gas_limit: block.header.gas_limit, + number: block.header.number.into(), + coinbase: block.header.beneficiary.into(), + timestamp: rU256::from(block.header.timestamp), + difficulty: block.header.difficulty.into(), + prevrandao: Some(block.header.mix_hash.into()), + basefee: block.header.base_fee_per_gas.unwrap_or_default().into(), + gas_limit: block.header.gas_limit.into(), }; f(state, block) }) @@ -1448,7 +1524,7 @@ impl Backend { }; let block_number: U256 = self.convert_block_number(block_number).into(); - if block_number < self.env.read().block.number { + if block_number < self.env.read().block.number.into() { { let mut states = self.states.write(); @@ -1457,13 +1533,13 @@ impl Backend { .and_then(|block| Some((states.get(&block.header.hash())?, block))) { let block = BlockEnv { - number: block.header.number, - coinbase: block.header.beneficiary, - timestamp: block.header.timestamp.into(), - difficulty: block.header.difficulty, - prevrandao: Some(block.header.mix_hash), - basefee: block.header.base_fee_per_gas.unwrap_or_default(), - gas_limit: block.header.gas_limit, + number: block.header.number.into(), + coinbase: block.header.beneficiary.into(), + timestamp: rU256::from(block.header.timestamp), + difficulty: block.header.difficulty.into(), + prevrandao: Some(block.header.mix_hash).map(Into::into), + basefee: block.header.base_fee_per_gas.unwrap_or_default().into(), + gas_limit: block.header.gas_limit.into(), }; return Ok(f(Box::new(state), block)) } @@ -1480,10 +1556,10 @@ impl Backend { let db = self.db.read().await; let gen_db = self.genesis.state_db_at_genesis(Box::new(&*db)); - block.number = block_number; - block.timestamp = fork.timestamp().into(); - block.difficulty = fork.total_difficulty(); - block.basefee = fork.base_fee().unwrap_or_default(); + block.number = block_number.into(); + block.timestamp = rU256::from(fork.timestamp()); + block.difficulty = fork.total_difficulty().into(); + block.basefee = fork.base_fee().unwrap_or_default().into(); return Ok(f(Box::new(&gen_db), block)) } @@ -1491,7 +1567,7 @@ impl Backend { warn!(target: "backend", "Not historic state found for block={}", block_number); return Err(BlockchainError::BlockOutOfRange( - self.env.read().block.number.as_u64(), + ru256_to_u256(self.env.read().block.number).as_u64(), block_number.as_u64(), )) } @@ -1509,8 +1585,8 @@ impl Backend { ) -> Result { self.with_database_at(block_request, |db, _| { trace!(target: "backend", "get storage for {:?} at {:?}", address, index); - let val = db.storage(address, index)?; - Ok(u256_to_h256_be(val)) + let val = db.storage(address.into(), index.into())?; + Ok(u256_to_h256_be(ru256_to_u256(val))) }) .await? } @@ -1536,7 +1612,7 @@ impl Backend { D: DatabaseRef, { trace!(target: "backend", "get code for {:?}", address); - let account = state.basic(address)?.unwrap_or_default(); + let account = state.basic(address.into())?.unwrap_or_default(); if account.code_hash == KECCAK_EMPTY { // if the code hash is `KECCAK_EMPTY`, we check no further return Ok(Default::default()) @@ -1570,7 +1646,7 @@ impl Backend { D: DatabaseRef, { trace!(target: "backend", "get balance for {:?}", address); - Ok(state.basic(address)?.unwrap_or_default().balance) + Ok(state.basic(address.into())?.unwrap_or_default().balance.into()) } /// Returns the nonce of the address @@ -1593,7 +1669,7 @@ impl Backend { }; self.with_database_at(final_block_request, |db, _| { trace!(target: "backend", "get nonce for {:?}", address); - Ok(db.basic(address)?.unwrap_or_default().nonce.into()) + Ok(db.basic(address.into())?.unwrap_or_default().nonce.into()) }) .await? } @@ -2036,7 +2112,7 @@ impl TransactionValidator for Backend { return Err(InvalidTransactionError::GasTooLow) } - if tx.gas_limit() > env.block.gas_limit { + if tx.gas_limit() > env.block.gas_limit.into() { warn!(target: "backend", "[{:?}] gas too high", tx.hash()); return Err(InvalidTransactionError::GasTooHigh) } @@ -2049,7 +2125,7 @@ impl TransactionValidator for Backend { } if (env.cfg.spec_id as u8) >= (SpecId::LONDON as u8) { - if tx.gas_price() < env.block.basefee { + if tx.gas_price() < env.block.basefee.into() { warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.block.basefee); return Err(InvalidTransactionError::FeeTooLow) } @@ -2073,7 +2149,7 @@ impl TransactionValidator for Backend { InvalidTransactionError::Payment })?; - if account.balance < req_funds { + if account.balance < req_funds.into() { warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender()); return Err(InvalidTransactionError::Payment) } diff --git a/anvil/src/eth/backend/mem/state.rs b/anvil/src/eth/backend/mem/state.rs index d2e8371024cf1..3460ae25c9830 100644 --- a/anvil/src/eth/backend/mem/state.rs +++ b/anvil/src/eth/backend/mem/state.rs @@ -5,19 +5,20 @@ use anvil_core::eth::{state::StateOverride, trie::RefSecTrieDBMut}; use bytes::Bytes; use ethers::{ abi::ethereum_types::BigEndianHash, - types::{Address, H256, U256}, + types::H256, utils::{rlp, rlp::RlpStream}, }; use forge::{ executor::DatabaseRef, revm::{ db::{CacheDB, DbAccount}, - Bytecode, + primitives::{Bytecode, B160, U256 as rU256}, }, + utils::{b160_to_h160, b256_to_h256, ru256_to_u256, u256_to_ru256}, }; use foundry_evm::{ executor::backend::DatabaseError, - revm::{AccountInfo, Log}, + revm::primitives::{AccountInfo, Log}, HashMap as Map, }; use memory_db::HashKey; @@ -30,9 +31,10 @@ pub fn log_rlp_hash(logs: Vec) -> H256 { let mut stream = RlpStream::new(); stream.begin_unbounded_list(); for log in logs { + let topics = log.topics.into_iter().map(b256_to_h256).collect::>(); stream.begin_list(3); - stream.append(&log.address); - stream.append_list(&log.topics); + stream.append(&b160_to_h160(log.address)); + stream.append_list(&topics); stream.append(&log.data); } stream.finalize_unbounded_list(); @@ -43,16 +45,16 @@ pub fn log_rlp_hash(logs: Vec) -> H256 { } /// Returns storage trie of an account as `HashDB` -pub fn storage_trie_db(storage: &Map) -> (AsHashDB, H256) { +pub fn storage_trie_db(storage: &Map) -> (AsHashDB, H256) { // Populate DB with full trie from entries. let (db, root) = { let mut db = , _>>::default(); let mut root = Default::default(); { let mut trie = RefSecTrieDBMut::new(&mut db, &mut root); - for (k, v) in storage.iter().filter(|(_k, v)| *v != &U256::zero()) { + for (k, v) in storage.iter().filter(|(_k, v)| *v != &rU256::from(0)) { let mut temp: [u8; 32] = [0; 32]; - k.to_big_endian(&mut temp); + ru256_to_u256(*k).to_big_endian(&mut temp); let key = H256::from(temp); let value = rlp::encode(v); trie.insert(key.as_bytes(), value.as_ref()).unwrap(); @@ -65,7 +67,7 @@ pub fn storage_trie_db(storage: &Map) -> (AsHashDB, H256) { } /// Returns the account data as `HashDB` -pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, H256) { +pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, H256) { let accounts = trie_accounts(accounts); // Populate DB with full trie from entries. @@ -85,7 +87,7 @@ pub fn trie_hash_db(accounts: &Map) -> (AsHashDB, H256) { } /// Returns all RLP-encoded Accounts -pub fn trie_accounts(accounts: &Map) -> Vec<(Address, Bytes)> { +pub fn trie_accounts(accounts: &Map) -> Vec<(B160, Bytes)> { accounts .iter() .map(|(address, account)| { @@ -95,12 +97,12 @@ pub fn trie_accounts(accounts: &Map) -> Vec<(Address, Bytes) .collect() } -pub fn state_merkle_trie_root(accounts: &Map) -> H256 { +pub fn state_merkle_trie_root(accounts: &Map) -> H256 { trie_hash_db(accounts).1 } /// Returns the RLP for this account. -pub fn trie_account_rlp(info: &AccountInfo, storage: &Map) -> Bytes { +pub fn trie_account_rlp(info: &AccountInfo, storage: &Map) -> Bytes { let mut stream = RlpStream::new_list(4); stream.append(&info.nonce); stream.append(&info.balance); @@ -119,7 +121,7 @@ where { let mut cache_db = CacheDB::new(state); for (account, account_overrides) in overrides.iter() { - let mut account_info = cache_db.basic(*account)?.unwrap_or_default(); + let mut account_info = cache_db.basic((*account).into())?.unwrap_or_default(); if let Some(nonce) = account_overrides.nonce { account_info.nonce = nonce; @@ -128,10 +130,10 @@ where account_info.code = Some(Bytecode::new_raw(code.to_vec().into())); } if let Some(balance) = account_overrides.balance { - account_info.balance = balance; + account_info.balance = balance.into(); } - cache_db.insert_account_info(*account, account_info); + cache_db.insert_account_info((*account).into(), account_info); // We ensure that not both state and state_diff are set. // If state is set, we must mark the account as "NewlyCreated", so that the old storage @@ -145,19 +147,21 @@ where (None, None) => (), (Some(new_account_state), None) => { cache_db.replace_account_storage( - *account, + (*account).into(), new_account_state .iter() - .map(|(key, value)| (key.into_uint(), value.into_uint())) + .map(|(key, value)| { + (u256_to_ru256(key.into_uint()), u256_to_ru256(value.into_uint())) + }) .collect(), )?; } (None, Some(account_state_diff)) => { for (key, value) in account_state_diff.iter() { cache_db.insert_account_storage( - *account, - key.into_uint(), - value.into_uint(), + (*account).into(), + key.into_uint().into(), + value.into_uint().into(), )?; } } diff --git a/anvil/src/eth/backend/mem/storage.rs b/anvil/src/eth/backend/mem/storage.rs index cde8e4ae89fb9..0040b784df837 100644 --- a/anvil/src/eth/backend/mem/storage.rs +++ b/anvil/src/eth/backend/mem/storage.rs @@ -15,7 +15,7 @@ use ethers::{ prelude::{BlockId, BlockNumber, DefaultFrame, Trace, H256, H256 as TxHash, U64}, types::{ActionType, Bytes, GethDebugTracingOptions, TransactionReceipt, U256}, }; -use forge::revm::{Env, Return}; +use forge::revm::{interpreter::InstructionResult, primitives::Env}; use parking_lot::RwLock; use std::{ collections::{HashMap, VecDeque}, @@ -226,9 +226,9 @@ impl BlockchainStorage { let partial_header = PartialHeader { timestamp, base_fee, - gas_limit: env.block.gas_limit, - beneficiary: env.block.coinbase, - difficulty: env.block.difficulty, + gas_limit: env.block.gas_limit.into(), + beneficiary: env.block.coinbase.into(), + difficulty: env.block.difficulty.into(), ..Default::default() }; let block = Block::new::(partial_header, vec![], vec![]); @@ -372,7 +372,7 @@ impl MinedTransaction { let action = node.parity_action(); let result = node.parity_result(); - let action_type = if node.status() == Return::SelfDestruct { + let action_type = if node.status() == InstructionResult::SelfDestruct { ActionType::Suicide } else { node.kind().into() @@ -415,7 +415,10 @@ mod tests { use super::*; use crate::eth::backend::db::Db; use ethers::{abi::ethereum_types::BigEndianHash, types::Address}; - use forge::revm::{db::DatabaseRef, AccountInfo}; + use forge::revm::{ + db::DatabaseRef, + primitives::{AccountInfo, U256 as rU256}, + }; use foundry_evm::executor::backend::MemDb; #[test] @@ -433,7 +436,7 @@ mod tests { let mut state = MemDb::default(); let addr = Address::random(); - let info = AccountInfo::from_balance(1337.into()); + let info = AccountInfo::from_balance(rU256::from(1337)); state.insert_account(addr, info); storage.insert(one, StateDb::new(state)); storage.insert(two, StateDb::new(MemDb::default())); @@ -446,8 +449,8 @@ mod tests { let loaded = storage.get(&one).unwrap(); - let acc = loaded.basic(addr).unwrap().unwrap(); - assert_eq!(acc.balance, 1337u64.into()); + let acc = loaded.basic(addr.into()).unwrap().unwrap(); + assert_eq!(acc.balance, rU256::from(1337u64)); } #[tokio::test(flavor = "multi_thread")] @@ -461,7 +464,7 @@ mod tests { let hash = H256::from_uint(&U256::from(idx)); let addr = Address::from(hash); let balance = (idx * 2) as u64; - let info = AccountInfo::from_balance(balance.into()); + let info = AccountInfo::from_balance(rU256::from(balance)); state.insert_account(addr, info); storage.insert(hash, StateDb::new(state)); } @@ -476,9 +479,9 @@ mod tests { let hash = H256::from_uint(&U256::from(idx)); let addr = Address::from(hash); let loaded = storage.get(&hash).unwrap(); - let acc = loaded.basic(addr).unwrap().unwrap(); + let acc = loaded.basic(addr.into()).unwrap().unwrap(); let balance = (idx * 2) as u64; - assert_eq!(acc.balance, balance.into()); + assert_eq!(acc.balance, rU256::from(balance)); } } } diff --git a/anvil/src/eth/backend/validate.rs b/anvil/src/eth/backend/validate.rs index 83f68f5c4c14f..3ea666f15a156 100644 --- a/anvil/src/eth/backend/validate.rs +++ b/anvil/src/eth/backend/validate.rs @@ -2,7 +2,7 @@ use crate::eth::error::{BlockchainError, InvalidTransactionError}; use anvil_core::eth::transaction::PendingTransaction; -use foundry_evm::revm::{AccountInfo, Env}; +use foundry_evm::revm::primitives::{AccountInfo, Env}; /// A trait for validating transactions #[async_trait::async_trait] diff --git a/anvil/src/eth/error.rs b/anvil/src/eth/error.rs index 8ee91373bb430..4e84f952a4607 100644 --- a/anvil/src/eth/error.rs +++ b/anvil/src/eth/error.rs @@ -12,7 +12,7 @@ use ethers::{ types::{Bytes, SignatureError, U256}, }; use foundry_common::SELECTOR_LEN; -use foundry_evm::{executor::backend::DatabaseError, revm::Return}; +use foundry_evm::{executor::backend::DatabaseError, revm::interpreter::InstructionResult}; use serde::Serialize; use tracing::error; @@ -51,7 +51,7 @@ pub enum BlockchainError { #[error(transparent)] ForkProvider(#[from] ProviderError), #[error("EVM error {0:?}")] - EvmError(Return), + EvmError(InstructionResult), #[error("Invalid url {0:?}")] InvalidUrl(String), #[error("Internal error: {0:?}")] diff --git a/anvil/src/eth/fees.rs b/anvil/src/eth/fees.rs index 314461f493473..044a8fc9deb80 100644 --- a/anvil/src/eth/fees.rs +++ b/anvil/src/eth/fees.rs @@ -4,7 +4,7 @@ use crate::eth::{ }; use anvil_core::eth::transaction::TypedTransaction; use ethers::types::{H256, U256}; -use foundry_evm::revm::SpecId; +use foundry_evm::revm::primitives::SpecId; use futures::StreamExt; use parking_lot::{Mutex, RwLock}; use std::{ diff --git a/anvil/src/eth/util.rs b/anvil/src/eth/util.rs index f27d8e6bd3222..0479049c885d6 100644 --- a/anvil/src/eth/util.rs +++ b/anvil/src/eth/util.rs @@ -1,10 +1,14 @@ -use ethers::abi::Address; -use forge::revm::SpecId; -use foundry_evm::revm::precompiles::Precompiles; +use ethers::{abi::Address, types::H160}; +use forge::revm::primitives::SpecId; +use foundry_evm::revm::precompile::Precompiles; use std::fmt; pub fn get_precompiles_for(spec_id: SpecId) -> Vec
{ - Precompiles::new(spec_id.to_precompile_id()).addresses().into_iter().copied().collect() + Precompiles::new(to_precompile_id(spec_id)) + .addresses() + .into_iter() + .map(|item| H160::from_slice(item)) + .collect() } /// wrapper type that displays byte as hex @@ -54,3 +58,26 @@ impl<'a> fmt::Debug for HexDisplay<'a> { Ok(()) } } + +pub fn to_precompile_id(spec_id: SpecId) -> forge::revm::precompile::SpecId { + match spec_id { + SpecId::FRONTIER | + SpecId::FRONTIER_THAWING | + SpecId::HOMESTEAD | + SpecId::DAO_FORK | + SpecId::TANGERINE | + SpecId::SPURIOUS_DRAGON => forge::revm::precompile::SpecId::HOMESTEAD, + SpecId::BYZANTIUM | SpecId::CONSTANTINOPLE | SpecId::PETERSBURG => { + forge::revm::precompile::SpecId::BYZANTIUM + } + SpecId::ISTANBUL | SpecId::MUIR_GLACIER => forge::revm::precompile::SpecId::ISTANBUL, + SpecId::BERLIN | + SpecId::LONDON | + SpecId::ARROW_GLACIER | + SpecId::GRAY_GLACIER | + SpecId::MERGE | + SpecId::SHANGHAI | + SpecId::CANCUN | + SpecId::LATEST => forge::revm::precompile::SpecId::BERLIN, + } +} diff --git a/anvil/src/genesis.rs b/anvil/src/genesis.rs index aa58918bedd25..d455f9e35b5ac 100644 --- a/anvil/src/genesis.rs +++ b/anvil/src/genesis.rs @@ -1,10 +1,13 @@ //! Bindings for geth's `genesis.json` format -use crate::revm::AccountInfo; +use crate::revm::primitives::AccountInfo; use ethers::{ signers::LocalWallet, types::{serde_helpers::*, Address, Bytes, H256, U256}, }; -use forge::revm::{Bytecode, Env, KECCAK_EMPTY}; +use forge::{ + revm::primitives::{Bytecode, Env, KECCAK_EMPTY, U256 as rU256}, + utils::h160_to_b160, +}; use foundry_common::errors::FsPathError; use serde::{Deserialize, Serialize}; use std::{ @@ -83,22 +86,22 @@ impl Genesis { /// Applies all settings to the given `env` pub fn apply(&self, env: &mut Env) { if let Some(chain_id) = self.chain_id() { - env.cfg.chain_id = chain_id.into(); + env.cfg.chain_id = rU256::from(chain_id); } if let Some(timestamp) = self.timestamp { - env.block.timestamp = timestamp.into(); + env.block.timestamp = rU256::from(timestamp); } if let Some(base_fee) = self.base_fee_per_gas { - env.block.basefee = base_fee; + env.block.basefee = base_fee.into(); } if let Some(number) = self.number { - env.block.number = number.into(); + env.block.number = rU256::from(number); } if let Some(coinbase) = self.coinbase { - env.block.coinbase = coinbase; + env.block.coinbase = h160_to_b160(coinbase); } - env.block.difficulty = self.difficulty.into(); - env.block.gas_limit = self.gas_limit.into(); + env.block.difficulty = rU256::from(self.difficulty); + env.block.gas_limit = rU256::from(self.gas_limit); } /// Returns all private keys from the genesis accounts, if they exist @@ -141,7 +144,7 @@ impl From for AccountInfo { let GenesisAccount { code, balance, nonce, .. } = acc; let code = code.map(|code| Bytecode::new_raw(code.to_vec().into())); AccountInfo { - balance, + balance: balance.into(), nonce: nonce.unwrap_or_default(), code_hash: code.as_ref().map(|code| code.hash()).unwrap_or(KECCAK_EMPTY), code, diff --git a/anvil/src/hardfork.rs b/anvil/src/hardfork.rs index 0b52245256cbc..1192be0f73c77 100644 --- a/anvil/src/hardfork.rs +++ b/anvil/src/hardfork.rs @@ -1,6 +1,6 @@ use ethereum_forkid::{ForkHash, ForkId}; use ethers::types::BlockNumber; -use foundry_evm::revm::SpecId; +use foundry_evm::revm::primitives::SpecId; use std::str::FromStr; #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] diff --git a/anvil/tests/it/anvil_api.rs b/anvil/tests/it/anvil_api.rs index e6c944de97c71..8c7ef9745955c 100644 --- a/anvil/tests/it/anvil_api.rs +++ b/anvil/tests/it/anvil_api.rs @@ -14,7 +14,7 @@ use ethers::{ }, utils::hex, }; -use forge::revm::SpecId; +use forge::revm::primitives::SpecId; use std::{ str::FromStr, sync::Arc, diff --git a/anvil/tests/it/fork.rs b/anvil/tests/it/fork.rs index 8d468e476189a..7e5d97416d88b 100644 --- a/anvil/tests/it/fork.rs +++ b/anvil/tests/it/fork.rs @@ -13,6 +13,7 @@ use ethers::{ U256, }, }; +use forge::utils::h160_to_b160; use foundry_common::get_http_provider; use foundry_config::Config; use foundry_utils::{rpc, rpc::next_http_rpc_endpoint}; @@ -277,9 +278,9 @@ async fn test_separate_states() { let fork = api.get_fork().unwrap(); let fork_db = fork.database.read().await; - let acc = fork_db.inner().db().accounts.read().get(&addr).cloned().unwrap(); + let acc = fork_db.inner().db().accounts.read().get(&h160_to_b160(addr)).cloned().unwrap(); - assert_eq!(acc.balance, remote_balance) + assert_eq!(acc.balance, remote_balance.into()) } #[tokio::test(flavor = "multi_thread")] diff --git a/anvil/tests/it/proof/mod.rs b/anvil/tests/it/proof/mod.rs index 1ae01f785aca3..c82de8a2f5d8f 100644 --- a/anvil/tests/it/proof/mod.rs +++ b/anvil/tests/it/proof/mod.rs @@ -11,7 +11,7 @@ use anvil_core::eth::proof::{AccountProof, BasicAccount}; use crate::proof::eip1186::verify_proof; use anvil_core::eth::trie::ExtensionLayout; use ethers::utils::{keccak256, rlp}; -use foundry_evm::revm::KECCAK_EMPTY; +use foundry_evm::revm::primitives::KECCAK_EMPTY; mod eip1186; @@ -32,7 +32,7 @@ async fn can_get_proof() { nonce: 0.into(), balance: 0.into(), storage_root: proof.storage_hash, - code_hash: KECCAK_EMPTY, + code_hash: KECCAK_EMPTY.into(), }; let rlp_account = rlp::encode(&account); diff --git a/chisel/Cargo.toml b/chisel/Cargo.toml index 30769de4a0339..24c42941870ad 100644 --- a/chisel/Cargo.toml +++ b/chisel/Cargo.toml @@ -44,7 +44,7 @@ serde = "1.0.159" serde_json = { version = "1.0.95", features = ["raw_value"] } semver = "1.0.17" bytes = "1.4" -revm = "2.3" +revm = "3.1.1" eyre = "0.6" dirs = "5.0" time = { version = "0.3.20", features = ["formatting"] } diff --git a/chisel/src/executor.rs b/chisel/src/executor.rs index e1a0b9f2ca058..78fc0a91e0753 100644 --- a/chisel/src/executor.rs +++ b/chisel/src/executor.rs @@ -16,6 +16,7 @@ use eyre::{Result, WrapErr}; use forge::{ decode::decode_console_logs, executor::{inspector::CheatsConfig, Backend, ExecutorBuilder}, + utils::ru256_to_u256, }; use solang_parser::pt::{self, CodeLocation}; use yansi::Paint; @@ -185,7 +186,7 @@ impl SessionSource { // the file compiled correctly, thus the last stack item must be the memory offset of // the `bytes memory inspectoor` value - let mut offset = stack.data().last().unwrap().as_usize(); + let mut offset = ru256_to_u256(*stack.data().last().unwrap()).as_usize(); let mem = memory.data(); let len = U256::from(&mem[offset..offset + 32]).as_usize(); offset += 32; diff --git a/chisel/src/runner.rs b/chisel/src/runner.rs index ebf7e1ca9d624..1a8291a397372 100644 --- a/chisel/src/runner.rs +++ b/chisel/src/runner.rs @@ -12,7 +12,7 @@ use forge::{ executor::{DeployResult, Executor, RawCallResult}, trace::{CallTraceArena, TraceKind}, }; -use revm::{return_ok, Return}; +use revm::interpreter::{return_ok, InstructionResult}; use std::collections::BTreeMap; /// The function selector of the REPL contract's entrypoint, the `run()` function. @@ -52,7 +52,11 @@ pub struct ChiselResult { /// Called address pub address: Option
, /// EVM State at the final instruction of the `run()` function - pub state: Option<(revm::Stack, revm::Memory, revm::Return)>, + pub state: Option<( + revm::interpreter::Stack, + revm::interpreter::Memory, + revm::interpreter::InstructionResult, + )>, } /// ChiselRunner implementation @@ -155,10 +159,9 @@ impl ChiselRunner { self.executor.env_mut().tx.gas_limit = mid_gas_limit; let res = self.executor.call_raw(from, to, calldata.0.clone(), value)?; match res.exit_reason { - Return::Revert | - Return::OutOfGas | - Return::LackOfFundForGasLimit | - Return::OutOfFund => { + InstructionResult::Revert | + InstructionResult::OutOfGas | + InstructionResult::OutOfFund => { lowest_gas_limit = mid_gas_limit; } _ => { diff --git a/cli/src/cmd/cast/run.rs b/cli/src/cmd/cast/run.rs index 40763a851a41e..9a62dd28e3ee3 100644 --- a/cli/src/cmd/cast/run.rs +++ b/cli/src/cmd/cast/run.rs @@ -12,7 +12,9 @@ use forge::{ inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts, Backend, DeployResult, ExecutorBuilder, RawCallResult, }, + revm::primitives::U256 as rU256, trace::{identifier::EtherscanIdentifier, CallTraceDecoderBuilder, TraceKind}, + utils::h256_to_b256, }; use foundry_config::{find_project_root_path, Config}; use std::{collections::BTreeMap, str::FromStr}; @@ -93,16 +95,16 @@ impl RunArgs { let mut executor = builder.build(db); let mut env = executor.env().clone(); - env.block.number = tx_block_number.into(); + env.block.number = rU256::from(tx_block_number); let block = provider.get_block_with_txs(tx_block_number).await?; if let Some(ref block) = block { - env.block.timestamp = block.timestamp; - env.block.coinbase = block.author.unwrap_or_default(); - env.block.difficulty = block.difficulty; - env.block.prevrandao = block.mix_hash; - env.block.basefee = block.base_fee_per_gas.unwrap_or_default(); - env.block.gas_limit = block.gas_limit; + env.block.timestamp = block.timestamp.into(); + env.block.coinbase = block.author.unwrap_or_default().into(); + env.block.difficulty = block.difficulty.into(); + env.block.prevrandao = block.mix_hash.map(h256_to_b256); + env.block.basefee = block.base_fee_per_gas.unwrap_or_default().into(); + env.block.gas_limit = block.gas_limit.into(); } // Set the state to the moment right before the transaction diff --git a/cli/src/cmd/forge/coverage.rs b/cli/src/cmd/forge/coverage.rs index fba5d46fc96e0..44f908c774fc5 100644 --- a/cli/src/cmd/forge/coverage.rs +++ b/cli/src/cmd/forge/coverage.rs @@ -23,7 +23,7 @@ use forge::{ }, executor::{inspector::CheatsConfig, opts::EvmOpts}, result::SuiteResult, - revm::SpecId, + revm::primitives::SpecId, utils::{build_ic_pc_map, ICPCMap}, MultiContractRunnerBuilder, TestOptions, }; diff --git a/cli/src/cmd/forge/script/executor.rs b/cli/src/cmd/forge/script/executor.rs index 8bb1ccb41b134..b29b49b4ff284 100644 --- a/cli/src/cmd/forge/script/executor.rs +++ b/cli/src/cmd/forge/script/executor.rs @@ -18,6 +18,7 @@ use ethers::{ }; use forge::{ executor::{inspector::CheatsConfig, Backend, ExecutorBuilder}, + revm::primitives::U256 as rU256, trace::{CallTraceDecoder, Traces}, CallKind, }; @@ -179,7 +180,7 @@ impl ScriptArgs { // Simulate mining the transaction if the user passes `--slow`. if self.slow { - runner.executor.env_mut().block.number += U256::one(); + runner.executor.env_mut().block.number += rU256::from(1); } let is_fixed_gas_limit = tx.gas.is_some(); diff --git a/cli/src/cmd/forge/script/runner.rs b/cli/src/cmd/forge/script/runner.rs index 024871f574220..c49e9ec71440f 100644 --- a/cli/src/cmd/forge/script/runner.rs +++ b/cli/src/cmd/forge/script/runner.rs @@ -3,7 +3,7 @@ use cast::executor::ExecutionErr; use ethers::types::{Address, Bytes, NameOrAddress, U256}; use forge::{ executor::{CallResult, DeployResult, EvmError, Executor, RawCallResult}, - revm::{return_ok, Return}, + revm::interpreter::{return_ok, InstructionResult}, trace::{TraceKind, Traces}, CALLER, }; @@ -339,10 +339,9 @@ impl ScriptRunner { self.executor.env_mut().tx.gas_limit = mid_gas_limit; let res = self.executor.call_raw(from, to, calldata.0.clone(), value)?; match res.exit_reason { - Return::Revert | - Return::OutOfGas | - Return::LackOfFundForGasLimit | - Return::OutOfFund => { + InstructionResult::Revert | + InstructionResult::OutOfGas | + InstructionResult::OutOfFund => { lowest_gas_limit = mid_gas_limit; } _ => { diff --git a/cli/src/utils.rs b/cli/src/utils.rs index c8654a57e3982..d8f4dc2ff1ebd 100644 --- a/cli/src/utils.rs +++ b/cli/src/utils.rs @@ -8,7 +8,7 @@ use ethers::{ utils::format_units, }; use eyre::Result; -use forge::executor::SpecId; +use forge::revm::primitives::SpecId; use foundry_config::{Chain, Config}; use std::{ future::Future, diff --git a/cli/tests/fixtures/can_create_template_contract-2nd.stdout b/cli/tests/fixtures/can_create_template_contract-2nd.stdout index d07b9ebc613ea..dfef3326dbd9b 100644 --- a/cli/tests/fixtures/can_create_template_contract-2nd.stdout +++ b/cli/tests/fixtures/can_create_template_contract-2nd.stdout @@ -1,4 +1,4 @@ No files changed, compilation skipped Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -Transaction hash: 0x1a619fbe6f0d8eebc933a06019d8ad629d7bb4d9e8d893f18354dfed4990f723 +Transaction hash: 0xe4fffb8145d57d12e70bcb9586e2c714592dfd66811a87af92e15638fa027322 diff --git a/cli/tests/fixtures/can_create_template_contract.stdout b/cli/tests/fixtures/can_create_template_contract.stdout index 2942a13d92fc5..5013abaffd4be 100644 --- a/cli/tests/fixtures/can_create_template_contract.stdout +++ b/cli/tests/fixtures/can_create_template_contract.stdout @@ -3,4 +3,4 @@ Solc 0.8.15 finished in 2.27s Compiler run successful Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 -Transaction hash: 0x25ca21420b59327d31db412f13568621b1a8ddaa3a0c2d84ed83597cb661d55b +Transaction hash: 0xd6e14926926a3bf1f51ccd13ccb190887a82d0ba7d5f3f0af7928faa22c4d3ec diff --git a/cli/tests/fixtures/can_create_using_unlocked-2nd.stdout b/cli/tests/fixtures/can_create_using_unlocked-2nd.stdout index d07b9ebc613ea..dfef3326dbd9b 100644 --- a/cli/tests/fixtures/can_create_using_unlocked-2nd.stdout +++ b/cli/tests/fixtures/can_create_using_unlocked-2nd.stdout @@ -1,4 +1,4 @@ No files changed, compilation skipped Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -Transaction hash: 0x1a619fbe6f0d8eebc933a06019d8ad629d7bb4d9e8d893f18354dfed4990f723 +Transaction hash: 0xe4fffb8145d57d12e70bcb9586e2c714592dfd66811a87af92e15638fa027322 diff --git a/cli/tests/fixtures/can_create_using_unlocked.stdout b/cli/tests/fixtures/can_create_using_unlocked.stdout index 17a15ac67ba6c..80cb0492d2f3e 100644 --- a/cli/tests/fixtures/can_create_using_unlocked.stdout +++ b/cli/tests/fixtures/can_create_using_unlocked.stdout @@ -3,4 +3,4 @@ Solc 0.8.15 finished in 1.95s Compiler run successful Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 -Transaction hash: 0x25ca21420b59327d31db412f13568621b1a8ddaa3a0c2d84ed83597cb661d55b +Transaction hash: 0xd6e14926926a3bf1f51ccd13ccb190887a82d0ba7d5f3f0af7928faa22c4d3ec diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 1087f817aff1f..ace803dfe158b 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -35,10 +35,9 @@ once_cell = "1.17" # EVM bytes = "1.4.0" hashbrown = { version = "0.13", features = ["serde"] } -revm = { version = "2.3", default-features = false, features = [ +revm = { version = "3.1.1", default-features = false, features = [ "std", - "k256", - "with-serde", + "serde", "memory_limit", "optional_eip3607", "optional_block_gas_limit", diff --git a/evm/src/coverage/anchors.rs b/evm/src/coverage/anchors.rs index f598ada57da69..69cc347f026d7 100644 --- a/evm/src/coverage/anchors.rs +++ b/evm/src/coverage/anchors.rs @@ -4,7 +4,10 @@ use ethers::prelude::{ sourcemap::{SourceElement, SourceMap}, Bytes, }; -use revm::{opcode, spec_opcode_gas, SpecId}; +use revm::{ + interpreter::opcode::{self, spec_opcode_gas}, + primitives::SpecId, +}; /// Attempts to find anchors for the given items using the given source map and bytecode. pub fn find_anchors( diff --git a/evm/src/debug.rs b/evm/src/debug.rs index 8580f4fb437b1..885830abb4da1 100644 --- a/evm/src/debug.rs +++ b/evm/src/debug.rs @@ -1,6 +1,6 @@ use crate::{abi::HEVM_ABI, CallKind}; use ethers::types::{Address, U256}; -use revm::{Memory, OpCode}; +use revm::interpreter::{Memory, OpCode}; use serde::{Deserialize, Serialize}; use std::fmt::Display; @@ -133,7 +133,7 @@ impl Default for DebugStep { Self { stack: vec![], memory: Memory::new(), - instruction: Instruction::OpCode(revm::opcode::INVALID), + instruction: Instruction::OpCode(revm::interpreter::opcode::INVALID), push_bytes: None, pc: 0, total_gas_used: 0, diff --git a/evm/src/decode.rs b/evm/src/decode.rs index d1001734f934e..f2476fae9f477 100644 --- a/evm/src/decode.rs +++ b/evm/src/decode.rs @@ -12,7 +12,7 @@ use ethers::{ use foundry_common::{abi::format_token, SELECTOR_LEN}; use itertools::Itertools; use once_cell::sync::Lazy; -use revm::Return; +use revm::interpreter::{return_ok, InstructionResult}; /// Decode a set of logs, only returning logs from DSTest logging events and Hardhat's `console.log` pub fn decode_console_logs(logs: &[Log]) -> Vec { @@ -70,11 +70,11 @@ pub fn decode_console_log(log: &Log) -> Option { pub fn decode_revert( err: &[u8], maybe_abi: Option<&Abi>, - status: Option, + status: Option, ) -> eyre::Result { if err.len() < SELECTOR_LEN { if let Some(status) = status { - if !matches!(status, revm::return_ok!()) { + if !matches!(status, return_ok!()) { return Ok(format!("EvmError: {status:?}")) } } diff --git a/evm/src/executor/backend/fuzz.rs b/evm/src/executor/backend/fuzz.rs index 03c3808edbc2c..26d9269729f89 100644 --- a/evm/src/executor/backend/fuzz.rs +++ b/evm/src/executor/backend/fuzz.rs @@ -9,10 +9,11 @@ use crate::{ Address, }; use ethers::prelude::{H256, U256}; -use hashbrown::HashMap as Map; + use revm::{ - db::DatabaseRef, Account, AccountInfo, Bytecode, Database, Env, ExecutionResult, Inspector, - JournaledState, + db::DatabaseRef, + primitives::{AccountInfo, Bytecode, Env, ResultAndState, B160, B256, U256 as rU256}, + Database, Inspector, JournaledState, }; use std::borrow::Cow; use tracing::trace; @@ -55,14 +56,17 @@ impl<'a> FuzzBackendWrapper<'a> { &mut self, env: &mut Env, mut inspector: INSP, - ) -> (ExecutionResult, Map) + ) -> eyre::Result where INSP: Inspector, { // this is a new call to inspect with a new env, so even if we've cloned the backend // already, we reset the initialized state self.is_initialized = false; - revm::evm_inner::(env, self, &mut inspector).transact() + match revm::evm_inner::(env, self, &mut inspector).transact() { + Ok(result) => Ok(result), + Err(e) => eyre::bail!("fuzz: failed to inspect: {:?}", e), + } } /// Returns a mutable instance of the Backend. @@ -205,19 +209,19 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { impl<'a> DatabaseRef for FuzzBackendWrapper<'a> { type Error = DatabaseError; - fn basic(&self, address: Address) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { DatabaseRef::basic(self.backend.as_ref(), address) } - fn code_by_hash(&self, code_hash: H256) -> Result { + fn code_by_hash(&self, code_hash: B256) -> Result { DatabaseRef::code_by_hash(self.backend.as_ref(), code_hash) } - fn storage(&self, address: Address, index: U256) -> Result { + fn storage(&self, address: B160, index: rU256) -> Result { DatabaseRef::storage(self.backend.as_ref(), address, index) } - fn block_hash(&self, number: U256) -> Result { + fn block_hash(&self, number: rU256) -> Result { DatabaseRef::block_hash(self.backend.as_ref(), number) } } @@ -225,19 +229,19 @@ impl<'a> DatabaseRef for FuzzBackendWrapper<'a> { impl<'a> Database for FuzzBackendWrapper<'a> { type Error = DatabaseError; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: B160) -> Result, Self::Error> { DatabaseRef::basic(self, address) } - fn code_by_hash(&mut self, code_hash: H256) -> Result { + fn code_by_hash(&mut self, code_hash: B256) -> Result { DatabaseRef::code_by_hash(self, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: B160, index: rU256) -> Result { DatabaseRef::storage(self, address, index) } - fn block_hash(&mut self, number: U256) -> Result { + fn block_hash(&mut self, number: rU256) -> Result { DatabaseRef::block_hash(self, number) } } diff --git a/evm/src/executor/backend/in_memory_db.rs b/evm/src/executor/backend/in_memory_db.rs index 7baa861b4cc02..4b3dcaf1a868b 100644 --- a/evm/src/executor/backend/in_memory_db.rs +++ b/evm/src/executor/backend/in_memory_db.rs @@ -1,13 +1,10 @@ //! The in memory DB use crate::executor::backend::error::DatabaseError; -use ethers::{ - prelude::{H256, U256}, - types::Address, -}; use hashbrown::HashMap as Map; use revm::{ db::{CacheDB, DatabaseRef, EmptyDB}, - Account, AccountInfo, Bytecode, Database, DatabaseCommit, + primitives::{Account, AccountInfo, Bytecode, B160, B256, U256}, + Database, DatabaseCommit, }; use crate::executor::snapshot::Snapshots; @@ -34,19 +31,19 @@ impl Default for MemDb { impl DatabaseRef for MemDb { type Error = DatabaseError; - fn basic(&self, address: Address) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { DatabaseRef::basic(&self.inner, address) } - fn code_by_hash(&self, code_hash: H256) -> Result { + fn code_by_hash(&self, code_hash: B256) -> Result { DatabaseRef::code_by_hash(&self.inner, code_hash) } - fn storage(&self, address: Address, index: U256) -> Result { + fn storage(&self, address: B160, index: U256) -> Result { DatabaseRef::storage(&self.inner, address, index) } - fn block_hash(&self, number: U256) -> Result { + fn block_hash(&self, number: U256) -> Result { DatabaseRef::block_hash(&self.inner, number) } } @@ -54,26 +51,26 @@ impl DatabaseRef for MemDb { impl Database for MemDb { type Error = DatabaseError; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: B160) -> Result, Self::Error> { // Note: this will always return `Some(AccountInfo)`, See `EmptyDBWrapper` Database::basic(&mut self.inner, address) } - fn code_by_hash(&mut self, code_hash: H256) -> Result { + fn code_by_hash(&mut self, code_hash: B256) -> Result { Database::code_by_hash(&mut self.inner, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: B160, index: U256) -> Result { Database::storage(&mut self.inner, address, index) } - fn block_hash(&mut self, number: U256) -> Result { + fn block_hash(&mut self, number: U256) -> Result { Database::block_hash(&mut self.inner, number) } } impl DatabaseCommit for MemDb { - fn commit(&mut self, changes: Map) { + fn commit(&mut self, changes: Map) { DatabaseCommit::commit(&mut self.inner, changes) } } @@ -96,18 +93,18 @@ pub struct EmptyDBWrapper(EmptyDB); impl DatabaseRef for EmptyDBWrapper { type Error = DatabaseError; - fn basic(&self, address: Address) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { // Note: this will always return `Some(AccountInfo)`, for the reason explained above Ok(Some(self.0.basic(address)?.unwrap_or_default())) } - fn code_by_hash(&self, code_hash: H256) -> Result { + fn code_by_hash(&self, code_hash: B256) -> Result { Ok(self.0.code_by_hash(code_hash)?) } - fn storage(&self, address: Address, index: U256) -> Result { + fn storage(&self, address: B160, index: U256) -> Result { Ok(self.0.storage(address, index)?) } - fn block_hash(&self, number: U256) -> Result { + fn block_hash(&self, number: U256) -> Result { Ok(self.0.block_hash(number)?) } } @@ -122,13 +119,12 @@ mod tests { #[test] fn cache_db_insert_basic_non_existing() { let mut db = CacheDB::new(EmptyDB::default()); - let address = Address::random(); - + let address = B160::random(); // call `basic` on a non-existing account let info = Database::basic(&mut db, address).unwrap(); assert!(info.is_none()); let mut info = info.unwrap_or_default(); - info.balance = 500u64.into(); + info.balance = U256::from(500u64); // insert the modified account info db.insert_account_info(address, info); @@ -142,12 +138,12 @@ mod tests { #[test] fn cache_db_insert_basic_default() { let mut db = CacheDB::new(EmptyDB::default()); - let address = Address::random(); + let address = B160::random(); let info = DatabaseRef::basic(&db, address).unwrap(); assert!(info.is_none()); let mut info = info.unwrap_or_default(); - info.balance = 500u64.into(); + info.balance = U256::from(500u64); // insert the modified account info db.insert_account_info(address, info.clone()); @@ -161,12 +157,12 @@ mod tests { #[test] fn mem_db_insert_basic_default() { let mut db = MemDb::default(); - let address = Address::random(); + let address = B160::random(); let info = Database::basic(&mut db, address).unwrap(); assert!(info.is_some()); let mut info = info.unwrap(); - info.balance = 500u64.into(); + info.balance = U256::from(500u64); // insert the modified account info db.inner.insert_account_info(address, info.clone()); diff --git a/evm/src/executor/backend/mod.rs b/evm/src/executor/backend/mod.rs index 4cc04ee45a194..c81505e6e2199 100644 --- a/evm/src/executor/backend/mod.rs +++ b/evm/src/executor/backend/mod.rs @@ -6,6 +6,7 @@ use crate::{ inspector::{cheatcodes::Cheatcodes, DEFAULT_CREATE2_DEPLOYER}, snapshot::Snapshots, }, + utils::{b160_to_h160, h160_to_b160, h256_to_b256, ru256_to_u256, u256_to_ru256}, CALLER, TEST_CONTRACT_ADDRESS, }; use ethers::{ @@ -17,9 +18,12 @@ use hashbrown::HashMap as Map; pub use in_memory_db::MemDb; use revm::{ db::{CacheDB, DatabaseRef}, - precompiles::Precompiles, - Account, AccountInfo, Bytecode, CreateScheme, Database, DatabaseCommit, Env, ExecutionResult, - Inspector, JournaledState, Log, SpecId, TransactTo, EVM, KECCAK_EMPTY, + precompile::{Precompiles, SpecId}, + primitives::{ + Account, AccountInfo, Bytecode, CreateScheme, Env, Log, ResultAndState, TransactTo, B160, + B256, KECCAK_EMPTY, U256 as rU256, + }, + Database, DatabaseCommit, Inspector, JournaledState, EVM, }; use std::collections::{HashMap, HashSet}; use tracing::{trace, warn}; @@ -448,9 +452,9 @@ impl Backend { pub fn insert_account_info(&mut self, address: H160, account: AccountInfo) { if let Some(db) = self.active_fork_db_mut() { - db.insert_account_info(address, account) + db.insert_account_info(h160_to_b160(address), account) } else { - self.mem_db.insert_account_info(address, account) + self.mem_db.insert_account_info(h160_to_b160(address), account) } } @@ -480,7 +484,7 @@ impl Backend { } /// Sets the caller address - pub fn set_caller(&mut self, acc: Address) -> &mut Self { + pub fn set_caller(&mut self, acc: H160) -> &mut Self { trace!(?acc, "setting caller account"); self.inner.caller = Some(acc); self.allow_cheatcode_access(acc); @@ -490,7 +494,7 @@ impl Backend { /// Sets the current spec id pub fn set_spec_id(&mut self, spec_id: SpecId) -> &mut Self { trace!("setting precompile id"); - self.inner.precompile_id = spec_id.to_precompile_id(); + self.inner.precompile_id = spec_id; self } @@ -535,8 +539,8 @@ impl Backend { bool private _failed; } */ - let value = self.storage(address, U256::zero()).unwrap_or_default(); - value.byte(1) != 0 + let value = self.storage(h160_to_b160(address), U256::zero().into()).unwrap_or_default(); + value.as_le_bytes()[1] != 0 } /// Checks if the given test function failed by looking at the present value of the test @@ -550,10 +554,15 @@ impl Backend { address: Address, current_state: &JournaledState, ) -> bool { + let address = h160_to_b160(address); if let Some(account) = current_state.state.get(&address) { - let value = - account.storage.get(&U256::zero()).cloned().unwrap_or_default().present_value(); - return value.byte(1) != 0 + let value = account + .storage + .get(&revm::primitives::U256::ZERO) + .cloned() + .unwrap_or_default() + .present_value(); + return value.as_le_bytes()[1] != 0 } false @@ -564,7 +573,9 @@ impl Backend { /// See pub fn is_global_failure(&self) -> bool { let index = U256::from(&b"failed"[..]); - self.storage(CHEATCODE_ADDRESS, index).map(|value| value == U256::one()).unwrap_or_default() + self.storage(h160_to_b160(CHEATCODE_ADDRESS), index.into()) + .map(|value| value == revm::primitives::U256::from(1)) + .unwrap_or_default() } /// When creating or switching forks, we update the AccountInfo of the contract @@ -673,20 +684,20 @@ impl Backend { /// /// We need to track these mainly to prevent issues when switching between different evms pub(crate) fn initialize(&mut self, env: &Env) { - self.set_caller(env.tx.caller); - self.set_spec_id(env.cfg.spec_id); + self.set_caller(b160_to_h160(env.tx.caller)); + self.set_spec_id(SpecId::from_spec_id(env.cfg.spec_id)); let test_contract = match env.tx.transact_to { TransactTo::Call(to) => to, TransactTo::Create(CreateScheme::Create) => { - revm::create_address(env.tx.caller, env.tx.nonce.unwrap_or_default()) + revm::primitives::create_address(env.tx.caller, env.tx.nonce.unwrap_or_default()) } TransactTo::Create(CreateScheme::Create2 { salt }) => { let code_hash = H256::from_slice(keccak256(&env.tx.data).as_slice()); - revm::create2_address(env.tx.caller, code_hash, salt) + revm::primitives::create2_address(env.tx.caller, h256_to_b256(code_hash), salt) } }; - self.set_test_contract(test_contract); + self.set_test_contract(b160_to_h160(test_contract)); } /// Executes the configured test call of the `env` without committing state changes @@ -694,17 +705,20 @@ impl Backend { &mut self, env: &mut Env, mut inspector: INSP, - ) -> (ExecutionResult, Map) + ) -> eyre::Result where INSP: Inspector, { self.initialize(env); - revm::evm_inner::(env, self, &mut inspector).transact() + match revm::evm_inner::(env, self, &mut inspector).transact() { + Ok(res) => Ok(res), + Err(e) => eyre::bail!("backend: failed while inspecting: {:?}", e), + } } /// Returns true if the address is a precompile - pub fn is_existing_precompile(&self, addr: &Address) -> bool { + pub fn is_existing_precompile(&self, addr: &B160) -> bool { self.inner.precompiles().contains(addr) } @@ -722,7 +736,9 @@ impl Backend { .fork_init_journaled_state .state .iter() - .filter(|(addr, _)| !self.is_existing_precompile(addr) && !self.is_persistent(addr)) + .filter(|(addr, _)| { + !self.is_existing_precompile(addr) && !self.is_persistent(&b160_to_h160(**addr)) + }) .map(|(addr, _)| addr) .copied() .collect::>(); @@ -732,7 +748,7 @@ impl Backend { for loaded_account in loaded_accounts.iter().copied() { trace!(?loaded_account, "replacing account on init"); let fork_account = Database::basic(&mut fork.db, loaded_account)? - .ok_or(DatabaseError::MissingAccount(loaded_account))?; + .ok_or(DatabaseError::MissingAccount(b160_to_h160(loaded_account)))?; let init_account = journaled_state.state.get_mut(&loaded_account).expect("exists; qed"); init_account.info = fork_account; @@ -785,8 +801,10 @@ impl Backend { let fork_id = self.ensure_fork_id(id)?.clone(); let fork = self.inner.get_fork_by_id_mut(id)?; - let full_block = - fork.db.db.get_full_block(BlockNumber::Number(env.block.number.as_u64().into()))?; + let full_block = fork + .db + .db + .get_full_block(BlockNumber::Number(ru256_to_u256(env.block.number).as_u64().into()))?; for tx in full_block.transactions.into_iter() { if tx.hash().eq(&tx_hash) { @@ -795,7 +813,7 @@ impl Backend { } trace!(tx=?tx.hash, "committing transaction"); - commit_transaction(tx, env.clone(), journaled_state, fork, &fork_id, None); + commit_transaction(tx, env.clone(), journaled_state, fork, &fork_id, None)?; } Ok(None) @@ -942,7 +960,7 @@ impl DatabaseExt for Backend { // Initialize caller with its fork info if let Some(mut acc) = caller_account { let fork_account = Database::basic(&mut target_fork.db, caller)? - .ok_or(DatabaseError::MissingAccount(caller))?; + .ok_or(DatabaseError::MissingAccount(b160_to_h160(caller)))?; acc.info = fork_account; target_fork.journaled_state.state.insert(caller, acc); @@ -1047,7 +1065,7 @@ impl DatabaseExt for Backend { for (addr, acc) in journaled_state.state.iter() { if acc.is_touched { merge_journaled_state_data( - *addr, + b160_to_h160(*addr), journaled_state, &mut active.journaled_state, ); @@ -1079,13 +1097,13 @@ impl DatabaseExt for Backend { self.roll_fork(Some(id), fork_block.as_u64().into(), env, journaled_state)?; // update the block's env accordingly - env.block.timestamp = block.timestamp; - env.block.coinbase = block.author.unwrap_or_default(); - env.block.difficulty = block.difficulty; - env.block.prevrandao = block.mix_hash; - env.block.basefee = block.base_fee_per_gas.unwrap_or_default(); - env.block.gas_limit = block.gas_limit; - env.block.number = block.number.unwrap_or(fork_block).as_u64().into(); + env.block.timestamp = block.timestamp.into(); + env.block.coinbase = h160_to_b160(block.author.unwrap_or_default()); + env.block.difficulty = block.difficulty.into(); + env.block.prevrandao = block.mix_hash.map(h256_to_b256); + env.block.basefee = block.base_fee_per_gas.unwrap_or_default().into(); + env.block.gas_limit = block.gas_limit.into(); + env.block.number = u256_to_ru256(block.number.unwrap_or(fork_block).as_u64().into()); // replay all transactions that came before let env = env.clone(); @@ -1118,7 +1136,7 @@ impl DatabaseExt for Backend { let fork = self.inner.get_fork_by_id_mut(id)?; let tx = fork.db.db.get_transaction(transaction)?; - commit_transaction(tx, env, journaled_state, fork, &fork_id, cheatcodes_inspector); + commit_transaction(tx, env, journaled_state, fork, &fork_id, cheatcodes_inspector)?; Ok(()) } @@ -1225,7 +1243,7 @@ impl DatabaseExt for Backend { impl DatabaseRef for Backend { type Error = DatabaseError; - fn basic(&self, address: Address) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { if let Some(db) = self.active_fork_db() { db.basic(address) } else { @@ -1233,7 +1251,7 @@ impl DatabaseRef for Backend { } } - fn code_by_hash(&self, code_hash: H256) -> Result { + fn code_by_hash(&self, code_hash: B256) -> Result { if let Some(db) = self.active_fork_db() { db.code_by_hash(code_hash) } else { @@ -1241,7 +1259,7 @@ impl DatabaseRef for Backend { } } - fn storage(&self, address: Address, index: U256) -> Result { + fn storage(&self, address: B160, index: rU256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { @@ -1249,7 +1267,7 @@ impl DatabaseRef for Backend { } } - fn block_hash(&self, number: U256) -> Result { + fn block_hash(&self, number: rU256) -> Result { if let Some(db) = self.active_fork_db() { db.block_hash(number) } else { @@ -1260,7 +1278,7 @@ impl DatabaseRef for Backend { impl<'a> DatabaseRef for &'a mut Backend { type Error = DatabaseError; - fn basic(&self, address: Address) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { if let Some(db) = self.active_fork_db() { DatabaseRef::basic(db, address) } else { @@ -1268,7 +1286,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn code_by_hash(&self, code_hash: H256) -> Result { + fn code_by_hash(&self, code_hash: B256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::code_by_hash(db, code_hash) } else { @@ -1276,7 +1294,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn storage(&self, address: Address, index: U256) -> Result { + fn storage(&self, address: B160, index: rU256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::storage(db, address, index) } else { @@ -1284,7 +1302,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } } - fn block_hash(&self, number: U256) -> Result { + fn block_hash(&self, number: rU256) -> Result { if let Some(db) = self.active_fork_db() { DatabaseRef::block_hash(db, number) } else { @@ -1294,7 +1312,7 @@ impl<'a> DatabaseRef for &'a mut Backend { } impl DatabaseCommit for Backend { - fn commit(&mut self, changes: Map) { + fn commit(&mut self, changes: Map) { if let Some(db) = self.active_fork_db_mut() { db.commit(changes) } else { @@ -1305,7 +1323,7 @@ impl DatabaseCommit for Backend { impl Database for Backend { type Error = DatabaseError; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: B160) -> Result, Self::Error> { if let Some(db) = self.active_fork_db_mut() { db.basic(address) } else { @@ -1313,7 +1331,7 @@ impl Database for Backend { } } - fn code_by_hash(&mut self, code_hash: H256) -> Result { + fn code_by_hash(&mut self, code_hash: B256) -> Result { if let Some(db) = self.active_fork_db_mut() { db.code_by_hash(code_hash) } else { @@ -1321,7 +1339,7 @@ impl Database for Backend { } } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: B160, index: rU256) -> Result { if let Some(db) = self.active_fork_db_mut() { Database::storage(db, address, index) } else { @@ -1329,7 +1347,7 @@ impl Database for Backend { } } - fn block_hash(&mut self, number: U256) -> Result { + fn block_hash(&mut self, number: rU256) -> Result { if let Some(db) = self.active_fork_db_mut() { db.block_hash(number) } else { @@ -1359,7 +1377,7 @@ pub struct Fork { impl Fork { /// Returns true if the account is a contract pub fn is_contract(&self, acc: Address) -> bool { - if let Ok(Some(acc)) = self.db.basic(acc) { + if let Ok(Some(acc)) = self.db.basic(h160_to_b160(acc)) { if acc.code_hash != KECCAK_EMPTY { return true } @@ -1422,7 +1440,7 @@ pub struct BackendInner { /// See also [`clone_data()`] pub persistent_accounts: HashSet
, /// The configured precompile spec id - pub precompile_id: revm::precompiles::SpecId, + pub precompile_id: revm::precompile::SpecId, /// All accounts that are allowed to execute cheatcodes pub cheatcode_access_accounts: HashSet
, } @@ -1607,7 +1625,7 @@ impl Default for BackendInner { caller: None, next_fork_id: Default::default(), persistent_accounts: Default::default(), - precompile_id: revm::precompiles::SpecId::LATEST, + precompile_id: revm::precompile::SpecId::LATEST, // grant the cheatcode,default test and caller address access to execute cheatcodes // itself cheatcode_access_accounts: HashSet::from([ @@ -1653,6 +1671,8 @@ fn merge_journaled_state_data( active_journaled_state: &JournaledState, fork_journaled_state: &mut JournaledState, ) { + let addr = h160_to_b160(addr); + if let Some(mut acc) = active_journaled_state.state.get(&addr).cloned() { trace!(?addr, "updating journaled_state account data"); if let Some(fork_account) = fork_journaled_state.state.get_mut(&addr) { @@ -1673,6 +1693,8 @@ fn merge_db_account_data( ) { trace!(?addr, "merging database data"); + let addr = h160_to_b160(addr); + let mut acc = if let Some(acc) = active.accounts.get(&addr).cloned() { acc } else { @@ -1696,6 +1718,8 @@ fn merge_db_account_data( /// Returns true of the address is a contract fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool { + let acc = h160_to_b160(acc); + journaled_state .state .get(&acc) @@ -1712,10 +1736,10 @@ fn commit_transaction( fork: &mut Fork, fork_id: &ForkId, cheatcodes_inspector: Option<&mut Cheatcodes>, -) { +) -> eyre::Result<()> { configure_tx_env(&mut env, &tx); - let (_, state) = { + let state = { let mut evm = EVM::new(); evm.env = env; @@ -1723,19 +1747,26 @@ fn commit_transaction( evm.database(db); if let Some(inspector) = cheatcodes_inspector { - evm.inspect(inspector) + match evm.inspect(inspector) { + Ok(res) => res.state, + Err(e) => eyre::bail!("backend: failed committing transaction: {:?}", e), + } } else { - evm.transact() + match evm.transact() { + Ok(res) => res.state, + Err(e) => eyre::bail!("backend: failed committing transaction: {:?}", e), + } } }; apply_state_changeset(state, journaled_state, fork); + Ok(()) } /// Applies the changeset of a transaction to the active journaled state and also commits it in the /// forked db fn apply_state_changeset( - state: hashbrown::HashMap, + state: hashbrown::HashMap, journaled_state: &mut JournaledState, fork: &mut Fork, ) { diff --git a/evm/src/executor/backend/snapshot.rs b/evm/src/executor/backend/snapshot.rs index f7e1d3ad6bba3..ce9db6e3edce4 100644 --- a/evm/src/executor/backend/snapshot.rs +++ b/evm/src/executor/backend/snapshot.rs @@ -1,14 +1,16 @@ -use ethers::types::{Address, H256, U256}; use hashbrown::HashMap as Map; -use revm::{AccountInfo, Env, JournaledState}; +use revm::{ + primitives::{AccountInfo, Env, B160, B256, U256}, + JournaledState, +}; use serde::{Deserialize, Serialize}; /// A minimal abstraction of a state at a certain point in time #[derive(Default, Debug, Serialize, Deserialize)] pub struct StateSnapshot { - pub accounts: Map, - pub storage: Map>, - pub block_hashes: Map, + pub accounts: Map, + pub storage: Map>, + pub block_hashes: Map, } /// Represents a snapshot taken during evm execution diff --git a/evm/src/executor/builder.rs b/evm/src/executor/builder.rs index 7baafa94391a8..5e021c7658c65 100644 --- a/evm/src/executor/builder.rs +++ b/evm/src/executor/builder.rs @@ -7,7 +7,7 @@ use crate::{ fuzz::{invariant::RandomCallGenerator, strategies::EvmFuzzState}, }; use ethers::types::U256; -use revm::{Env, SpecId}; +use revm::primitives::{Env, SpecId}; /// The builder that allows to configure an evm [`Executor`] which a stack of optional /// [`revm::Inspector`]s, such as [`Cheatcodes`] @@ -29,7 +29,7 @@ impl ExecutorBuilder { #[must_use] pub fn with_cheatcodes(mut self, config: CheatsConfig) -> Self { self.inspector_config.cheatcodes = - Some(Cheatcodes::new(self.env.block.clone(), self.env.tx.gas_price, config)); + Some(Cheatcodes::new(self.env.block.clone(), self.env.tx.gas_price.into(), config)); self } @@ -92,7 +92,7 @@ impl ExecutorBuilder { #[must_use] pub fn with_config(mut self, env: Env) -> Self { self.inspector_config.block = env.block.clone(); - self.inspector_config.gas_price = env.tx.gas_price; + self.inspector_config.gas_price = env.tx.gas_price.into(); self.env = env; self } @@ -106,7 +106,7 @@ impl ExecutorBuilder { /// Builds the executor as configured. pub fn build(self, db: Backend) -> Executor { - let gas_limit = self.gas_limit.unwrap_or(self.env.block.gas_limit); + let gas_limit = self.gas_limit.unwrap_or(self.env.block.gas_limit.into()); Executor::new(db, self.env, self.inspector_config, gas_limit) } } diff --git a/evm/src/executor/fork/backend.rs b/evm/src/executor/fork/backend.rs index 153dc9fad72d4..d882d1655f2a6 100644 --- a/evm/src/executor/fork/backend.rs +++ b/evm/src/executor/fork/backend.rs @@ -1,12 +1,15 @@ //! Smart caching and deduplication of requests when using a forking provider -use crate::executor::{ - backend::error::{DatabaseError, DatabaseResult}, - fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, +use crate::{ + executor::{ + backend::error::{DatabaseError, DatabaseResult}, + fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, + }, + utils::{b160_to_h160, b256_to_h256, h160_to_b160, h256_to_b256, ru256_to_u256, u256_to_ru256}, }; use ethers::{ core::abi::ethereum_types::BigEndianHash, providers::Middleware, - types::{Address, Block, BlockId, Bytes, Transaction, H160, H256, U256}, + types::{Address, Block, BlockId, Bytes, Transaction, H256, U256}, utils::keccak256, }; use futures::{ @@ -15,7 +18,10 @@ use futures::{ task::{Context, Poll}, Future, FutureExt, }; -use revm::{db::DatabaseRef, AccountInfo, Bytecode, KECCAK_EMPTY}; +use revm::{ + db::DatabaseRef, + primitives::{AccountInfo, Bytecode, B160, B256, KECCAK_EMPTY, U256 as rU256}, +}; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, pin::Pin, @@ -133,7 +139,7 @@ where match req { BackendRequest::Basic(addr, sender) => { trace!(target: "backendhandler", "received request basic address={:?}", addr); - let acc = self.db.accounts().read().get(&addr).cloned(); + let acc = self.db.accounts().read().get(&h160_to_b160(addr)).cloned(); if let Some(basic) = acc { let _ = sender.send(Ok(basic)); } else { @@ -141,9 +147,9 @@ where } } BackendRequest::BlockHash(number, sender) => { - let hash = self.db.block_hashes().read().get(&U256::from(number)).cloned(); + let hash = self.db.block_hashes().read().get(&rU256::from(number)).cloned(); if let Some(hash) = hash { - let _ = sender.send(Ok(hash)); + let _ = sender.send(Ok(hash.into())); } else { self.request_hash(number, sender); } @@ -156,10 +162,14 @@ where } BackendRequest::Storage(addr, idx, sender) => { // account is already stored in the cache - let value = - self.db.storage().read().get(&addr).and_then(|acc| acc.get(&idx).copied()); + let value = self + .db + .storage() + .read() + .get(&h160_to_b160(addr)) + .and_then(|acc| acc.get(&u256_to_ru256(idx)).copied()); if let Some(value) = value { - let _ = sender.send(Ok(value)); + let _ = sender.send(Ok(ru256_to_u256(value))); } else { // account present but not storage -> fetch storage self.request_account_storage(addr, idx, sender); @@ -265,7 +275,7 @@ where warn!(target: "backendhandler", ?number, "block not found"); // if no block was returned then the block does not exist, in which case // we return empty hash - Ok(KECCAK_EMPTY) + Ok(b256_to_h256(KECCAK_EMPTY)) } Err(err) => { error!(target: "backendhandler", ?err, ?number, "failed to get block"); @@ -341,11 +351,11 @@ where // update the cache let acc = AccountInfo { nonce: nonce.as_u64(), - balance, + balance: balance.into(), code: code.map(|bytes| Bytecode::new_raw(bytes).to_checked()), code_hash, }; - pin.db.accounts().write().insert(addr, acc.clone()); + pin.db.accounts().write().insert(addr.into(), acc.clone()); // notify all listeners if let Some(listeners) = pin.account_requests.remove(&addr) { @@ -379,7 +389,12 @@ where }; // update the cache - pin.db.storage().write().entry(addr).or_default().insert(idx, value); + pin.db + .storage() + .write() + .entry(addr.into()) + .or_default() + .insert(idx.into(), value.into()); // notify all listeners if let Some(listeners) = pin.storage_requests.remove(&(addr, idx)) { @@ -410,7 +425,7 @@ where }; // update the cache - pin.db.block_hashes().write().insert(number.into(), value); + pin.db.block_hashes().write().insert(rU256::from(number), value.into()); // notify all listeners if let Some(listeners) = pin.block_requests.remove(&number) { @@ -628,36 +643,43 @@ impl SharedBackend { impl DatabaseRef for SharedBackend { type Error = DatabaseError; - fn basic(&self, address: H160) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { trace!( target: "sharedbackend", "request basic {:?}", address); - self.do_get_basic(address).map_err(|err| { + self.do_get_basic(b160_to_h160(address)).map_err(|err| { error!(target: "sharedbackend", ?err, ?address, "Failed to send/recv `basic`"); err }) } - fn code_by_hash(&self, hash: H256) -> Result { - Err(DatabaseError::MissingCode(hash)) + fn code_by_hash(&self, hash: B256) -> Result { + Err(DatabaseError::MissingCode(b256_to_h256(hash))) } - fn storage(&self, address: H160, index: U256) -> Result { + fn storage(&self, address: B160, index: rU256) -> Result { trace!( target: "sharedbackend", "request storage {:?} at {:?}", address, index); - self.do_get_storage(address, index).map_err(|err| { + match self.do_get_storage(b160_to_h160(address), index.into()).map_err(|err| { error!( target: "sharedbackend", ?err, ?address, ?index, "Failed to send/recv `storage`"); err - }) + }) { + Ok(val) => Ok(val.into()), + Err(err) => Err(err), + } } - fn block_hash(&self, number: U256) -> Result { - if number > U256::from(u64::MAX) { + fn block_hash(&self, number: rU256) -> Result { + if number > rU256::from(u64::MAX) { return Ok(KECCAK_EMPTY) } + let number: U256 = number.into(); let number = number.as_u64(); trace!( target: "sharedbackend", "request block hash for number {:?}", number); - self.do_get_block_hash(number).map_err(|err| { + match self.do_get_block_hash(number).map_err(|err| { error!(target: "sharedbackend",?err, ?number, "Failed to send/recv `block_hash`"); err - }) + }) { + Ok(val) => Ok(h256_to_b256(val)), + Err(err) => Err(err), + } } } @@ -669,10 +691,7 @@ mod tests { opts::EvmOpts, Backend, }; - use ethers::{ - solc::utils::RuntimeOrHandle, - types::{Address, Chain}, - }; + use ethers::{solc::utils::RuntimeOrHandle, types::Chain}; use foundry_common::get_http_provider; use foundry_config::Config; use std::{collections::BTreeSet, path::PathBuf, sync::Arc}; @@ -693,9 +712,9 @@ mod tests { runtime.block_on(SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None)); // some rng contract from etherscan - let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); + let address: B160 = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); - let idx = U256::from(0u64); + let idx = rU256::from(0u64); let value = backend.storage(address, idx).unwrap(); let account = backend.basic(address).unwrap().unwrap(); @@ -704,17 +723,17 @@ mod tests { assert_eq!(account.nonce, mem_acc.nonce); let slots = db.storage().read().get(&address).unwrap().clone(); assert_eq!(slots.len(), 1); - assert_eq!(slots.get(&idx).copied().unwrap(), value); + assert_eq!(slots.get(&idx).copied().unwrap(), value.into()); - let num = U256::from(10u64); - let hash = backend.block_hash(num).unwrap(); + let num = rU256::from(10u64); + let hash = backend.block_hash(num.into()).unwrap(); let mem_hash = *db.block_hashes().read().get(&num).unwrap(); assert_eq!(hash, mem_hash); let max_slots = 5; let handle = std::thread::spawn(move || { for i in 1..max_slots { - let idx = U256::from(i); + let idx = rU256::from(i); let _ = backend.storage(address, idx); } }); @@ -752,16 +771,16 @@ mod tests { let backend = Backend::spawn(Some(fork)); // some rng contract from etherscan - let address: Address = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); + let address: B160 = "63091244180ae240c87d1f528f5f269134cb07b3".parse().unwrap(); - let idx = U256::from(0u64); + let idx = rU256::from(0u64); let _value = backend.storage(address, idx); let _account = backend.basic(address); // fill some slots let num_slots = 10u64; for idx in 1..num_slots { - let _ = backend.storage(address, idx.into()); + let _ = backend.storage(address, rU256::from(idx)); } drop(backend); diff --git a/evm/src/executor/fork/cache.rs b/evm/src/executor/fork/cache.rs index d6c8b23a2c9a6..7ce7f1037bd33 100644 --- a/evm/src/executor/fork/cache.rs +++ b/evm/src/executor/fork/cache.rs @@ -1,8 +1,10 @@ //! Cache related abstraction use crate::{executor::backend::snapshot::StateSnapshot, HashMap as Map}; -use ethers::types::{Address, H256, U256}; use parking_lot::RwLock; -use revm::{Account, AccountInfo, DatabaseCommit, KECCAK_EMPTY}; +use revm::{ + primitives::{Account, AccountInfo, B160, B256, KECCAK_EMPTY, U256}, + DatabaseCommit, +}; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; use std::{collections::BTreeSet, fs, io::BufWriter, path::PathBuf, sync::Arc}; use tracing::{trace, warn}; @@ -77,17 +79,17 @@ impl BlockchainDb { } /// Returns the map that holds the account related info - pub fn accounts(&self) -> &RwLock> { + pub fn accounts(&self) -> &RwLock> { &self.db.accounts } /// Returns the map that holds the storage related info - pub fn storage(&self) -> &RwLock> { + pub fn storage(&self) -> &RwLock> { &self.db.storage } /// Returns the map that holds all the block hashes - pub fn block_hashes(&self) -> &RwLock> { + pub fn block_hashes(&self) -> &RwLock> { &self.db.block_hashes } @@ -110,15 +112,15 @@ impl BlockchainDb { /// relevant identifying markers in the context of [BlockchainDb] #[derive(Debug, Clone, Eq, Serialize)] pub struct BlockchainDbMeta { - pub cfg_env: revm::CfgEnv, - pub block_env: revm::BlockEnv, + pub cfg_env: revm::primitives::CfgEnv, + pub block_env: revm::primitives::BlockEnv, /// all the hosts used to connect to pub hosts: BTreeSet, } impl BlockchainDbMeta { /// Creates a new instance - pub fn new(env: revm::Env, url: String) -> Self { + pub fn new(env: revm::primitives::Env, url: String) -> Self { let host = Url::parse(&url) .ok() .and_then(|url| url.host().map(|host| host.to_string())) @@ -151,7 +153,7 @@ impl<'de> Deserialize<'de> for BlockchainDbMeta { /// default [revm::CfgEnv], for example enabling an optional feature. /// By hand rolling deserialize impl we can prevent cache file issues struct CfgEnvBackwardsCompat { - inner: revm::CfgEnv, + inner: revm::primitives::CfgEnv, } impl<'de> Deserialize<'de> for CfgEnvBackwardsCompat { @@ -177,7 +179,7 @@ impl<'de> Deserialize<'de> for BlockchainDbMeta { } } - let cfg_env: revm::CfgEnv = + let cfg_env: revm::primitives::CfgEnv = serde_json::from_value(value).map_err(serde::de::Error::custom)?; Ok(Self { inner: cfg_env }) } @@ -187,7 +189,7 @@ impl<'de> Deserialize<'de> for BlockchainDbMeta { #[derive(Deserialize)] struct Meta { cfg_env: CfgEnvBackwardsCompat, - block_env: revm::BlockEnv, + block_env: revm::primitives::BlockEnv, /// all the hosts used to connect to #[serde(alias = "host")] hosts: Hosts, @@ -217,11 +219,11 @@ impl<'de> Deserialize<'de> for BlockchainDbMeta { #[derive(Debug, Default)] pub struct MemDb { /// Account related data - pub accounts: RwLock>, + pub accounts: RwLock>, /// Storage related data - pub storage: RwLock>, + pub storage: RwLock>, /// All retrieved block hashes - pub block_hashes: RwLock>, + pub block_hashes: RwLock>, } impl MemDb { @@ -233,12 +235,12 @@ impl MemDb { } // Inserts the account, replacing it if it exists already - pub fn do_insert_account(&self, address: Address, account: AccountInfo) { + pub fn do_insert_account(&self, address: B160, account: AccountInfo) { self.accounts.write().insert(address, account); } /// The implementation of [DatabaseCommit::commit()] - pub fn do_commit(&self, changes: Map) { + pub fn do_commit(&self, changes: Map) { let mut storage = self.storage.write(); let mut accounts = self.accounts.write(); for (add, mut acc) in changes { @@ -261,7 +263,7 @@ impl MemDb { acc_storage.clear(); } for (index, value) in acc.storage { - if value.present_value().is_zero() { + if value.present_value() == U256::from(0) { acc_storage.remove(&index); } else { acc_storage.insert(index, value.present_value()); @@ -286,7 +288,7 @@ impl Clone for MemDb { } impl DatabaseCommit for MemDb { - fn commit(&mut self, changes: Map) { + fn commit(&mut self, changes: Map) { self.do_commit(changes) } } diff --git a/evm/src/executor/fork/database.rs b/evm/src/executor/fork/database.rs index 4e51aa7e95500..5cfd48018f109 100644 --- a/evm/src/executor/fork/database.rs +++ b/evm/src/executor/fork/database.rs @@ -8,13 +8,14 @@ use crate::{ }, revm::db::CacheDB, }; -use ethers::{ - prelude::{Address, H256, U256}, - types::BlockId, -}; +use ethers::{prelude::U256, types::BlockId}; use hashbrown::HashMap as Map; use parking_lot::Mutex; -use revm::{db::DatabaseRef, Account, AccountInfo, Bytecode, Database, DatabaseCommit}; +use revm::{ + db::DatabaseRef, + primitives::{Account, AccountInfo, Bytecode, B160, B256, U256 as rU256}, + Database, DatabaseCommit, +}; use std::sync::Arc; use tracing::{trace, warn}; @@ -151,22 +152,22 @@ impl ForkedDatabase { impl Database for ForkedDatabase { type Error = DatabaseError; - fn basic(&mut self, address: Address) -> Result, Self::Error> { + fn basic(&mut self, address: B160) -> Result, Self::Error> { // Note: this will always return Some, since the `SharedBackend` will always load the // account, this differs from `::basic`, See also // [MemDb::ensure_loaded](crate::executor::backend::MemDb::ensure_loaded) Database::basic(&mut self.cache_db, address) } - fn code_by_hash(&mut self, code_hash: H256) -> Result { + fn code_by_hash(&mut self, code_hash: B256) -> Result { Database::code_by_hash(&mut self.cache_db, code_hash) } - fn storage(&mut self, address: Address, index: U256) -> Result { + fn storage(&mut self, address: B160, index: rU256) -> Result { Database::storage(&mut self.cache_db, address, index) } - fn block_hash(&mut self, number: U256) -> Result { + fn block_hash(&mut self, number: rU256) -> Result { Database::block_hash(&mut self.cache_db, number) } } @@ -174,25 +175,25 @@ impl Database for ForkedDatabase { impl DatabaseRef for ForkedDatabase { type Error = DatabaseError; - fn basic(&self, address: Address) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { self.cache_db.basic(address) } - fn code_by_hash(&self, code_hash: H256) -> Result { + fn code_by_hash(&self, code_hash: B256) -> Result { self.cache_db.code_by_hash(code_hash) } - fn storage(&self, address: Address, index: U256) -> Result { + fn storage(&self, address: B160, index: rU256) -> Result { DatabaseRef::storage(&self.cache_db, address, index) } - fn block_hash(&self, number: U256) -> Result { + fn block_hash(&self, number: rU256) -> Result { self.cache_db.block_hash(number) } } impl DatabaseCommit for ForkedDatabase { - fn commit(&mut self, changes: Map) { + fn commit(&mut self, changes: Map) { self.database_mut().commit(changes) } } @@ -209,7 +210,7 @@ pub struct ForkDbSnapshot { // === impl DbSnapshot === impl ForkDbSnapshot { - fn get_storage(&self, address: Address, index: U256) -> Option { + fn get_storage(&self, address: B160, index: rU256) -> Option { self.local.accounts.get(&address).and_then(|account| account.storage.get(&index)).copied() } } @@ -220,7 +221,7 @@ impl ForkDbSnapshot { impl DatabaseRef for ForkDbSnapshot { type Error = DatabaseError; - fn basic(&self, address: Address) -> Result, Self::Error> { + fn basic(&self, address: B160) -> Result, Self::Error> { match self.local.accounts.get(&address) { Some(account) => Ok(Some(account.info.clone())), None => { @@ -234,11 +235,11 @@ impl DatabaseRef for ForkDbSnapshot { } } - fn code_by_hash(&self, code_hash: H256) -> Result { + fn code_by_hash(&self, code_hash: B256) -> Result { self.local.code_by_hash(code_hash) } - fn storage(&self, address: Address, index: U256) -> Result { + fn storage(&self, address: B160, index: rU256) -> Result { match self.local.accounts.get(&address) { Some(account) => match account.storage.get(&index) { Some(entry) => Ok(*entry), @@ -254,7 +255,7 @@ impl DatabaseRef for ForkDbSnapshot { } } - fn block_hash(&self, number: U256) -> Result { + fn block_hash(&self, number: rU256) -> Result { match self.snapshot.block_hashes.get(&number).copied() { None => self.local.block_hash(number), Some(block_hash) => Ok(block_hash), @@ -285,12 +286,12 @@ mod tests { let backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await; let mut db = ForkedDatabase::new(backend, db); - let address = Address::random(); + let address = B160::random(); let info = Database::basic(&mut db, address).unwrap(); assert!(info.is_some()); let mut info = info.unwrap(); - info.balance = 500u64.into(); + info.balance = rU256::from(500u64); // insert the modified account info db.database_mut().insert_account_info(address, info.clone()); diff --git a/evm/src/executor/fork/init.rs b/evm/src/executor/fork/init.rs index 11ffadf529962..193a5fd172929 100644 --- a/evm/src/executor/fork/init.rs +++ b/evm/src/executor/fork/init.rs @@ -1,11 +1,13 @@ -use crate::utils::apply_chain_and_block_specific_env_changes; +use crate::utils::{ + apply_chain_and_block_specific_env_changes, h160_to_b160, h256_to_b256, u256_to_ru256, +}; use ethers::{ providers::Middleware, types::{Address, Block, TxHash, U256}, }; use eyre::WrapErr; use futures::TryFutureExt; -use revm::{BlockEnv, CfgEnv, Env, TxEnv}; +use revm::primitives::{BlockEnv, CfgEnv, Env, TxEnv}; /// Initializes a REVM block environment based on a forked /// ethereum provider. @@ -51,7 +53,7 @@ where let mut env = Env { cfg: CfgEnv { - chain_id: override_chain_id.unwrap_or(rpc_chain_id.as_u64()).into(), + chain_id: u256_to_ru256(override_chain_id.unwrap_or(rpc_chain_id.as_u64()).into()), memory_limit, limit_contract_code_size: Some(usize::MAX), // EIP-3607 rejects transactions from senders with deployed code. @@ -61,17 +63,17 @@ where ..Default::default() }, block: BlockEnv { - number: block.number.expect("block number not found").as_u64().into(), - timestamp: block.timestamp, - coinbase: block.author.unwrap_or_default(), - difficulty: block.difficulty, - prevrandao: block.mix_hash, - basefee: block.base_fee_per_gas.unwrap_or_default(), - gas_limit: block.gas_limit, + number: u256_to_ru256(block.number.expect("block number not found").as_u64().into()), + timestamp: block.timestamp.into(), + coinbase: h160_to_b160(block.author.unwrap_or_default()), + difficulty: block.difficulty.into(), + prevrandao: block.mix_hash.map(h256_to_b256), + basefee: block.base_fee_per_gas.unwrap_or_default().into(), + gas_limit: block.gas_limit.into(), }, tx: TxEnv { - caller: origin, - gas_price: gas_price.map(U256::from).unwrap_or(fork_gas_price), + caller: h160_to_b160(origin), + gas_price: gas_price.map(U256::from).unwrap_or(fork_gas_price).into(), chain_id: Some(override_chain_id.unwrap_or(rpc_chain_id.as_u64())), gas_limit: block.gas_limit.as_u64(), ..Default::default() diff --git a/evm/src/executor/fork/mod.rs b/evm/src/executor/fork/mod.rs index 6985b883017cb..8805c62b9356c 100644 --- a/evm/src/executor/fork/mod.rs +++ b/evm/src/executor/fork/mod.rs @@ -3,7 +3,7 @@ mod backend; use super::opts::EvmOpts; pub use backend::{BackendHandler, SharedBackend}; -use revm::Env; +use revm::primitives::Env; mod init; pub use init::environment; diff --git a/evm/src/executor/fork/multi.rs b/evm/src/executor/fork/multi.rs index f5015f9543b4c..241c973219f8d 100644 --- a/evm/src/executor/fork/multi.rs +++ b/evm/src/executor/fork/multi.rs @@ -3,8 +3,9 @@ //! The design is similar to the single `SharedBackend`, `BackendHandler` but supports multiple //! concurrently active pairs at once. -use crate::executor::fork::{ - BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend, +use crate::{ + executor::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}, + utils::ru256_to_u256, }; use ethers::{ abi::{AbiDecode, AbiEncode, AbiError}, @@ -19,7 +20,7 @@ use futures::{ task::{Context, Poll}, Future, FutureExt, StreamExt, }; -use revm::Env; +use revm::primitives::Env; use std::{ collections::HashMap, fmt, @@ -493,12 +494,14 @@ async fn create_fork( // we need to use the block number from the block because the env's number can be different on // some L2s (e.g. Arbitrum). - let number = - block.number.map(|num| num.as_u64()).unwrap_or_else(|| meta.block_env.number.as_u64()); + let number = block + .number + .map(|num| num.as_u64()) + .unwrap_or_else(|| ru256_to_u256(meta.block_env.number).as_u64()); // determine the cache path if caching is enabled let cache_path = if fork.enable_caching { - Config::foundry_block_cache_dir(meta.cfg_env.chain_id.as_u64(), number) + Config::foundry_block_cache_dir(ru256_to_u256(meta.cfg_env.chain_id).as_u64(), number) } else { None }; diff --git a/evm/src/executor/inspector/access_list.rs b/evm/src/executor/inspector/access_list.rs index 37202ca40de70..685963c66bc75 100644 --- a/evm/src/executor/inspector/access_list.rs +++ b/evm/src/executor/inspector/access_list.rs @@ -6,7 +6,12 @@ use ethers::{ }, }; use hashbrown::{HashMap, HashSet}; -use revm::{opcode, Database, EVMData, Inspector, Interpreter, Return}; +use revm::{ + interpreter::{opcode, InstructionResult, Interpreter}, + Database, EVMData, Inspector, +}; + +use crate::utils::{b160_to_h160, ru256_to_u256}; /// An inspector that collects touched accounts and storage slots. #[derive(Default, Debug)] @@ -54,7 +59,7 @@ where interpreter: &mut Interpreter, _data: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { + ) -> InstructionResult { let pc = interpreter.program_counter(); let op = interpreter.contract.bytecode.bytecode()[pc]; @@ -63,9 +68,9 @@ where if let Ok(slot) = interpreter.stack().peek(0) { let cur_contract = interpreter.contract.address; self.access_list - .entry(cur_contract) + .entry(b160_to_h160(cur_contract)) .or_default() - .insert(H256::from_uint(&slot)); + .insert(H256::from_uint(&ru256_to_u256(slot))); } } opcode::EXTCODECOPY | @@ -74,7 +79,7 @@ where opcode::BALANCE | opcode::SELFDESTRUCT => { if let Ok(slot) = interpreter.stack().peek(0) { - let addr: Address = H256::from_uint(&slot).into(); + let addr: Address = H256::from_uint(&ru256_to_u256(slot)).into(); if !self.excluded.contains(&addr) { self.access_list.entry(addr).or_default(); } @@ -82,7 +87,7 @@ where } opcode::DELEGATECALL | opcode::CALL | opcode::STATICCALL | opcode::CALLCODE => { if let Ok(slot) = interpreter.stack().peek(1) { - let addr: Address = H256::from_uint(&slot).into(); + let addr: Address = H256::from_uint(&ru256_to_u256(slot)).into(); if !self.excluded.contains(&addr) { self.access_list.entry(addr).or_default(); } @@ -91,6 +96,6 @@ where _ => (), } - Return::Continue + InstructionResult::Continue } } diff --git a/evm/src/executor/inspector/cheatcodes/env.rs b/evm/src/executor/inspector/cheatcodes/env.rs index 8f64e2aa9e59b..866a2a9ee79a3 100644 --- a/evm/src/executor/inspector/cheatcodes/env.rs +++ b/evm/src/executor/inspector/cheatcodes/env.rs @@ -8,6 +8,7 @@ use crate::{ backend::DatabaseExt, inspector::cheatcodes::{util::with_journaled_account, DealRecord}, }, + utils::{b160_to_h160, h160_to_b160, ru256_to_u256, u256_to_ru256}, }; use bytes::Bytes; use ethers::{ @@ -21,7 +22,7 @@ use ethers::{ types::{Address, U256}, }; use foundry_config::Config; -use revm::{Bytecode, Database, EVMData}; +use revm::{primitives::Bytecode, Database, EVMData}; use tracing::trace; #[derive(Clone, Debug, Default)] @@ -231,47 +232,52 @@ pub fn apply( ) -> Result, Bytes> { let res = match call { HEVMCalls::Warp(inner) => { - data.env.block.timestamp = inner.0; + data.env.block.timestamp = inner.0.into(); Bytes::new() } HEVMCalls::Difficulty(inner) => { - data.env.block.difficulty = inner.0; + data.env.block.difficulty = inner.0.into(); Bytes::new() } HEVMCalls::Roll(inner) => { - data.env.block.number = inner.0; + data.env.block.number = inner.0.into(); Bytes::new() } HEVMCalls::Fee(inner) => { - data.env.block.basefee = inner.0; + data.env.block.basefee = inner.0.into(); Bytes::new() } HEVMCalls::Coinbase(inner) => { - data.env.block.coinbase = inner.0; + data.env.block.coinbase = h160_to_b160(inner.0); Bytes::new() } HEVMCalls::Store(inner) => { data.journaled_state - .load_account(inner.0, data.db) + .load_account(h160_to_b160(inner.0), data.db) .map_err(|err| err.encode_string())?; // ensure the account is touched - data.journaled_state.touch(&inner.0); + data.journaled_state.touch(&h160_to_b160(inner.0)); data.journaled_state - .sstore(inner.0, inner.1.into(), inner.2.into(), data.db) + .sstore( + h160_to_b160(inner.0), + u256_to_ru256(inner.1.into()), + u256_to_ru256(inner.2.into()), + data.db, + ) .map_err(|err| err.encode_string())?; Bytes::new() } HEVMCalls::Load(inner) => { // TODO: Does this increase gas usage? data.journaled_state - .load_account(inner.0, data.db) + .load_account(h160_to_b160(inner.0), data.db) .map_err(|err| err.encode_string())?; let (val, _) = data .journaled_state - .sload(inner.0, inner.1.into(), data.db) + .sload(h160_to_b160(inner.0), u256_to_ru256(inner.1.into()), data.db) .map_err(|err| err.encode_string())?; - val.encode().into() + ru256_to_u256(val).encode().into() } HEVMCalls::Breakpoint0(inner) => add_breakpoint(state, caller, &inner.0, true)?, HEVMCalls::Breakpoint1(inner) => add_breakpoint(state, caller, &inner.0, inner.1)?, @@ -280,9 +286,10 @@ pub fn apply( trace!(address=?inner.0, code=?hex::encode(&code.0), "etch cheatcode"); // TODO: Does this increase gas usage? data.journaled_state - .load_account(inner.0, data.db) + .load_account(h160_to_b160(inner.0), data.db) .map_err(|err| err.encode_string())?; - data.journaled_state.set_code(inner.0, Bytecode::new_raw(code.0).to_checked()); + data.journaled_state + .set_code(h160_to_b160(inner.0), Bytecode::new_raw(code.0).to_checked()); Bytes::new() } HEVMCalls::Deal(inner) => { @@ -293,12 +300,12 @@ pub fn apply( // record the deal let record = DealRecord { address: who, - old_balance: account.info.balance, + old_balance: account.info.balance.into(), new_balance: value, }; state.eth_deals.push(record); - account.info.balance = value; + account.info.balance = value.into(); }) .map_err(|err| err.encode_string())?; Bytes::new() @@ -306,7 +313,7 @@ pub fn apply( HEVMCalls::Prank0(inner) => prank( state, caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), inner.0, None, data.journaled_state.depth(), @@ -315,7 +322,7 @@ pub fn apply( HEVMCalls::Prank1(inner) => prank( state, caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), inner.0, Some(inner.1), data.journaled_state.depth(), @@ -324,7 +331,7 @@ pub fn apply( HEVMCalls::StartPrank0(inner) => prank( state, caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), inner.0, None, data.journaled_state.depth(), @@ -333,7 +340,7 @@ pub fn apply( HEVMCalls::StartPrank1(inner) => prank( state, caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), inner.0, Some(inner.1), data.journaled_state.depth(), @@ -369,7 +376,7 @@ pub fn apply( } HEVMCalls::GetNonce(inner) => { correct_sender_nonce( - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), &mut data.journaled_state, &mut data.db, state, @@ -379,27 +386,27 @@ pub fn apply( // TODO: this is probably not a good long-term solution since it might mess up the gas // calculations data.journaled_state - .load_account(inner.0, data.db) + .load_account(h160_to_b160(inner.0), data.db) .map_err(|err| err.encode_string())?; // we can safely unwrap because `load_account` insert inner.0 to DB. - let account = data.journaled_state.state().get(&inner.0).unwrap(); + let account = data.journaled_state.state().get(&h160_to_b160(inner.0)).unwrap(); abi::encode(&[Token::Uint(account.info.nonce.into())]).into() } HEVMCalls::ChainId(inner) => { if inner.0 > U256::from(u64::MAX) { return Err("Chain ID must be less than 2^64".to_string().encode().into()) } - data.env.cfg.chain_id = inner.0; + data.env.cfg.chain_id = inner.0.into(); Bytes::new() } HEVMCalls::TxGasPrice(inner) => { - data.env.tx.gas_price = inner.0; + data.env.tx.gas_price = inner.0.into(); Bytes::new() } HEVMCalls::Broadcast0(_) => { correct_sender_nonce( - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), &mut data.journaled_state, &mut data.db, state, @@ -407,16 +414,16 @@ pub fn apply( .map_err(|err| err.encode_string())?; broadcast( state, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), data.journaled_state.depth(), true, )? } HEVMCalls::Broadcast1(inner) => { correct_sender_nonce( - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), &mut data.journaled_state, &mut data.db, state, @@ -426,14 +433,14 @@ pub fn apply( state, inner.0, caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), data.journaled_state.depth(), true, )? } HEVMCalls::Broadcast2(inner) => { correct_sender_nonce( - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), &mut data.journaled_state, &mut data.db, state, @@ -443,15 +450,15 @@ pub fn apply( state, inner.0, caller, - data.env.tx.caller, - data.env.cfg.chain_id, + b160_to_h160(data.env.tx.caller), + data.env.cfg.chain_id.into(), data.journaled_state.depth(), true, )? } HEVMCalls::StartBroadcast0(_) => { correct_sender_nonce( - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), &mut data.journaled_state, &mut data.db, state, @@ -459,16 +466,16 @@ pub fn apply( .map_err(|err| err.encode_string())?; broadcast( state, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), data.journaled_state.depth(), false, )? } HEVMCalls::StartBroadcast1(inner) => { correct_sender_nonce( - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), &mut data.journaled_state, &mut data.db, state, @@ -478,14 +485,14 @@ pub fn apply( state, inner.0, caller, - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), data.journaled_state.depth(), false, )? } HEVMCalls::StartBroadcast2(inner) => { correct_sender_nonce( - data.env.tx.caller, + b160_to_h160(data.env.tx.caller), &mut data.journaled_state, &mut data.db, state, @@ -495,8 +502,8 @@ pub fn apply( state, inner.0, caller, - data.env.tx.caller, - data.env.cfg.chain_id, + b160_to_h160(data.env.tx.caller), + data.env.cfg.chain_id.into(), data.journaled_state.depth(), false, )? diff --git a/evm/src/executor/inspector/cheatcodes/expect.rs b/evm/src/executor/inspector/cheatcodes/expect.rs index 7fbd5d2a289b2..e2bdf9d1c05dc 100644 --- a/evm/src/executor/inspector/cheatcodes/expect.rs +++ b/evm/src/executor/inspector/cheatcodes/expect.rs @@ -3,13 +3,18 @@ use crate::{ abi::HEVMCalls, error::{SolError, ERROR_PREFIX, REVERT_PREFIX}, executor::backend::DatabaseExt, + utils::h160_to_b160, }; use bytes::Bytes; use ethers::{ abi::{AbiDecode, AbiEncode, RawLog}, types::{Address, H160, U256}, }; -use revm::{return_ok, Bytecode, EVMData, Return}; +use revm::{ + interpreter::{return_ok, InstructionResult}, + primitives::Bytecode, + EVMData, +}; use std::cmp::Ordering; use tracing::{instrument, trace}; @@ -53,7 +58,7 @@ fn expect_revert( pub fn handle_expect_revert( is_create: bool, expected_revert: Option<&Bytes>, - status: Return, + status: InstructionResult, retdata: Bytes, ) -> Result<(Option
, Bytes), Bytes> { trace!("handle expect revert"); @@ -225,7 +230,7 @@ pub struct MockCallDataContext { #[derive(Clone, Debug)] pub struct MockCallReturnData { /// The return type for the mocked call - pub ret_type: Return, + pub ret_type: InstructionResult, /// Return data or error pub data: Bytes, } @@ -416,7 +421,7 @@ pub fn apply( } HEVMCalls::MockCall0(inner) => { // TODO: Does this increase gas usage? - if let Err(err) = data.journaled_state.load_account(inner.0, data.db) { + if let Err(err) = data.journaled_state.load_account(h160_to_b160(inner.0), data.db) { return Some(Err(err.encode_string())) } @@ -424,7 +429,7 @@ pub fn apply( // check Solidity might perform. if data .journaled_state - .account(inner.0) + .account(h160_to_b160(inner.0)) .info .code .as_ref() @@ -432,32 +437,44 @@ pub fn apply( .unwrap_or(true) { let code = Bytecode::new_raw(Bytes::from_static(&[0u8])).to_checked(); - data.journaled_state.set_code(inner.0, code); + data.journaled_state.set_code(h160_to_b160(inner.0), code); } state.mocked_calls.entry(inner.0).or_default().insert( MockCallDataContext { calldata: inner.1.to_vec().into(), value: None }, - MockCallReturnData { data: inner.2.to_vec().into(), ret_type: Return::Return }, + MockCallReturnData { + data: inner.2.to_vec().into(), + ret_type: InstructionResult::Return, + }, ); Ok(Bytes::new()) } HEVMCalls::MockCall1(inner) => { state.mocked_calls.entry(inner.0).or_default().insert( MockCallDataContext { calldata: inner.2.to_vec().into(), value: Some(inner.1) }, - MockCallReturnData { data: inner.3.to_vec().into(), ret_type: Return::Return }, + MockCallReturnData { + data: inner.3.to_vec().into(), + ret_type: InstructionResult::Return, + }, ); Ok(Bytes::new()) } HEVMCalls::MockCallRevert0(inner) => { state.mocked_calls.entry(inner.0).or_default().insert( MockCallDataContext { calldata: inner.1.to_vec().into(), value: None }, - MockCallReturnData { data: inner.2.to_vec().into(), ret_type: Return::Revert }, + MockCallReturnData { + data: inner.2.to_vec().into(), + ret_type: InstructionResult::Revert, + }, ); Ok(Bytes::new()) } HEVMCalls::MockCallRevert1(inner) => { state.mocked_calls.entry(inner.0).or_default().insert( MockCallDataContext { calldata: inner.2.to_vec().into(), value: Some(inner.1) }, - MockCallReturnData { data: inner.3.to_vec().into(), ret_type: Return::Revert }, + MockCallReturnData { + data: inner.3.to_vec().into(), + ret_type: InstructionResult::Revert, + }, ); Ok(Bytes::new()) } diff --git a/evm/src/executor/inspector/cheatcodes/mod.rs b/evm/src/executor/inspector/cheatcodes/mod.rs index 4844323c65788..2a7b8906a2dfa 100644 --- a/evm/src/executor/inspector/cheatcodes/mod.rs +++ b/evm/src/executor/inspector/cheatcodes/mod.rs @@ -10,21 +10,22 @@ use crate::{ backend::DatabaseExt, inspector::cheatcodes::env::RecordedLogs, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, }, + utils::{b160_to_h160, b256_to_h256, h160_to_b160, ru256_to_u256}, }; use bytes::Bytes; use ethers::{ abi::{AbiDecode, AbiEncode, RawLog}, signers::LocalWallet, types::{ - transaction::eip2718::TypedTransaction, Address, NameOrAddress, TransactionRequest, H256, - U256, + transaction::eip2718::TypedTransaction, Address, NameOrAddress, TransactionRequest, U256, }, }; use foundry_common::evm::Breakpoints; use itertools::Itertools; use revm::{ - opcode, BlockEnv, CallInputs, CreateInputs, EVMData, Gas, Inspector, Interpreter, Return, - TransactTo, + interpreter::{opcode, CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, + primitives::{BlockEnv, TransactTo, B160, B256}, + EVMData, Inspector, }; use serde_json::Value; use std::{ @@ -160,12 +161,12 @@ pub struct Cheatcodes { /// we need to copy. So we convert it to a `Some(None)` in `apply_cheatcode`, and once we have /// the interpreter, we copy the gas struct. Then each time there is an execution of an /// operation, we reset the gas. - pub gas_metering: Option>, + pub gas_metering: Option>, /// Holds stored gas info for when we pause gas metering, and we're entering/inside /// CREATE / CREATE2 frames. This is needed to make gas meter pausing work correctly when /// paused and creating new contracts. - pub gas_metering_create: Option>, + pub gas_metering_create: Option>, /// current program counter pub pc: usize, /// Breakpoints supplied by the `vm.breakpoint("")` cheatcode @@ -229,7 +230,9 @@ impl Cheatcodes { .unwrap_or_default(); let created_address = get_create_address(inputs, old_nonce); - if data.journaled_state.depth > 1 && !data.db.has_cheatcode_access(inputs.caller) { + if data.journaled_state.depth > 1 && + !data.db.has_cheatcode_access(b160_to_h160(inputs.caller)) + { // we only grant cheat code access for new contracts if the caller also has // cheatcode access and the new contract is created in top most call return @@ -259,8 +262,8 @@ impl Cheatcodes { // This will prevent overflow issues in revm's [`JournaledState::journal_revert`] routine // which rolls back any transfers. while let Some(record) = self.eth_deals.pop() { - if let Some(acc) = data.journaled_state.state.get_mut(&record.address) { - acc.info.balance = record.old_balance; + if let Some(acc) = data.journaled_state.state.get_mut(&h160_to_b160(record.address)) { + acc.info.balance = record.old_balance.into(); } } } @@ -275,17 +278,17 @@ where _: &mut Interpreter, data: &mut EVMData<'_, DB>, _: bool, - ) -> Return { + ) -> InstructionResult { // When the first interpreter is initialized we've circumvented the balance and gas checks, // so we apply our actual block data with the correct fees and all. if let Some(block) = self.block.take() { data.env.block = block; } if let Some(gas_price) = self.gas_price.take() { - data.env.tx.gas_price = gas_price; + data.env.tx.gas_price = gas_price.into(); } - Return::Continue + InstructionResult::Continue } fn step( @@ -293,7 +296,7 @@ where interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, _: bool, - ) -> Return { + ) -> InstructionResult { self.pc = interpreter.program_counter(); // reset gas if gas metering is turned off @@ -318,7 +321,7 @@ where // could just do this there instead. match self.gas_metering_create { None | Some(None) => { - interpreter.gas = revm::Gas::new(0); + interpreter.gas = revm::interpreter::Gas::new(0); } Some(Some(gas)) => { // If this was CREATE frame, set correct gas limit. This is needed @@ -332,7 +335,7 @@ where // used, and erases costs by `remaining` gas post-create. // gas used ref: https://github.com/bluealloy/revm/blob/2cb991091d32330cfe085320891737186947ce5a/crates/revm/src/instructions/host.rs#L254-L258 // post-create erase ref: https://github.com/bluealloy/revm/blob/2cb991091d32330cfe085320891737186947ce5a/crates/revm/src/instructions/host.rs#L279 - interpreter.gas = revm::Gas::new(gas.limit()); + interpreter.gas = revm::interpreter::Gas::new(gas.limit()); // reset CREATE gas metering because we're about to exit its frame self.gas_metering_create = None @@ -360,9 +363,9 @@ where let key = try_or_continue!(interpreter.stack().peek(0)); storage_accesses .reads - .entry(interpreter.contract().address) + .entry(b160_to_h160(interpreter.contract().address)) .or_insert_with(Vec::new) - .push(key); + .push(key.into()); } opcode::SSTORE => { let key = try_or_continue!(interpreter.stack().peek(0)); @@ -370,14 +373,14 @@ where // An SSTORE does an SLOAD internally storage_accesses .reads - .entry(interpreter.contract().address) + .entry(b160_to_h160(interpreter.contract().address)) .or_insert_with(Vec::new) - .push(key); + .push(key.into()); storage_accesses .writes - .entry(interpreter.contract().address) + .entry(b160_to_h160(interpreter.contract().address)) .or_insert_with(Vec::new) - .push(key); + .push(key.into()); } _ => (), } @@ -405,7 +408,7 @@ where opcode::MSTORE => { // The offset of the mstore operation is at the top of the stack. - let offset = try_or_continue!(interpreter.stack().peek(0)).as_u64(); + let offset = ru256_to_u256(try_or_continue!(interpreter.stack().peek(0))).as_u64(); // If none of the allowed ranges contain [offset, offset + 32), memory has been // unexpectedly mutated. @@ -413,18 +416,18 @@ where range.contains(&offset) && range.contains(&(offset + 31)) }) { revert_helper::disallowed_mem_write(offset, 32, interpreter, ranges); - return Return::Revert + return InstructionResult::Revert } } opcode::MSTORE8 => { // The offset of the mstore8 operation is at the top of the stack. - let offset = try_or_continue!(interpreter.stack().peek(0)).as_u64(); + let offset = ru256_to_u256(try_or_continue!(interpreter.stack().peek(0))).as_u64(); // If none of the allowed ranges contain the offset, memory has been // unexpectedly mutated. if !ranges.iter().any(|range| range.contains(&offset)) { revert_helper::disallowed_mem_write(offset, 1, interpreter, ranges); - return Return::Revert + return InstructionResult::Revert } } @@ -434,7 +437,7 @@ where opcode::MLOAD => { // The offset of the mload operation is at the top of the stack - let offset = try_or_continue!(interpreter.stack().peek(0)).as_u64(); + let offset = ru256_to_u256(try_or_continue!(interpreter.stack().peek(0))).as_u64(); // If the offset being loaded is >= than the memory size, the // memory is being expanded. If none of the allowed ranges contain @@ -443,7 +446,7 @@ where range.contains(&offset) && range.contains(&(offset + 31)) }) { revert_helper::disallowed_mem_write(offset, 32, interpreter, ranges); - return Return::Revert + return InstructionResult::Revert } } @@ -453,10 +456,10 @@ where $(opcode::$opcode => { // The destination offset of the operation is at the top of the stack. - let dest_offset = try_or_continue!(interpreter.stack().peek($offset_depth)).as_u64(); + let dest_offset = ru256_to_u256(try_or_continue!(interpreter.stack().peek($offset_depth))).as_u64(); // The size of the data that will be copied is the third item on the stack. - let size = try_or_continue!(interpreter.stack().peek($size_depth)).as_u64(); + let size = ru256_to_u256(try_or_continue!(interpreter.stack().peek($size_depth))).as_u64(); // If none of the allowed ranges contain [dest_offset, dest_offset + size), // memory outside of the expected ranges has been touched. If the opcode @@ -474,7 +477,7 @@ where // that gives information about the allowed ranges and revert. if fail_cond { revert_helper::disallowed_mem_write(dest_offset, size, interpreter, ranges); - return Return::Revert + return InstructionResult::Revert } })* _ => () @@ -506,24 +509,30 @@ where ]) } - Return::Continue + InstructionResult::Continue } - fn log(&mut self, _: &mut EVMData<'_, DB>, address: &Address, topics: &[H256], data: &Bytes) { + fn log(&mut self, _: &mut EVMData<'_, DB>, address: &B160, topics: &[B256], data: &Bytes) { // Match logs if `expectEmit` has been called if !self.expected_emits.is_empty() { handle_expect_emit( self, - RawLog { topics: topics.to_vec(), data: data.to_vec() }, - address, + RawLog { + topics: topics.iter().copied().map(b256_to_h256).collect_vec(), + data: data.to_vec(), + }, + &b160_to_h160(*address), ); } // Stores this log if `recordLogs` has been called if let Some(storage_recorded_logs) = &mut self.recorded_logs { storage_recorded_logs.entries.push(Log { - emitter: *address, - inner: RawLog { topics: topics.to_vec(), data: data.to_vec() }, + emitter: b160_to_h160(*address), + inner: RawLog { + topics: topics.iter().copied().map(b256_to_h256).collect_vec(), + data: data.to_vec(), + }, }); } } @@ -533,19 +542,19 @@ where data: &mut EVMData<'_, DB>, call: &mut CallInputs, is_static: bool, - ) -> (Return, Gas, Bytes) { - if call.contract == CHEATCODE_ADDRESS { - match self.apply_cheatcode(data, call.context.caller, call) { - Ok(retdata) => (Return::Return, Gas::new(call.gas_limit), retdata), - Err(err) => (Return::Revert, Gas::new(call.gas_limit), err), + ) -> (InstructionResult, Gas, Bytes) { + if call.contract == h160_to_b160(CHEATCODE_ADDRESS) { + match self.apply_cheatcode(data, b160_to_h160(call.context.caller), call) { + Ok(retdata) => (InstructionResult::Return, Gas::new(call.gas_limit), retdata), + Err(err) => (InstructionResult::Revert, Gas::new(call.gas_limit), err), } - } else if call.contract != HARDHAT_CONSOLE_ADDRESS { + } else if call.contract != h160_to_b160(HARDHAT_CONSOLE_ADDRESS) { // Handle expected calls - if let Some(expecteds) = self.expected_calls.get_mut(&call.contract) { + if let Some(expecteds) = self.expected_calls.get_mut(&(b160_to_h160(call.contract))) { if let Some((_, count)) = expecteds.iter_mut().find(|(expected, _)| { expected.calldata.len() <= call.input.len() && expected.calldata == call.input[..expected.calldata.len()] && - expected.value.map_or(true, |value| value == call.transfer.value) && + expected.value.map_or(true, |value| value == call.transfer.value.into()) && expected.gas.map_or(true, |gas| gas == call.gas_limit) && expected.min_gas.map_or(true, |min_gas| min_gas <= call.gas_limit) }) { @@ -554,10 +563,10 @@ where } // Handle mocked calls - if let Some(mocks) = self.mocked_calls.get(&call.contract) { + if let Some(mocks) = self.mocked_calls.get(&b160_to_h160(call.contract)) { let ctx = MockCallDataContext { calldata: call.input.clone(), - value: Some(call.transfer.value), + value: Some(call.transfer.value.into()), }; if let Some(mock_retdata) = mocks.get(&ctx) { return ( @@ -568,7 +577,9 @@ where } else if let Some((_, mock_retdata)) = mocks.iter().find(|(mock, _)| { mock.calldata.len() <= call.input.len() && *mock.calldata == call.input[..mock.calldata.len()] && - mock.value.map(|value| value == call.transfer.value).unwrap_or(true) + mock.value + .map(|value| value == call.transfer.value.into()) + .unwrap_or(true) }) { return ( mock_retdata.ret_type, @@ -581,17 +592,17 @@ where // Apply our prank if let Some(prank) = &self.prank { if data.journaled_state.depth() >= prank.depth && - call.context.caller == prank.prank_caller + call.context.caller == h160_to_b160(prank.prank_caller) { // At the target depth we set `msg.sender` if data.journaled_state.depth() == prank.depth { - call.context.caller = prank.new_caller; - call.transfer.source = prank.new_caller; + call.context.caller = h160_to_b160(prank.new_caller); + call.transfer.source = h160_to_b160(prank.new_caller); } // At the target depth, or deeper, we set `tx.origin` if let Some(new_origin) = prank.new_origin { - data.env.tx.caller = new_origin; + data.env.tx.caller = h160_to_b160(new_origin); } } } @@ -603,37 +614,45 @@ where // We do this because any subsequent contract calls *must* exist on chain and // we only want to grab *this* call, not internal ones if data.journaled_state.depth() == broadcast.depth && - call.context.caller == broadcast.original_caller + call.context.caller == h160_to_b160(broadcast.original_caller) { // At the target depth we set `msg.sender` & tx.origin. // We are simulating the caller as being an EOA, so *both* must be set to the // broadcast.origin. - data.env.tx.caller = broadcast.new_origin; + data.env.tx.caller = h160_to_b160(broadcast.new_origin); - call.context.caller = broadcast.new_origin; - call.transfer.source = broadcast.new_origin; + call.context.caller = h160_to_b160(broadcast.new_origin); + call.transfer.source = h160_to_b160(broadcast.new_origin); // Add a `legacy` transaction to the VecDeque. We use a legacy transaction here // because we only need the from, to, value, and data. We can later change this // into 1559, in the cli package, relatively easily once we // know the target chain supports EIP-1559. if !is_static { - if let Err(err) = - data.journaled_state.load_account(broadcast.new_origin, data.db) + if let Err(err) = data + .journaled_state + .load_account(h160_to_b160(broadcast.new_origin), data.db) { - return (Return::Revert, Gas::new(call.gas_limit), err.encode_string()) + return ( + InstructionResult::Revert, + Gas::new(call.gas_limit), + err.encode_string(), + ) } let is_fixed_gas_limit = check_if_fixed_gas_limit(data, call.gas_limit); - let account = - data.journaled_state.state().get_mut(&broadcast.new_origin).unwrap(); + let account = data + .journaled_state + .state() + .get_mut(&h160_to_b160(broadcast.new_origin)) + .unwrap(); self.broadcastable_transactions.push_back(BroadcastableTransaction { rpc: data.db.active_fork_url(), transaction: TypedTransaction::Legacy(TransactionRequest { from: Some(broadcast.new_origin), - to: Some(NameOrAddress::Address(call.contract)), - value: Some(call.transfer.value), + to: Some(NameOrAddress::Address(b160_to_h160(call.contract))), + value: Some(call.transfer.value.into()), data: Some(call.input.clone().into()), nonce: Some(account.info.nonce.into()), gas: if is_fixed_gas_limit { @@ -649,7 +668,7 @@ where account.info.nonce += 1; } else if broadcast.single_call { return ( - Return::Revert, + InstructionResult::Revert, Gas::new(0), "Staticcalls are not allowed after vm.broadcast. Either remove it, or use vm.startBroadcast instead." .to_string() @@ -660,9 +679,9 @@ where } } - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) } else { - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) } } @@ -671,18 +690,20 @@ where data: &mut EVMData<'_, DB>, call: &CallInputs, remaining_gas: Gas, - status: Return, + status: InstructionResult, retdata: Bytes, _: bool, - ) -> (Return, Gas, Bytes) { - if call.contract == CHEATCODE_ADDRESS || call.contract == HARDHAT_CONSOLE_ADDRESS { + ) -> (InstructionResult, Gas, Bytes) { + if call.contract == h160_to_b160(CHEATCODE_ADDRESS) || + call.contract == h160_to_b160(HARDHAT_CONSOLE_ADDRESS) + { return (status, remaining_gas, retdata) } // Clean up pranks if let Some(prank) = &self.prank { if data.journaled_state.depth() == prank.depth { - data.env.tx.caller = prank.prank_origin; + data.env.tx.caller = h160_to_b160(prank.prank_origin); } if prank.single_call { std::mem::take(&mut self.prank); @@ -692,7 +713,7 @@ where // Clean up broadcast if let Some(broadcast) = &self.broadcast { if data.journaled_state.depth() == broadcast.depth { - data.env.tx.caller = broadcast.original_origin; + data.env.tx.caller = h160_to_b160(broadcast.original_origin); } if broadcast.single_call { @@ -712,9 +733,9 @@ where ) { Err(retdata) => { trace!(expected=?expected_revert, actual=%hex::encode(&retdata), ?status, "Expected revert mismatch"); - (Return::Revert, remaining_gas, retdata) + (InstructionResult::Revert, remaining_gas, retdata) } - Ok((_, retdata)) => (Return::Return, remaining_gas, retdata), + Ok((_, retdata)) => (InstructionResult::Return, remaining_gas, retdata), } } } @@ -727,7 +748,7 @@ where .all(|expected| expected.found) { return ( - Return::Revert, + InstructionResult::Revert, remaining_gas, "Log != expected log".to_string().encode().into(), ) @@ -754,7 +775,7 @@ where if count.is_none() { if *actual_count == 0 { return ( - Return::Revert, + InstructionResult::Revert, remaining_gas, format!("Expected at least one call to {address:?} with {expected_values}, but got none") .encode() @@ -763,7 +784,7 @@ where } } else if *count != Some(*actual_count) { return ( - Return::Revert, + InstructionResult::Revert, remaining_gas, format!( "Expected call to {:?} with {} to be made {} time(s), but was called {} time(s)", @@ -782,7 +803,7 @@ where // Check if we have any leftover expected emits if !self.expected_emits.is_empty() { return ( - Return::Revert, + InstructionResult::Revert, remaining_gas, "Expected an emit, but no logs were emitted afterward" .to_string() @@ -794,7 +815,7 @@ where // if there's a revert and a previous call was diagnosed as fork related revert then we can // return a better error here - if status == Return::Revert { + if status == InstructionResult::Revert { if let Some(err) = self.fork_revert_diagnostic.take() { return (status, remaining_gas, err.to_error_msg(self).encode().into()) } @@ -809,10 +830,12 @@ where if let TransactTo::Call(test_contract) = data.env.tx.transact_to { // if a call to a different contract than the original test contract returned with // `Stop` we check if the contract actually exists on the active fork - if data.db.is_forked_mode() && status == Return::Stop && call.contract != test_contract + if data.db.is_forked_mode() && + status == InstructionResult::Stop && + call.contract != test_contract { self.fork_revert_diagnostic = - data.db.diagnose_revert(call.contract, &data.journaled_state); + data.db.diagnose_revert(b160_to_h160(call.contract), &data.journaled_state); } } @@ -823,21 +846,23 @@ where &mut self, data: &mut EVMData<'_, DB>, call: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { // allow cheatcodes from the address of the new contract self.allow_cheatcodes_on_create(data, call); // Apply our prank if let Some(prank) = &self.prank { - if data.journaled_state.depth() >= prank.depth && call.caller == prank.prank_caller { + if data.journaled_state.depth() >= prank.depth && + call.caller == h160_to_b160(prank.prank_caller) + { // At the target depth we set `msg.sender` if data.journaled_state.depth() == prank.depth { - call.caller = prank.new_caller; + call.caller = h160_to_b160(prank.new_caller); } // At the target depth, or deeper, we set `tx.origin` if let Some(new_origin) = prank.new_origin { - data.env.tx.caller = new_origin; + data.env.tx.caller = h160_to_b160(new_origin); } } } @@ -845,13 +870,20 @@ where // Apply our broadcast if let Some(broadcast) = &self.broadcast { if data.journaled_state.depth() == broadcast.depth && - call.caller == broadcast.original_caller + call.caller == h160_to_b160(broadcast.original_caller) { - if let Err(err) = data.journaled_state.load_account(broadcast.new_origin, data.db) { - return (Return::Revert, None, Gas::new(call.gas_limit), err.encode_string()) + if let Err(err) = + data.journaled_state.load_account(h160_to_b160(broadcast.new_origin), data.db) + { + return ( + InstructionResult::Revert, + None, + Gas::new(call.gas_limit), + err.encode_string(), + ) } - data.env.tx.caller = broadcast.new_origin; + data.env.tx.caller = h160_to_b160(broadcast.new_origin); let (bytecode, to, nonce) = match process_create( broadcast.new_origin, @@ -861,7 +893,12 @@ where ) { Ok(val) => val, Err(err) => { - return (Return::Revert, None, Gas::new(call.gas_limit), err.encode_string()) + return ( + InstructionResult::Revert, + None, + Gas::new(call.gas_limit), + err.encode_string(), + ) } }; @@ -872,7 +909,7 @@ where transaction: TypedTransaction::Legacy(TransactionRequest { from: Some(broadcast.new_origin), to, - value: Some(call.value), + value: Some(call.value.into()), data: Some(bytecode.into()), nonce: Some(nonce.into()), gas: if is_fixed_gas_limit { Some(call.gas_limit.into()) } else { None }, @@ -882,22 +919,22 @@ where } } - (Return::Continue, None, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()) } fn create_end( &mut self, data: &mut EVMData<'_, DB>, _: &CreateInputs, - status: Return, - address: Option
, + status: InstructionResult, + address: Option, remaining_gas: Gas, retdata: Bytes, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { // Clean up pranks if let Some(prank) = &self.prank { if data.journaled_state.depth() == prank.depth { - data.env.tx.caller = prank.prank_origin; + data.env.tx.caller = h160_to_b160(prank.prank_origin); } if prank.single_call { std::mem::take(&mut self.prank); @@ -907,7 +944,7 @@ where // Clean up broadcasts if let Some(broadcast) = &self.broadcast { if data.journaled_state.depth() == broadcast.depth { - data.env.tx.caller = broadcast.original_origin; + data.env.tx.caller = h160_to_b160(broadcast.original_origin); } if broadcast.single_call { @@ -925,8 +962,13 @@ where status, retdata, ) { - Err(retdata) => (Return::Revert, None, remaining_gas, retdata), - Ok((address, retdata)) => (Return::Return, address, remaining_gas, retdata), + Err(retdata) => (InstructionResult::Revert, None, remaining_gas, retdata), + Ok((address, retdata)) => ( + InstructionResult::Return, + address.map(h160_to_b160), + remaining_gas, + retdata, + ), } } } @@ -934,7 +976,6 @@ where (status, address, remaining_gas, retdata) } } - /// Contains additional, test specific resources that should be kept for the duration of the test #[derive(Debug, Default)] pub struct Context { diff --git a/evm/src/executor/inspector/cheatcodes/util.rs b/evm/src/executor/inspector/cheatcodes/util.rs index a18b3ac90b737..a22258f438a89 100644 --- a/evm/src/executor/inspector/cheatcodes/util.rs +++ b/evm/src/executor/inspector/cheatcodes/util.rs @@ -5,7 +5,7 @@ use crate::{ error::{DatabaseError, DatabaseResult}, DatabaseExt, }, - utils::h256_to_u256_be, + utils::{h160_to_b160, h256_to_u256_be, ru256_to_u256, u256_to_ru256}, }; use bytes::{BufMut, Bytes, BytesMut}; use ethers::{ @@ -20,7 +20,11 @@ use ethers::{ utils, }; use foundry_common::{fmt::*, RpcUrl}; -use revm::{Account, CreateInputs, Database, EVMData, JournaledState, TransactTo}; +use revm::{ + interpreter::CreateInputs, + primitives::{Account, TransactTo}, + Database, EVMData, JournaledState, +}; use std::collections::VecDeque; use tracing::trace; @@ -41,11 +45,11 @@ pub struct BroadcastableTransaction { pub type BroadcastableTransactions = VecDeque; /// Configures the env for the transaction -pub fn configure_tx_env(env: &mut revm::Env, tx: &Transaction) { - env.tx.caller = tx.from; +pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { + env.tx.caller = h160_to_b160(tx.from); env.tx.gas_limit = tx.gas.as_u64(); - env.tx.gas_price = tx.gas_price.unwrap_or_default(); - env.tx.gas_priority_fee = tx.max_priority_fee_per_gas; + env.tx.gas_price = tx.gas_price.unwrap_or_default().into(); + env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(Into::into); env.tx.nonce = Some(tx.nonce.as_u64()); env.tx.access_list = tx .access_list @@ -53,11 +57,17 @@ pub fn configure_tx_env(env: &mut revm::Env, tx: &Transaction) { .unwrap_or_default() .0 .into_iter() - .map(|item| (item.address, item.storage_keys.into_iter().map(h256_to_u256_be).collect())) + .map(|item| { + ( + h160_to_b160(item.address), + item.storage_keys.into_iter().map(h256_to_u256_be).map(u256_to_ru256).collect(), + ) + }) .collect(); - env.tx.value = tx.value; + env.tx.value = tx.value.into(); env.tx.data = tx.input.0.clone(); - env.tx.transact_to = tx.to.map(TransactTo::Call).unwrap_or_else(TransactTo::create) + env.tx.transact_to = + tx.to.map(h160_to_b160).map(TransactTo::Call).unwrap_or_else(TransactTo::create) } /// Applies the given function `f` to the `revm::Account` belonging to the `addr` @@ -72,6 +82,7 @@ pub fn with_journaled_account( where F: FnMut(&mut Account) -> R, { + let addr = h160_to_b160(addr); journaled_state.load_account(addr, db)?; journaled_state.touch(&addr); let account = journaled_state.state.get_mut(&addr).expect("account loaded;"); @@ -141,12 +152,12 @@ pub fn apply( ) -> Option> { Some(match call { HEVMCalls::Addr(inner) => addr(inner.0), - HEVMCalls::Sign(inner) => sign(inner.0, inner.1.into(), data.env.cfg.chain_id), + HEVMCalls::Sign(inner) => sign(inner.0, inner.1.into(), data.env.cfg.chain_id.into()), HEVMCalls::DeriveKey0(inner) => { derive_key(&inner.0, DEFAULT_DERIVATION_PATH_PREFIX, inner.1) } HEVMCalls::DeriveKey1(inner) => derive_key(&inner.0, &inner.1, inner.2), - HEVMCalls::RememberKey(inner) => remember_key(state, inner.0, data.env.cfg.chain_id), + HEVMCalls::RememberKey(inner) => remember_key(state, inner.0, data.env.cfg.chain_id.into()), HEVMCalls::Label(inner) => { state.labels.insert(inner.0, inner.1.clone()); Ok(Bytes::new()) @@ -188,17 +199,18 @@ pub fn process_create( where DB: Database, { + let broadcast_sender = h160_to_b160(broadcast_sender); match call.scheme { - revm::CreateScheme::Create => { + revm::primitives::CreateScheme::Create => { call.caller = broadcast_sender; Ok((bytecode, None, data.journaled_state.account(broadcast_sender).info.nonce)) } - revm::CreateScheme::Create2 { salt } => { + revm::primitives::CreateScheme::Create2 { salt } => { // Sanity checks for our CREATE2 deployer - data.journaled_state.load_account(DEFAULT_CREATE2_DEPLOYER, data.db)?; + data.journaled_state.load_account(h160_to_b160(DEFAULT_CREATE2_DEPLOYER), data.db)?; - let info = &data.journaled_state.account(DEFAULT_CREATE2_DEPLOYER).info; + let info = &data.journaled_state.account(h160_to_b160(DEFAULT_CREATE2_DEPLOYER)).info; match &info.code { Some(code) => { if code.is_empty() { @@ -215,7 +227,7 @@ where } } - call.caller = DEFAULT_CREATE2_DEPLOYER; + call.caller = h160_to_b160(DEFAULT_CREATE2_DEPLOYER); // We have to increment the nonce of the user address, since this create2 will be done // by the create2_deployer @@ -225,6 +237,7 @@ where // Proxy deployer requires the data to be on the following format `salt.init_code` let mut calldata = BytesMut::with_capacity(32 + bytecode.len()); + let salt = ru256_to_u256(salt); let mut salt_bytes = [0u8; 32]; salt.to_big_endian(&mut salt_bytes); calldata.put_slice(&salt_bytes); @@ -327,8 +340,8 @@ pub fn check_if_fixed_gas_limit( // time of the call, which should be rather close to configured gas limit. // TODO: Find a way to reliably make this determination. (for example by // generating it in the compilation or evm simulation process) - U256::from(data.env.tx.gas_limit) > data.env.block.gas_limit && - U256::from(call_gas_limit) <= data.env.block.gas_limit + U256::from(data.env.tx.gas_limit) > data.env.block.gas_limit.into() && + U256::from(call_gas_limit) <= data.env.block.gas_limit.into() // Transfers in forge scripts seem to be estimated at 2300 by revm leading to "Intrinsic // gas too low" failure when simulated on chain && call_gas_limit > 2300 diff --git a/evm/src/executor/inspector/chisel_state.rs b/evm/src/executor/inspector/chisel_state.rs index b0b4ca7a9da4d..e10720bba85a2 100644 --- a/evm/src/executor/inspector/chisel_state.rs +++ b/evm/src/executor/inspector/chisel_state.rs @@ -1,4 +1,7 @@ -use revm::{Database, Inspector}; +use revm::{ + interpreter::{InstructionResult, Interpreter, Memory, Stack}, + Database, Inspector, +}; /// An inspector for Chisel #[derive(Default)] @@ -6,7 +9,7 @@ pub struct ChiselState { /// The PC of the final instruction pub final_pc: usize, /// The final state of the REPL contract call - pub state: Option<(revm::Stack, revm::Memory, revm::Return)>, + pub state: Option<(Stack, Memory, InstructionResult)>, } impl ChiselState { @@ -21,11 +24,11 @@ where { fn step_end( &mut self, - interp: &mut revm::Interpreter, + interp: &mut Interpreter, _: &mut revm::EVMData<'_, DB>, _: bool, - eval: revm::Return, - ) -> revm::Return { + eval: InstructionResult, + ) -> InstructionResult { // If we are at the final pc of the REPL contract execution, set the state. if self.final_pc == interp.program_counter() - 1 { self.state = Some((interp.stack().clone(), interp.memory.clone(), eval)) diff --git a/evm/src/executor/inspector/coverage.rs b/evm/src/executor/inspector/coverage.rs index d447c14cea499..d3fa0fd812f45 100644 --- a/evm/src/executor/inspector/coverage.rs +++ b/evm/src/executor/inspector/coverage.rs @@ -1,6 +1,12 @@ -use crate::coverage::{HitMap, HitMaps}; +use crate::{ + coverage::{HitMap, HitMaps}, + utils::b256_to_h256, +}; use bytes::Bytes; -use revm::{Database, EVMData, Inspector, Interpreter, Return}; +use revm::{ + interpreter::{InstructionResult, Interpreter}, + Database, EVMData, Inspector, +}; #[derive(Default, Debug)] pub struct CoverageCollector { @@ -17,14 +23,14 @@ where interpreter: &mut Interpreter, _: &mut EVMData<'_, DB>, _: bool, - ) -> Return { - self.maps.entry(interpreter.contract.bytecode.hash()).or_insert_with(|| { + ) -> InstructionResult { + self.maps.entry(b256_to_h256(interpreter.contract.bytecode.hash())).or_insert_with(|| { HitMap::new(Bytes::copy_from_slice( interpreter.contract.bytecode.original_bytecode_slice(), )) }); - Return::Continue + InstructionResult::Continue } fn step( @@ -32,11 +38,11 @@ where interpreter: &mut Interpreter, _: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { + ) -> InstructionResult { self.maps - .entry(interpreter.contract.bytecode.hash()) + .entry(b256_to_h256(interpreter.contract.bytecode.hash())) .and_modify(|map| map.hit(interpreter.program_counter())); - Return::Continue + InstructionResult::Continue } } diff --git a/evm/src/executor/inspector/debugger.rs b/evm/src/executor/inspector/debugger.rs index 3c8b6cac8f9a2..fe6b255148374 100644 --- a/evm/src/executor/inspector/debugger.rs +++ b/evm/src/executor/inspector/debugger.rs @@ -6,13 +6,19 @@ use crate::{ inspector::utils::{gas_used, get_create_address}, CHEATCODE_ADDRESS, }, + utils::b160_to_h160, CallKind, }; use bytes::Bytes; use ethers::types::Address; use revm::{ - opcode, spec_opcode_gas, CallInputs, CreateInputs, EVMData, Gas, GasInspector, Inspector, - Interpreter, Memory, Return, + inspectors::GasInspector, + interpreter::{ + opcode::{self, spec_opcode_gas}, + CallInputs, CreateInputs, Gas, InstructionResult, Interpreter, Memory, + }, + primitives::B160, + EVMData, Inspector, }; use std::{cell::RefCell, rc::Rc}; @@ -65,7 +71,7 @@ where interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { + ) -> InstructionResult { let pc = interpreter.program_counter(); let op = interpreter.contract.bytecode.bytecode()[pc]; @@ -92,14 +98,14 @@ where self.arena.arena[self.head].steps.push(DebugStep { pc, - stack: interpreter.stack().data().clone(), + stack: interpreter.stack().data().iter().copied().map(|d| d.into()).collect(), memory: interpreter.memory.clone(), instruction: Instruction::OpCode(op), push_bytes, total_gas_used, }); - Return::Continue + InstructionResult::Continue } fn call( @@ -107,13 +113,13 @@ where data: &mut EVMData<'_, DB>, call: &mut CallInputs, _: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { self.enter( data.journaled_state.depth() as usize, - call.context.code_address, + b160_to_h160(call.context.code_address), call.context.scheme.into(), ); - if call.contract == CHEATCODE_ADDRESS { + if CHEATCODE_ADDRESS == b160_to_h160(call.contract) { self.arena.arena[self.head].steps.push(DebugStep { memory: Memory::new(), instruction: Instruction::Cheatcode( @@ -123,7 +129,7 @@ where }); } - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) } fn call_end( @@ -131,10 +137,10 @@ where _: &mut EVMData<'_, DB>, _: &CallInputs, gas: Gas, - status: Return, + status: InstructionResult, retdata: Bytes, _: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { self.exit(); (status, gas, retdata) @@ -144,10 +150,10 @@ where &mut self, data: &mut EVMData<'_, DB>, call: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { // TODO: Does this increase gas cost? if let Err(err) = data.journaled_state.load_account(call.caller, data.db) { - return (Return::Revert, None, Gas::new(call.gas_limit), err.encode_string()) + return (InstructionResult::Revert, None, Gas::new(call.gas_limit), err.encode_string()) } let nonce = data.journaled_state.account(call.caller).info.nonce; @@ -157,18 +163,18 @@ where CallKind::Create, ); - (Return::Continue, None, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()) } fn create_end( &mut self, _: &mut EVMData<'_, DB>, _: &CreateInputs, - status: Return, - address: Option
, + status: InstructionResult, + address: Option, gas: Gas, retdata: Bytes, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { self.exit(); (status, address, gas, retdata) diff --git a/evm/src/executor/inspector/fuzzer.rs b/evm/src/executor/inspector/fuzzer.rs index c11e08ee5ff92..3a4762c5ad6f9 100644 --- a/evm/src/executor/inspector/fuzzer.rs +++ b/evm/src/executor/inspector/fuzzer.rs @@ -1,9 +1,12 @@ use crate::{ fuzz::{invariant::RandomCallGenerator, strategies::EvmFuzzState}, - utils, + utils::{self, b160_to_h160, h160_to_b160}, }; use bytes::Bytes; -use revm::{db::Database, CallInputs, CallScheme, EVMData, Gas, Inspector, Interpreter, Return}; +use revm::{ + interpreter::{CallInputs, CallScheme, Gas, InstructionResult, Interpreter}, + Database, EVMData, Inspector, +}; /// An inspector that can fuzz and collect data for that effect. #[derive(Clone, Debug)] @@ -25,13 +28,13 @@ where interpreter: &mut Interpreter, _: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { + ) -> InstructionResult { // We only collect `stack` and `memory` data before and after calls. if self.collect { self.collect_data(interpreter); self.collect = false; } - Return::Continue + InstructionResult::Continue } fn call( @@ -39,7 +42,7 @@ where data: &mut EVMData<'_, DB>, call: &mut CallInputs, _: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { // We don't want to override the very first call made to the test contract. if self.call_generator.is_some() && data.env.tx.caller != call.context.caller { self.override_call(call); @@ -49,7 +52,7 @@ where // this will be turned off on the next `step` self.collect = true; - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) } fn call_end( @@ -57,10 +60,10 @@ where _: &mut EVMData<'_, DB>, _: &CallInputs, remaining_gas: Gas, - status: Return, + status: InstructionResult, retdata: Bytes, _: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { if let Some(ref mut call_generator) = self.call_generator { call_generator.used = false; } @@ -79,7 +82,7 @@ impl Fuzzer { let mut state = self.fuzz_state.write(); for slot in interpreter.stack().data() { - state.values_mut().insert(utils::u256_to_h256_be(*slot).into()); + state.values_mut().insert(utils::u256_to_h256_be(utils::ru256_to_u256(*slot)).into()); } // TODO: disabled for now since it's flooding the dictionary @@ -95,21 +98,21 @@ impl Fuzzer { fn override_call(&mut self, call: &mut CallInputs) { if let Some(ref mut call_generator) = self.call_generator { // We only override external calls which are not coming from the test contract. - if call.context.caller != call_generator.test_address && + if call.context.caller != h160_to_b160(call_generator.test_address) && call.context.scheme == CallScheme::Call && !call_generator.used { // There's only a 30% chance that an override happens. - if let Some((sender, (contract, input))) = - call_generator.next(call.context.caller, call.contract) + if let Some((sender, (contract, input))) = call_generator + .next(b160_to_h160(call.context.caller), b160_to_h160(call.contract)) { call.input = input.0; - call.context.caller = sender; - call.contract = contract; + call.context.caller = h160_to_b160(sender); + call.contract = h160_to_b160(contract); // TODO: in what scenarios can the following be problematic - call.context.code_address = contract; - call.context.address = contract; + call.context.code_address = h160_to_b160(contract); + call.context.address = h160_to_b160(contract); call_generator.used = true; } diff --git a/evm/src/executor/inspector/logs.rs b/evm/src/executor/inspector/logs.rs index 79c861dede8da..5c92d2a355a53 100644 --- a/evm/src/executor/inspector/logs.rs +++ b/evm/src/executor/inspector/logs.rs @@ -1,13 +1,18 @@ -use crate::executor::{ - patch_hardhat_console_selector, HardhatConsoleCalls, HARDHAT_CONSOLE_ADDRESS, +use crate::{ + executor::{patch_hardhat_console_selector, HardhatConsoleCalls, HARDHAT_CONSOLE_ADDRESS}, + utils::{b160_to_h160, b256_to_h256, h160_to_b160}, }; use bytes::Bytes; use ethers::{ abi::{AbiDecode, Token}, - types::{Address, Log, H256}, + types::{Log, H256}, }; use foundry_macros::ConsoleFmt; -use revm::{db::Database, CallInputs, EVMData, Gas, Inspector, Return}; +use revm::{ + interpreter::{CallInputs, Gas, InstructionResult}, + primitives::{B160, B256}, + Database, EVMData, Inspector, +}; /// An inspector that collects logs during execution. /// @@ -18,14 +23,14 @@ pub struct LogCollector { } impl LogCollector { - fn hardhat_log(&mut self, mut input: Vec) -> (Return, Bytes) { + fn hardhat_log(&mut self, mut input: Vec) -> (InstructionResult, Bytes) { // Patch the Hardhat-style selectors patch_hardhat_console_selector(&mut input); let decoded = match HardhatConsoleCalls::decode(input) { Ok(inner) => inner, Err(err) => { return ( - Return::Revert, + InstructionResult::Revert, ethers::abi::encode(&[Token::String(err.to_string())]).into(), ) } @@ -34,7 +39,7 @@ impl LogCollector { // Convert it to a DS-style `emit log(string)` event self.logs.push(convert_hh_log_to_event(decoded)); - (Return::Continue, Bytes::new()) + (InstructionResult::Continue, Bytes::new()) } } @@ -42,10 +47,10 @@ impl Inspector for LogCollector where DB: Database, { - fn log(&mut self, _: &mut EVMData<'_, DB>, address: &Address, topics: &[H256], data: &Bytes) { + fn log(&mut self, _: &mut EVMData<'_, DB>, address: &B160, topics: &[B256], data: &Bytes) { self.logs.push(Log { - address: *address, - topics: topics.to_vec(), + address: b160_to_h160(*address), + topics: topics.iter().copied().map(b256_to_h256).collect(), data: data.clone().into(), ..Default::default() }); @@ -56,12 +61,12 @@ where _: &mut EVMData<'_, DB>, call: &mut CallInputs, _: bool, - ) -> (Return, Gas, Bytes) { - if call.contract == HARDHAT_CONSOLE_ADDRESS { + ) -> (InstructionResult, Gas, Bytes) { + if call.contract == h160_to_b160(HARDHAT_CONSOLE_ADDRESS) { let (status, reason) = self.hardhat_log(call.input.to_vec()); (status, Gas::new(call.gas_limit), reason) } else { - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) } } } diff --git a/evm/src/executor/inspector/mod.rs b/evm/src/executor/inspector/mod.rs index 2a65efa33cafd..74ab7da036d0c 100644 --- a/evm/src/executor/inspector/mod.rs +++ b/evm/src/executor/inspector/mod.rs @@ -29,7 +29,7 @@ pub use chisel_state::ChiselState; use ethers::types::U256; -use revm::{BlockEnv, GasInspector}; +use revm::{inspectors::GasInspector, primitives::BlockEnv}; mod fuzzer; pub use fuzzer::Fuzzer; diff --git a/evm/src/executor/inspector/printer.rs b/evm/src/executor/inspector/printer.rs index 631c84efa17c9..b9a1abc7f19d6 100644 --- a/evm/src/executor/inspector/printer.rs +++ b/evm/src/executor/inspector/printer.rs @@ -1,8 +1,9 @@ use bytes::Bytes; -use ethers::types::Address; use revm::{ - opcode::{self}, - CallInputs, CreateInputs, Database, EVMData, Gas, GasInspector, Inspector, Return, + inspectors::GasInspector, + interpreter::{opcode, CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, + primitives::B160, + Database, EVMData, Inspector, }; #[derive(Clone, Default)] @@ -13,22 +14,22 @@ pub struct TracePrinter { impl Inspector for TracePrinter { fn initialize_interp( &mut self, - interp: &mut revm::Interpreter, + interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { self.gas_inspector.initialize_interp(interp, data, is_static); - Return::Continue + InstructionResult::Continue } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. // all other information can be obtained from interp. fn step( &mut self, - interp: &mut revm::Interpreter, + interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { let opcode = interp.current_opcode(); let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; @@ -51,18 +52,18 @@ impl Inspector for TracePrinter { self.gas_inspector.step(interp, data, is_static); - Return::Continue + InstructionResult::Continue } fn step_end( &mut self, - interp: &mut revm::Interpreter, + interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - eval: revm::Return, - ) -> Return { + eval: InstructionResult, + ) -> InstructionResult { self.gas_inspector.step_end(interp, data, is_static, eval); - Return::Continue + InstructionResult::Continue } fn call( @@ -70,7 +71,7 @@ impl Inspector for TracePrinter { _data: &mut EVMData<'_, DB>, inputs: &mut CallInputs, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { println!( "SM CALL: {:?},context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}", inputs.contract, @@ -79,7 +80,7 @@ impl Inspector for TracePrinter { inputs.transfer, inputs.input.len(), ); - (Return::Continue, Gas::new(0), Bytes::new()) + (InstructionResult::Continue, Gas::new(0), Bytes::new()) } fn call_end( @@ -87,10 +88,10 @@ impl Inspector for TracePrinter { data: &mut EVMData<'_, DB>, inputs: &CallInputs, remaining_gas: Gas, - ret: Return, + ret: InstructionResult, out: Bytes, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { self.gas_inspector.call_end(data, inputs, remaining_gas, ret, out.clone(), is_static); (ret, remaining_gas, out) } @@ -99,7 +100,7 @@ impl Inspector for TracePrinter { &mut self, _data: &mut EVMData<'_, DB>, inputs: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { println!( "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", inputs.caller, @@ -108,18 +109,18 @@ impl Inspector for TracePrinter { hex::encode(&inputs.init_code), inputs.gas_limit ); - (Return::Continue, None, Gas::new(0), Bytes::new()) + (InstructionResult::Continue, None, Gas::new(0), Bytes::new()) } fn create_end( &mut self, data: &mut EVMData<'_, DB>, inputs: &CreateInputs, - ret: Return, - address: Option
, + ret: InstructionResult, + address: Option, remaining_gas: Gas, out: Bytes, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { self.gas_inspector.create_end(data, inputs, ret, address, remaining_gas, out.clone()); (ret, address, remaining_gas, out) } diff --git a/evm/src/executor/inspector/stack.rs b/evm/src/executor/inspector/stack.rs index 1f0f9414d645e..898ca459737fc 100644 --- a/evm/src/executor/inspector/stack.rs +++ b/evm/src/executor/inspector/stack.rs @@ -8,11 +8,15 @@ use crate::{ use bytes::Bytes; use ethers::{ signers::LocalWallet, - types::{Address, Log, H256}, + types::{Address, Log}, }; use revm::{ - return_revert, CallInputs, CreateInputs, EVMData, Gas, GasInspector, Inspector, Interpreter, - Return, + inspectors::GasInspector, + interpreter::{ + return_revert, CallInputs, CreateInputs, Gas, InstructionResult, Interpreter, Memory, Stack, + }, + primitives::{B160, B256}, + EVMData, Inspector, }; use std::{cell::RefCell, collections::BTreeMap, rc::Rc}; @@ -35,15 +39,16 @@ pub struct InspectorData { pub traces: Option, pub debug: Option, pub coverage: Option, + pub gas: Option, pub cheatcodes: Option, pub script_wallets: Vec, - pub chisel_state: Option<(revm::Stack, revm::Memory, revm::Return)>, + pub chisel_state: Option<(Stack, Memory, InstructionResult)>, } /// An inspector that calls multiple inspectors in sequence. /// -/// If a call to an inspector returns a value other than [Return::Continue] (or equivalent) the -/// remaining inspectors are not called. +/// If a call to an inspector returns a value other than [InstructionResult::Continue] (or +/// equivalent) the remaining inspectors are not called. #[derive(Default)] pub struct InspectorStack { pub tracer: Option, @@ -69,6 +74,7 @@ impl InspectorStack { traces: self.tracer.map(|tracer| tracer.traces), debug: self.debugger.map(|debugger| debugger.arena), coverage: self.coverage.map(|coverage| coverage.maps), + gas: self.gas.map(|gas| gas.borrow().gas_remaining()), script_wallets: self .cheatcodes .as_ref() @@ -84,10 +90,10 @@ impl InspectorStack { data: &mut EVMData<'_, DB>, call: &CallInputs, remaining_gas: Gas, - status: Return, + status: InstructionResult, retdata: Bytes, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { call_inspectors!( inspector, [ @@ -112,7 +118,8 @@ impl InspectorStack { // If the inspector returns a different status or a revert with a non-empty message, // we assume it wants to tell us something - if new_status != status || (new_status == Return::Revert && new_retdata != retdata) + if new_status != status || + (new_status == InstructionResult::Revert && new_retdata != retdata) { return (new_status, new_gas, new_retdata) } @@ -132,7 +139,7 @@ where interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { call_inspectors!( inspector, [ @@ -148,13 +155,13 @@ where let status = inspector.initialize_interp(interpreter, data, is_static); // Allow inspectors to exit early - if status != Return::Continue { + if status != InstructionResult::Continue { return status } } ); - Return::Continue + InstructionResult::Continue } fn step( @@ -162,7 +169,7 @@ where interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { call_inspectors!( inspector, [ @@ -179,20 +186,20 @@ where let status = inspector.step(interpreter, data, is_static); // Allow inspectors to exit early - if status != Return::Continue { + if status != InstructionResult::Continue { return status } } ); - Return::Continue + InstructionResult::Continue } fn log( &mut self, evm_data: &mut EVMData<'_, DB>, - address: &Address, - topics: &[H256], + address: &B160, + topics: &[B256], data: &Bytes, ) { call_inspectors!( @@ -209,8 +216,8 @@ where interpreter: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - status: Return, - ) -> Return { + status: InstructionResult, + ) -> InstructionResult { call_inspectors!( inspector, [ @@ -226,13 +233,13 @@ where let status = inspector.step_end(interpreter, data, is_static, status); // Allow inspectors to exit early - if status != Return::Continue { + if status != InstructionResult::Continue { return status } } ); - Return::Continue + InstructionResult::Continue } fn call( @@ -240,7 +247,7 @@ where data: &mut EVMData<'_, DB>, call: &mut CallInputs, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { call_inspectors!( inspector, [ @@ -257,13 +264,13 @@ where let (status, gas, retdata) = inspector.call(data, call, is_static); // Allow inspectors to exit early - if status != Return::Continue { + if status != InstructionResult::Continue { return (status, gas, retdata) } } ); - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(call.gas_limit), Bytes::new()) } fn call_end( @@ -271,10 +278,10 @@ where data: &mut EVMData<'_, DB>, call: &CallInputs, remaining_gas: Gas, - status: Return, + status: InstructionResult, retdata: Bytes, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { let res = self.do_call_end(data, call, remaining_gas, status, retdata, is_static); if matches!(res.0, return_revert!()) { @@ -293,7 +300,7 @@ where &mut self, data: &mut EVMData<'_, DB>, call: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { call_inspectors!( inspector, [ @@ -309,24 +316,24 @@ where let (status, addr, gas, retdata) = inspector.create(data, call); // Allow inspectors to exit early - if status != Return::Continue { + if status != InstructionResult::Continue { return (status, addr, gas, retdata) } } ); - (Return::Continue, None, Gas::new(call.gas_limit), Bytes::new()) + (InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new()) } fn create_end( &mut self, data: &mut EVMData<'_, DB>, call: &CreateInputs, - status: Return, - address: Option
, + status: InstructionResult, + address: Option, remaining_gas: Gas, retdata: Bytes, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { call_inspectors!( inspector, [ @@ -357,7 +364,7 @@ where (status, address, remaining_gas, retdata) } - fn selfdestruct(&mut self) { + fn selfdestruct(&mut self, contract: B160, target: B160) { call_inspectors!( inspector, [ @@ -369,7 +376,7 @@ where &mut self.chisel_state ], { - Inspector::::selfdestruct(inspector); + Inspector::::selfdestruct(inspector, contract, target); } ); } diff --git a/evm/src/executor/inspector/tracer.rs b/evm/src/executor/inspector/tracer.rs index 9164ad0ac86b5..6a6404c26b4bc 100644 --- a/evm/src/executor/inspector/tracer.rs +++ b/evm/src/executor/inspector/tracer.rs @@ -5,16 +5,22 @@ use crate::{ CallTrace, CallTraceArena, CallTraceStep, LogCallOrder, RawOrDecodedCall, RawOrDecodedLog, RawOrDecodedReturnData, }, + utils::{b160_to_h160, b256_to_h256, ru256_to_u256}, CallKind, }; use bytes::Bytes; use ethers::{ abi::RawLog, - types::{Address, H256, U256}, + types::{Address, U256}, }; use revm::{ - opcode, return_ok, CallInputs, CallScheme, CreateInputs, Database, EVMData, Gas, GasInspector, - Inspector, Interpreter, JournalEntry, Return, + inspectors::GasInspector, + interpreter::{ + opcode, return_ok, CallInputs, CallScheme, CreateInputs, Gas, InstructionResult, + Interpreter, + }, + primitives::{B160, B256}, + Database, EVMData, Inspector, JournalEntry, }; use std::{cell::RefCell, rc::Rc}; @@ -58,14 +64,20 @@ impl Tracer { kind, data: RawOrDecodedCall::Raw(data.into()), value, - status: Return::Continue, + status: InstructionResult::Continue, caller, ..Default::default() }, )); } - fn fill_trace(&mut self, status: Return, cost: u64, output: Vec, address: Option
) { + fn fill_trace( + &mut self, + status: InstructionResult, + cost: u64, + output: Vec, + address: Option
, + ) { let success = matches!(status, return_ok!()); let trace = &mut self.traces.arena [self.trace_stack.pop().expect("more traces were filled than started")] @@ -93,7 +105,7 @@ impl Tracer { depth: data.journaled_state.depth(), pc, op: OpCode(interp.contract.bytecode.bytecode()[pc]), - contract: interp.contract.address, + contract: b160_to_h160(interp.contract.address), stack: interp.stack.clone(), memory: interp.memory.clone(), gas: self.gas_inspector.borrow().gas_remaining(), @@ -108,7 +120,7 @@ impl Tracer { &mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>, - status: Return, + status: InstructionResult, ) { let (trace_idx, step_idx) = self.step_stack.pop().expect("can't fill step without starting a step first"); @@ -128,10 +140,10 @@ impl Tracer { step.state_diff = match (op, journal_entry) { ( opcode::SLOAD | opcode::SSTORE, - Some(JournalEntry::StorageChage { address, key, .. }), + Some(JournalEntry::StorageChange { address, key, .. }), ) => { let value = data.journaled_state.state[address].storage[key].present_value(); - Some((*key, value)) + Some((ru256_to_u256(*key), value.into())) } _ => None, }; @@ -140,7 +152,7 @@ impl Tracer { } // Error codes only - if status as u8 > Return::OutOfGas as u8 { + if status as u8 > InstructionResult::OutOfGas as u8 { step.error = Some(format!("{status:?}")); } } @@ -155,21 +167,21 @@ where interp: &mut Interpreter, data: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { + ) -> InstructionResult { if !self.record_steps { - return Return::Continue + return InstructionResult::Continue } self.start_step(interp, data); - Return::Continue + InstructionResult::Continue } - fn log(&mut self, _: &mut EVMData<'_, DB>, _: &Address, topics: &[H256], data: &Bytes) { + fn log(&mut self, _: &mut EVMData<'_, DB>, _: &B160, topics: &[B256], data: &Bytes) { let node = &mut self.traces.arena[*self.trace_stack.last().expect("no ongoing trace")]; + let topics: Vec<_> = topics.iter().copied().map(b256_to_h256).collect(); node.ordering.push(LogCallOrder::Log(node.logs.len())); - node.logs - .push(RawOrDecodedLog::Raw(RawLog { topics: topics.to_vec(), data: data.to_vec() })); + node.logs.push(RawOrDecodedLog::Raw(RawLog { topics, data: data.to_vec() })); } fn step_end( @@ -177,10 +189,10 @@ where interp: &mut Interpreter, data: &mut EVMData<'_, DB>, _is_static: bool, - status: Return, - ) -> Return { + status: InstructionResult, + ) -> InstructionResult { if !self.record_steps { - return Return::Continue + return InstructionResult::Continue } self.fill_step(interp, data, status); @@ -193,7 +205,7 @@ where data: &mut EVMData<'_, DB>, inputs: &mut CallInputs, _is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { let (from, to) = match inputs.context.scheme { CallScheme::DelegateCall | CallScheme::CallCode => { (inputs.context.address, inputs.context.code_address) @@ -203,14 +215,14 @@ where self.start_trace( data.journaled_state.depth() as usize, - to, + b160_to_h160(to), inputs.input.to_vec(), - inputs.transfer.value, + inputs.transfer.value.into(), inputs.context.scheme.into(), - from, + b160_to_h160(from), ); - (Return::Continue, Gas::new(inputs.gas_limit), Bytes::new()) + (InstructionResult::Continue, Gas::new(inputs.gas_limit), Bytes::new()) } fn call_end( @@ -218,10 +230,10 @@ where data: &mut EVMData<'_, DB>, _inputs: &CallInputs, gas: Gas, - status: Return, + status: InstructionResult, retdata: Bytes, _is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { self.fill_trace( status, gas_used(data.env.cfg.spec_id, gas.spend(), gas.refunded() as u64), @@ -236,7 +248,7 @@ where &mut self, data: &mut EVMData<'_, DB>, inputs: &mut CreateInputs, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { // TODO: Does this increase gas cost? let _ = data.journaled_state.load_account(inputs.caller, data.db); let nonce = data.journaled_state.account(inputs.caller).info.nonce; @@ -244,23 +256,23 @@ where data.journaled_state.depth() as usize, get_create_address(inputs, nonce), inputs.init_code.to_vec(), - inputs.value, + inputs.value.into(), inputs.scheme.into(), - inputs.caller, + b160_to_h160(inputs.caller), ); - (Return::Continue, None, Gas::new(inputs.gas_limit), Bytes::new()) + (InstructionResult::Continue, None, Gas::new(inputs.gas_limit), Bytes::new()) } fn create_end( &mut self, data: &mut EVMData<'_, DB>, _inputs: &CreateInputs, - status: Return, - address: Option
, + status: InstructionResult, + address: Option, gas: Gas, retdata: Bytes, - ) -> (Return, Option
, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { let code = match address { Some(address) => data .journaled_state @@ -275,7 +287,7 @@ where status, gas_used(data.env.cfg.spec_id, gas.spend(), gas.refunded() as u64), code, - address, + address.map(b160_to_h160), ); (status, address, gas, retdata) diff --git a/evm/src/executor/inspector/utils.rs b/evm/src/executor/inspector/utils.rs index 5ea3a39e87321..22fecc6b7b05d 100644 --- a/evm/src/executor/inspector/utils.rs +++ b/evm/src/executor/inspector/utils.rs @@ -2,9 +2,14 @@ use ethers::{ types::Address, utils::{get_contract_address, get_create2_address}, }; -use revm::{CreateInputs, CreateScheme, SpecId}; +use revm::{ + interpreter::CreateInputs, + primitives::{CreateScheme, SpecId}, +}; + +use crate::utils::{b160_to_h160, ru256_to_u256}; -/// Returns [Return::Continue] on an error, discarding the error. +/// Returns [InstructionResult::Continue] on an error, discarding the error. /// /// Useful for inspectors that read state that might be invalid, but do not want to emit /// appropriate errors themselves, instead opting to continue. @@ -12,7 +17,7 @@ macro_rules! try_or_continue { ($e:expr) => { match $e { Ok(v) => v, - Err(_) => return Return::Continue, + Err(_) => return InstructionResult::Continue, } }; } @@ -20,11 +25,12 @@ macro_rules! try_or_continue { /// Get the address of a contract creation pub fn get_create_address(call: &CreateInputs, nonce: u64) -> Address { match call.scheme { - CreateScheme::Create => get_contract_address(call.caller, nonce), + CreateScheme::Create => get_contract_address(b160_to_h160(call.caller), nonce), CreateScheme::Create2 { salt } => { - let mut buffer: [u8; 4 * 8] = [0; 4 * 8]; - salt.to_big_endian(&mut buffer); - get_create2_address(call.caller, buffer, call.init_code.clone()) + let salt = ru256_to_u256(salt); + let mut salt_bytes = [0u8; 32]; + salt.to_big_endian(&mut salt_bytes); + get_create2_address(b160_to_h160(call.caller), salt_bytes, call.init_code.clone()) } } } diff --git a/evm/src/executor/mod.rs b/evm/src/executor/mod.rs index 1ee25c1a4a87b..06fff974e2663 100644 --- a/evm/src/executor/mod.rs +++ b/evm/src/executor/mod.rs @@ -1,7 +1,13 @@ use self::inspector::{ cheatcodes::util::BroadcastableTransactions, Cheatcodes, InspectorData, InspectorStackConfig, }; -use crate::{debug::DebugArena, decode, trace::CallTraceArena, CALLER}; +use crate::{ + debug::DebugArena, + decode, + trace::CallTraceArena, + utils::{b160_to_h160, eval_to_instruction_result, h160_to_b160, halt_to_instruction_result}, + CALLER, +}; pub use abi::{ patch_hardhat_console_selector, HardhatConsoleCalls, CHEATCODE_ADDRESS, CONSOLE_ABI, HARDHAT_CONSOLE_ABI, HARDHAT_CONSOLE_ADDRESS, @@ -16,12 +22,16 @@ use ethers::{ }; use foundry_common::{abi::IntoFunction, evm::Breakpoints}; use hashbrown::HashMap; -use revm::{ - db::DatabaseCommit, return_ok, Account, BlockEnv, Bytecode, CreateScheme, ExecutionResult, - Return, TransactOut, TransactTo, TxEnv, -}; /// Reexport commonly used revm types -pub use revm::{db::DatabaseRef, Env, SpecId}; +pub use revm::primitives::{Env, SpecId}; +pub use revm::{ + db::{DatabaseCommit, DatabaseRef}, + interpreter::{return_ok, CreateScheme, InstructionResult, Memory, Stack}, + primitives::{ + Account, BlockEnv, Bytecode, ExecutionResult, Output, ResultAndState, TransactTo, TxEnv, + B160, U256 as rU256, + }, +}; use std::collections::BTreeMap; use tracing::trace; @@ -53,7 +63,7 @@ use crate::{ pub use builder::ExecutorBuilder; /// A mapping of addresses to their changed state. -pub type StateChangeset = HashMap; +pub type StateChangeset = HashMap; /// A type that can execute calls /// @@ -92,7 +102,7 @@ impl Executor { // does not fail backend.insert_account_info( CHEATCODE_ADDRESS, - revm::AccountInfo { + revm::primitives::AccountInfo { code: Some(Bytecode::new_raw(vec![0u8].into()).to_checked()), ..Default::default() }, @@ -135,7 +145,7 @@ impl Executor { trace!("deploying local create2 deployer"); let create2_deployer_account = self .backend_mut() - .basic(DEFAULT_CREATE2_DEPLOYER)? + .basic(h160_to_b160(DEFAULT_CREATE2_DEPLOYER))? .ok_or(DatabaseError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?; if create2_deployer_account.code.is_none() || @@ -163,8 +173,8 @@ impl Executor { /// Set the balance of an account. pub fn set_balance(&mut self, address: Address, amount: U256) -> DatabaseResult<&mut Self> { trace!(?address, ?amount, "setting account balance"); - let mut account = self.backend_mut().basic(address)?.unwrap_or_default(); - account.balance = amount; + let mut account = self.backend_mut().basic(h160_to_b160(address))?.unwrap_or_default(); + account.balance = amount.into(); self.backend_mut().insert_account_info(address, account); Ok(self) @@ -172,12 +182,16 @@ impl Executor { /// Gets the balance of an account pub fn get_balance(&self, address: Address) -> DatabaseResult { - Ok(self.backend().basic(address)?.map(|acc| acc.balance).unwrap_or_default()) + Ok(self + .backend() + .basic(h160_to_b160(address))? + .map(|acc| acc.balance.into()) + .unwrap_or_default()) } /// Set the nonce of an account. pub fn set_nonce(&mut self, address: Address, nonce: u64) -> DatabaseResult<&mut Self> { - let mut account = self.backend_mut().basic(address)?.unwrap_or_default(); + let mut account = self.backend_mut().basic(h160_to_b160(address))?.unwrap_or_default(); account.nonce = nonce; self.backend_mut().insert_account_info(address, account); @@ -282,7 +296,7 @@ impl Executor { calldata: Bytes, value: U256, ) -> eyre::Result { - let env = self.build_test_env(from, TransactTo::Call(to), calldata, value); + let env = self.build_test_env(from, TransactTo::Call(h160_to_b160(to)), calldata, value); let mut result = self.call_raw_with_env(env)?; self.commit(&mut result); Ok(result) @@ -302,7 +316,12 @@ impl Executor { let calldata = Bytes::from(encode_function_data(&func, args)?.to_vec()); // execute the call - let env = self.build_test_env(from, TransactTo::Call(test_contract), calldata, value); + let env = self.build_test_env( + from, + TransactTo::Call(h160_to_b160(test_contract)), + calldata, + value, + ); let call_result = self.call_raw_with_env(env)?; convert_call_result(abi, &func, call_result) @@ -339,9 +358,10 @@ impl Executor { // execute the call let mut inspector = self.inspector_config.stack(); // Build VM - let mut env = self.build_test_env(from, TransactTo::Call(to), calldata, value); + let mut env = + self.build_test_env(from, TransactTo::Call(h160_to_b160(to)), calldata, value); let mut db = FuzzBackendWrapper::new(self.backend()); - let result = db.inspect_ref(&mut env, &mut inspector); + let result = db.inspect_ref(&mut env, &mut inspector)?; convert_executed_result(env, inspector, result) } @@ -357,7 +377,7 @@ impl Executor { pub fn call_raw_with_env(&mut self, mut env: Env) -> eyre::Result { // execute the call let mut inspector = self.inspector_config.stack(); - let result = self.backend_mut().inspect_ref(&mut env, &mut inspector); + let result = self.backend_mut().inspect_ref(&mut env, &mut inspector)?; convert_executed_result(env, inspector, result) } @@ -413,13 +433,13 @@ impl Executor { } = result; let result = match out { - TransactOut::Create(ref data, _) => data.to_owned(), + Some(Output::Create(ref data, _)) => data.to_owned(), _ => Bytes::default(), }; let address = match exit_reason { return_ok!() => { - if let TransactOut::Create(_, Some(addr)) = out { + if let Some(Output::Create(_, Some(addr))) = out { addr } else { return Err(EvmError::Execution(Box::new(ExecutionErr { @@ -460,11 +480,19 @@ impl Executor { // also mark this library as persistent, this will ensure that the state of the library is // persistent across fork swaps in forking mode - self.backend.add_persistent_account(address); + self.backend.add_persistent_account(b160_to_h160(address)); trace!(address=?address, "deployed contract"); - Ok(DeployResult { address, gas_used, gas_refunded, logs, traces, debug, env }) + Ok(DeployResult { + address: b160_to_h160(address), + gas_used, + gas_refunded, + logs, + traces, + debug, + env, + }) } /// Deploys a contract and commits the new state to the underlying database. @@ -525,7 +553,7 @@ impl Executor { // we only clone the test contract and cheatcode accounts, that's all we need to evaluate // success for addr in [address, CHEATCODE_ADDRESS] { - let acc = self.backend().basic(addr)?.unwrap_or_default(); + let acc = self.backend().basic(h160_to_b160(addr))?.unwrap_or_default(); backend.insert_account_info(addr, acc); } @@ -567,17 +595,17 @@ impl Executor { // network conditions - the actual gas price is kept in `self.block` and is applied by // the cheatcode handler if it is enabled block: BlockEnv { - basefee: 0.into(), - gas_limit: self.gas_limit, + basefee: rU256::from(0), + gas_limit: self.gas_limit.into(), ..self.env.block.clone() }, tx: TxEnv { - caller, + caller: h160_to_b160(caller), transact_to, data, - value, + value: value.into(), // As above, we set the gas price to 0. - gas_price: 0.into(), + gas_price: rU256::from(0), gas_priority_fee: None, gas_limit: self.gas_limit.as_u64(), ..self.env.tx.clone() @@ -679,7 +707,7 @@ pub struct CallResult { #[derive(Debug)] pub struct RawCallResult { /// The status of the call - pub exit_reason: Return, + pub exit_reason: InstructionResult, /// Whether the call reverted or not pub reverted: bool, /// The raw result of the call @@ -714,15 +742,15 @@ pub struct RawCallResult { /// The cheatcode states after execution pub cheatcodes: Option, /// The raw output of the execution - pub out: TransactOut, + pub out: Option, /// The chisel state - pub chisel_state: Option<(revm::Stack, revm::Memory, revm::Return)>, + pub chisel_state: Option<(Stack, Memory, InstructionResult)>, } impl Default for RawCallResult { fn default() -> Self { Self { - exit_reason: Return::Continue, + exit_reason: InstructionResult::Continue, reverted: false, result: Bytes::new(), gas_used: 0, @@ -738,7 +766,7 @@ impl Default for RawCallResult { script_wallets: Vec::new(), env: Default::default(), cheatcodes: Default::default(), - out: TransactOut::None, + out: None, chisel_state: None, } } @@ -754,15 +782,26 @@ fn calc_stipend(calldata: &[u8], spec: SpecId) -> u64 { fn convert_executed_result( env: Env, inspector: InspectorStack, - result: (ExecutionResult, StateChangeset), + result: ResultAndState, ) -> eyre::Result { - let (exec_result, state_changeset) = result; - let ExecutionResult { exit_reason, gas_refunded, gas_used, out, .. } = exec_result; + let ResultAndState { result: exec_result, state: state_changeset } = result; + let (exit_reason, gas_refunded, gas_used, out) = match exec_result { + ExecutionResult::Success { reason, gas_used, gas_refunded, output, .. } => { + (eval_to_instruction_result(reason), gas_refunded, gas_used, Some(output)) + } + ExecutionResult::Revert { gas_used, output } => { + // Need to fetch the unused gas + (InstructionResult::Revert, 0_u64, gas_used, Some(Output::Call(output))) + } + ExecutionResult::Halt { reason, gas_used } => { + (halt_to_instruction_result(reason), 0_u64, gas_used, None) + } + }; let stipend = calc_stipend(&env.tx.data, env.cfg.spec_id); let result = match out { - TransactOut::Call(ref data) => data.to_owned(), + Some(Output::Call(ref data)) => data.to_owned(), _ => Bytes::default(), }; @@ -770,6 +809,7 @@ fn convert_executed_result( logs, labels, traces, + gas, coverage, debug, cheatcodes, @@ -777,6 +817,7 @@ fn convert_executed_result( chisel_state, } = inspector.collect_inspector_states(); + let gas_refunded = gas.unwrap_or(gas_refunded); let transactions = match cheatcodes.as_ref() { Some(cheats) if !cheats.broadcastable_transactions.is_empty() => { Some(cheats.broadcastable_transactions.clone()) diff --git a/evm/src/executor/opts.rs b/evm/src/executor/opts.rs index df64552c5fd1f..dca77c192fe3f 100644 --- a/evm/src/executor/opts.rs +++ b/evm/src/executor/opts.rs @@ -1,4 +1,7 @@ -use crate::executor::fork::CreateFork; +use crate::{ + executor::fork::CreateFork, + utils::{h160_to_b160, h256_to_b256}, +}; use ethers::{ providers::{Middleware, Provider}, solc::utils::RuntimeOrHandle, @@ -7,7 +10,7 @@ use ethers::{ use eyre::WrapErr; use foundry_common::{self, ProviderBuilder, RpcUrl, ALCHEMY_FREE_TIER_CUPS}; use foundry_config::Config; -use revm::{BlockEnv, CfgEnv, SpecId, TxEnv}; +use revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv, U256 as rU256}; use serde::{Deserialize, Deserializer, Serialize}; use super::fork::environment; @@ -57,7 +60,7 @@ impl EvmOpts { /// /// If a `fork_url` is set, it gets configured with settings fetched from the endpoint (chain /// id, ) - pub async fn evm_env(&self) -> revm::Env { + pub async fn evm_env(&self) -> revm::primitives::Env { if let Some(ref fork_url) = self.fork_url { self.fork_evm_env(fork_url).await.expect("Could not instantiate forked environment").0 } else { @@ -70,7 +73,7 @@ impl EvmOpts { /// This only attaches are creates a temporary tokio runtime if `fork_url` is set /// /// Returns an error if a RPC request failed, or the fork url is not a valid url - pub fn evm_env_blocking(&self) -> eyre::Result { + pub fn evm_env_blocking(&self) -> eyre::Result { if let Some(ref fork_url) = self.fork_url { RuntimeOrHandle::new().block_on(self.fork_evm_env(fork_url)).map(|res| res.0) } else { @@ -83,7 +86,7 @@ impl EvmOpts { pub async fn fork_evm_env( &self, fork_url: impl AsRef, - ) -> eyre::Result<(revm::Env, Block)> { + ) -> eyre::Result<(revm::primitives::Env, Block)> { let fork_url = fork_url.as_ref(); let provider = ProviderBuilder::new(fork_url) .compute_units_per_second(self.get_compute_units_per_second()) @@ -103,19 +106,19 @@ impl EvmOpts { } /// Returns the `revm::Env` configured with only local settings - pub fn local_evm_env(&self) -> revm::Env { - revm::Env { + pub fn local_evm_env(&self) -> revm::primitives::Env { + revm::primitives::Env { block: BlockEnv { - number: self.env.block_number.into(), - coinbase: self.env.block_coinbase, - timestamp: self.env.block_timestamp.into(), - difficulty: self.env.block_difficulty.into(), - prevrandao: Some(self.env.block_prevrandao), - basefee: self.env.block_base_fee_per_gas.into(), - gas_limit: self.gas_limit(), + number: rU256::from(self.env.block_number), + coinbase: h160_to_b160(self.env.block_coinbase), + timestamp: rU256::from(self.env.block_timestamp), + difficulty: rU256::from(self.env.block_difficulty), + prevrandao: Some(h256_to_b256(self.env.block_prevrandao)), + basefee: rU256::from(self.env.block_base_fee_per_gas), + gas_limit: self.gas_limit().into(), }, cfg: CfgEnv { - chain_id: self.env.chain_id.unwrap_or(foundry_common::DEV_CHAIN_ID).into(), + chain_id: rU256::from(self.env.chain_id.unwrap_or(foundry_common::DEV_CHAIN_ID)), spec_id: SpecId::MERGE, limit_contract_code_size: self.env.code_size_limit.or(Some(usize::MAX)), memory_limit: self.memory_limit, @@ -126,9 +129,9 @@ impl EvmOpts { ..Default::default() }, tx: TxEnv { - gas_price: self.env.gas_price.unwrap_or_default().into(), + gas_price: rU256::from(self.env.gas_price.unwrap_or_default()), gas_limit: self.gas_limit().as_u64(), - caller: self.sender, + caller: h160_to_b160(self.sender), ..Default::default() }, } @@ -147,9 +150,9 @@ impl EvmOpts { /// /// for `mainnet` and `--fork-block-number 14435000` on mac the corresponding storage cache will /// be at `~/.foundry/cache/mainnet/14435000/storage.json` - pub fn get_fork(&self, config: &Config, env: revm::Env) -> Option { + pub fn get_fork(&self, config: &Config, env: revm::primitives::Env) -> Option { let url = self.fork_url.clone()?; - let enable_caching = config.enable_caching(&url, env.cfg.chain_id.as_u64()); + let enable_caching = config.enable_caching(&url, env.cfg.chain_id.to::()); Some(CreateFork { url, enable_caching, env, evm_opts: self.clone() }) } diff --git a/evm/src/fuzz/invariant/executor.rs b/evm/src/fuzz/invariant/executor.rs index eafbf02dbfe81..f0658a862e4ff 100644 --- a/evm/src/fuzz/invariant/executor.rs +++ b/evm/src/fuzz/invariant/executor.rs @@ -15,7 +15,7 @@ use crate::{ }, FuzzCase, FuzzedCases, }, - utils::get_function, + utils::{get_function, h160_to_b160}, CALLER, }; use ethers::{ @@ -31,7 +31,7 @@ use proptest::{ strategy::{BoxedStrategy, Strategy, ValueTree}, test_runner::{TestCaseError, TestRunner}, }; -use revm::DatabaseCommit; +use revm::{primitives::B160, DatabaseCommit}; use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; use tracing::warn; @@ -521,7 +521,7 @@ impl<'a> InvariantExecutor<'a> { /// before inserting it into the dictionary. Otherwise, we flood the dictionary with /// randomly generated addresses. fn collect_data( - state_changeset: &mut HashMap, + state_changeset: &mut HashMap, sender: &Address, call_result: &RawCallResult, fuzz_state: EvmFuzzState, @@ -529,7 +529,8 @@ fn collect_data( ) { // Verify it has no code. let mut has_code = false; - if let Some(Some(code)) = state_changeset.get(sender).map(|account| account.info.code.as_ref()) + if let Some(Some(code)) = + state_changeset.get(&h160_to_b160(*sender)).map(|account| account.info.code.as_ref()) { has_code = !code.is_empty(); } @@ -537,14 +538,14 @@ fn collect_data( // We keep the nonce changes to apply later. let mut sender_changeset = None; if !has_code { - sender_changeset = state_changeset.remove(sender); + sender_changeset = state_changeset.remove(&h160_to_b160(*sender)); } collect_state_from_call(&call_result.logs, &*state_changeset, fuzz_state, config); // Re-add changes if let Some(changed) = sender_changeset { - state_changeset.insert(*sender, changed); + state_changeset.insert(h160_to_b160(*sender), changed); } } diff --git a/evm/src/fuzz/strategies/state.rs b/evm/src/fuzz/strategies/state.rs index fa55fe3f65945..c3f9f9b664ab6 100644 --- a/evm/src/fuzz/strategies/state.rs +++ b/evm/src/fuzz/strategies/state.rs @@ -2,7 +2,7 @@ use super::fuzz_param_from_state; use crate::{ executor::StateChangeset, fuzz::invariant::{ArtifactFilters, FuzzRunIdentifiedContracts}, - utils::{self}, + utils::{self, b160_to_h160, ru256_to_u256}, }; use bytes::Bytes; use ethers::{ @@ -16,7 +16,8 @@ use parking_lot::RwLock; use proptest::prelude::{BoxedStrategy, Strategy}; use revm::{ db::{CacheDB, DatabaseRef}, - opcode, spec_opcode_gas, SpecId, + interpreter::opcode::{self, spec_opcode_gas}, + primitives::SpecId, }; use std::{io::Write, sync::Arc}; @@ -91,13 +92,14 @@ pub fn build_initial_state( let mut state = FuzzDictionary::default(); for (address, account) in db.accounts.iter() { + let address: Address = (*address).into(); // Insert basic account information - state.values_mut().insert(H256::from(*address).into()); + state.values_mut().insert(H256::from(address).into()); // Insert push bytes if config.include_push_bytes { if let Some(code) = &account.info.code { - if state.addresses_mut().insert(*address) { + if state.addresses_mut().insert(address) { for push_byte in collect_push_bytes(code.bytes().clone()) { state.values_mut().insert(push_byte); } @@ -108,8 +110,10 @@ pub fn build_initial_state( if config.include_storage { // Insert storage for (slot, value) in &account.storage { - state.values_mut().insert(utils::u256_to_h256_be(*slot).into()); - state.values_mut().insert(utils::u256_to_h256_be(*value).into()); + let slot = (*slot).into(); + let value = (*value).into(); + state.values_mut().insert(utils::u256_to_h256_be(slot).into()); + state.values_mut().insert(utils::u256_to_h256_be(value).into()); } } } @@ -136,13 +140,13 @@ pub fn collect_state_from_call( for (address, account) in state_changeset { // Insert basic account information - state.values_mut().insert(H256::from(*address).into()); + state.values_mut().insert(H256::from(b160_to_h160(*address)).into()); if config.include_push_bytes && state.addresses.len() < config.max_fuzz_dictionary_addresses { // Insert push bytes if let Some(code) = &account.info.code { - if state.addresses_mut().insert(*address) { + if state.addresses_mut().insert(b160_to_h160(*address)) { for push_byte in collect_push_bytes(code.bytes().clone()) { state.values_mut().insert(push_byte); } @@ -153,8 +157,10 @@ pub fn collect_state_from_call( if config.include_storage && state.state_values.len() < config.max_fuzz_dictionary_values { // Insert storage for (slot, value) in &account.storage { - state.values_mut().insert(utils::u256_to_h256_be(*slot).into()); - state.values_mut().insert(utils::u256_to_h256_be(value.present_value()).into()); + let slot = (*slot).into(); + let value = ru256_to_u256(value.present_value()); + state.values_mut().insert(utils::u256_to_h256_be(slot).into()); + state.values_mut().insert(utils::u256_to_h256_be(value).into()); } } else { return @@ -227,7 +233,7 @@ pub fn collect_created_contracts( let mut writable_targeted = targeted_contracts.lock(); for (address, account) in state_changeset { - if !setup_contracts.contains_key(address) { + if !setup_contracts.contains_key(&b160_to_h160(*address)) { if let (true, Some(code)) = (&account.is_touched, &account.info.code) { if !code.is_empty() { if let Some((artifact, (abi, _))) = project_contracts.find_by_code(code.bytes()) @@ -235,9 +241,11 @@ pub fn collect_created_contracts( if let Some(functions) = artifact_filters.get_targeted_functions(artifact, abi)? { - created_contracts.push(*address); - writable_targeted - .insert(*address, (artifact.name.clone(), abi.clone(), functions)); + created_contracts.push(b160_to_h160(*address)); + writable_targeted.insert( + b160_to_h160(*address), + (artifact.name.clone(), abi.clone(), functions), + ); } } } diff --git a/evm/src/lib.rs b/evm/src/lib.rs index e9a7c81fd213a..733791d82f194 100644 --- a/evm/src/lib.rs +++ b/evm/src/lib.rs @@ -29,7 +29,7 @@ pub mod error; pub use ethers::types::Address; pub use hashbrown::{self, HashMap}; pub use revm; -use revm::{CallScheme, CreateScheme}; +use revm::interpreter::{CallScheme, CreateScheme}; use serde::{Deserialize, Serialize}; /// Stores the caller address to be used as _sender_ account for: diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index 1767e6f1c14a7..9f276479be3b4 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -10,7 +10,7 @@ use ethers::{ use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use hashbrown::HashMap; use node::CallTraceNode; -use revm::{opcode, CallContext, Memory, Return, Stack}; +use revm::interpreter::{opcode, CallContext, InstructionResult, Memory, Stack}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashSet}, @@ -427,7 +427,7 @@ impl From<&CallTraceStep> for StructLog { } else { None }, - stack: Some(step.stack.data().clone()), + stack: Some(step.stack.data().iter().copied().map(|data| data.into()).collect()), // Filled in `CallTraceArena::geth_trace` as a result of compounding all slot changes storage: None, } @@ -467,7 +467,7 @@ pub struct CallTrace { /// The gas cost of the call pub gas_cost: u64, /// The status of the trace's call - pub status: Return, + pub status: InstructionResult, /// call context of the runtime pub call_context: Option, /// Opcode-level execution steps @@ -497,7 +497,7 @@ impl Default for CallTrace { data: Default::default(), output: Default::default(), gas_cost: Default::default(), - status: Return::Continue, + status: InstructionResult::Continue, call_context: Default::default(), steps: Default::default(), } diff --git a/evm/src/trace/node.rs b/evm/src/trace/node.rs index 35c6284f85020..be0c55162a113 100644 --- a/evm/src/trace/node.rs +++ b/evm/src/trace/node.rs @@ -12,7 +12,7 @@ use ethers::{ types::{Action, Address, Call, CallResult, Create, CreateResult, Res, Suicide}, }; use foundry_common::SELECTOR_LEN; -use revm::Return; +use revm::interpreter::InstructionResult; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -41,7 +41,7 @@ impl CallTraceNode { } /// Returns the status of the call - pub fn status(&self) -> Return { + pub fn status(&self) -> InstructionResult { self.trace.status } @@ -64,7 +64,7 @@ impl CallTraceNode { /// Returns the `Action` for a parity trace pub fn parity_action(&self) -> Action { - if self.status() == Return::SelfDestruct { + if self.status() == InstructionResult::SelfDestruct { return Action::Suicide(Suicide { address: self.trace.address, // TODO deserialize from calldata here? diff --git a/evm/src/utils.rs b/evm/src/utils.rs index bd0478c2edf73..067d0b088aa5c 100644 --- a/evm/src/utils.rs +++ b/evm/src/utils.rs @@ -1,10 +1,13 @@ use ethers::{ abi::{Abi, FixedBytes, Function}, prelude::{H256, U256}, - types::{BigEndianHash, Block, Chain}, + types::{Block, Chain}, }; use eyre::ContextCompat; -use revm::{opcode, spec_opcode_gas, SpecId}; +use revm::{ + interpreter::{opcode, opcode::spec_opcode_gas}, + primitives::SpecId, +}; use std::collections::BTreeMap; /// Small helper function to convert [U256] into [H256]. @@ -31,19 +34,124 @@ pub fn h256_to_u256_le(storage: H256) -> U256 { U256::from_little_endian(storage.as_bytes()) } +/// Small helper function to convert revm's [B160] into ethers's [H160]. +#[inline] +pub fn b160_to_h160(b: revm::primitives::B160) -> ethers::types::H160 { + ethers::types::H160::from_slice(&b.to_fixed_bytes()) +} + +/// Small helper function to convert ethers's [H160] into revm's [B160]. +#[inline] +pub fn h160_to_b160(h: ethers::types::H160) -> revm::primitives::B160 { + revm::primitives::B160::from_slice(&h.to_fixed_bytes()) +} + +/// Small helper function to convert revm's [B256] into ethers's [H256]. +#[inline] +pub fn b256_to_h256(b: revm::primitives::B256) -> ethers::types::H256 { + ethers::types::H256::from_slice(&b.to_fixed_bytes()) +} + +/// Small helper function to convert ether's [H256] into revm's [B256]. +#[inline] +pub fn h256_to_b256(h: ethers::types::H256) -> revm::primitives::B256 { + revm::primitives::B256::from_slice(&h.to_fixed_bytes()) +} + +/// Small helper function to convert ether's [U256] into revm's [U256]. +#[inline] +pub fn u256_to_ru256(u: ethers::types::U256) -> revm::primitives::U256 { + let mut buffer = [0u8; 32]; + u.to_little_endian(buffer.as_mut_slice()); + revm::primitives::U256::from_le_bytes(buffer) +} + +/// Small helper function to convert revm's [U256] into ethers's [U256]. +#[inline] +pub fn ru256_to_u256(u: revm::primitives::U256) -> ethers::types::U256 { + ethers::types::U256::from_little_endian(u.as_le_slice()) +} + +/// Small helper function to convert an Eval into an InstructionResult +pub fn eval_to_instruction_result( + eval: revm::primitives::Eval, +) -> revm::interpreter::InstructionResult { + match eval { + revm::primitives::Eval::Return => revm::interpreter::InstructionResult::Return, + revm::primitives::Eval::Stop => revm::interpreter::InstructionResult::Stop, + revm::primitives::Eval::SelfDestruct => revm::interpreter::InstructionResult::SelfDestruct, + } +} + +/// Small helper function to convert a Halt into an InstructionResult +pub fn halt_to_instruction_result( + halt: revm::primitives::Halt, +) -> revm::interpreter::InstructionResult { + match halt { + revm::primitives::Halt::OutOfGas(_) => revm::interpreter::InstructionResult::OutOfGas, + revm::primitives::Halt::OpcodeNotFound => { + revm::interpreter::InstructionResult::OpcodeNotFound + } + revm::primitives::Halt::InvalidFEOpcode => { + revm::interpreter::InstructionResult::InvalidFEOpcode + } + revm::primitives::Halt::InvalidJump => revm::interpreter::InstructionResult::InvalidJump, + revm::primitives::Halt::NotActivated => revm::interpreter::InstructionResult::NotActivated, + revm::primitives::Halt::StackOverflow => { + revm::interpreter::InstructionResult::StackOverflow + } + revm::primitives::Halt::StackUnderflow => { + revm::interpreter::InstructionResult::StackUnderflow + } + revm::primitives::Halt::OutOfOffset => revm::interpreter::InstructionResult::OutOfOffset, + revm::primitives::Halt::CreateCollision => { + revm::interpreter::InstructionResult::CreateCollision + } + revm::primitives::Halt::PrecompileError => { + revm::interpreter::InstructionResult::PrecompileError + } + revm::primitives::Halt::NonceOverflow => { + revm::interpreter::InstructionResult::NonceOverflow + } + revm::primitives::Halt::CreateContractSizeLimit => { + revm::interpreter::InstructionResult::CreateContractSizeLimit + } + revm::primitives::Halt::CreateContractStartingWithEF => { + revm::interpreter::InstructionResult::CreateContractStartingWithEF + } + revm::primitives::Halt::CreateInitcodeSizeLimit => { + revm::interpreter::InstructionResult::CreateInitcodeSizeLimit + } + revm::primitives::Halt::OverflowPayment => { + revm::interpreter::InstructionResult::OverflowPayment + } + revm::primitives::Halt::StateChangeDuringStaticCall => { + revm::interpreter::InstructionResult::StateChangeDuringStaticCall + } + revm::primitives::Halt::CallNotAllowedInsideStatic => { + revm::interpreter::InstructionResult::CallNotAllowedInsideStatic + } + revm::primitives::Halt::OutOfFund => revm::interpreter::InstructionResult::OutOfFund, + revm::primitives::Halt::CallTooDeep => revm::interpreter::InstructionResult::CallTooDeep, + } +} + /// Depending on the configured chain id and block number this should apply any specific changes /// /// This checks for: /// - prevrandao mixhash after merge -pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::Env, block: &Block) { - if let Ok(chain) = Chain::try_from(env.cfg.chain_id) { +pub fn apply_chain_and_block_specific_env_changes( + env: &mut revm::primitives::Env, + block: &Block, +) { + if let Ok(chain) = Chain::try_from(ru256_to_u256(env.cfg.chain_id)) { let block_number = block.number.unwrap_or_default(); match chain { Chain::Mainnet => { // after merge difficulty is supplanted with prevrandao EIP-4399 if block_number.as_u64() >= 15_537_351u64 { - env.block.difficulty = env.block.prevrandao.unwrap_or_default().into_uint(); + env.block.difficulty = env.block.prevrandao.unwrap_or_default().into(); } return @@ -56,7 +164,7 @@ pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::Env, block: // `l1BlockNumber` field if let Some(l1_block_number) = block.other.get("l1BlockNumber").cloned() { if let Ok(l1_block_number) = serde_json::from_value::(l1_block_number) { - env.block.number = l1_block_number; + env.block.number = l1_block_number.into(); } } } @@ -66,7 +174,7 @@ pub fn apply_chain_and_block_specific_env_changes(env: &mut revm::Env, block: // if difficulty is `0` we assume it's past merge if block.difficulty.is_zero() { - env.block.difficulty = env.block.prevrandao.unwrap_or_default().into_uint(); + env.block.difficulty = env.block.prevrandao.unwrap_or_default().into(); } } diff --git a/forge/src/multi_runner.rs b/forge/src/multi_runner.rs index 5b5637fbae722..ef684764ec2fb 100644 --- a/forge/src/multi_runner.rs +++ b/forge/src/multi_runner.rs @@ -10,12 +10,13 @@ use foundry_common::{ContractsByArtifact, TestFunctionExt}; use foundry_evm::{ executor::{ backend::Backend, fork::CreateFork, inspector::CheatsConfig, opts::EvmOpts, Executor, - ExecutorBuilder, SpecId, + ExecutorBuilder, }, revm, }; use foundry_utils::PostLinkInput; use rayon::prelude::*; +use revm::primitives::SpecId; use std::{collections::BTreeMap, path::Path, sync::mpsc::Sender}; pub type DeployableContracts = BTreeMap)>; @@ -31,7 +32,7 @@ pub struct MultiContractRunner { /// The EVM instance used in the test runner pub evm_opts: EvmOpts, /// The configured evm - pub env: revm::Env, + pub env: revm::primitives::Env, /// The EVM spec pub evm_spec: SpecId, /// All known errors, used for decoding reverts @@ -226,7 +227,7 @@ impl MultiContractRunnerBuilder { self, root: impl AsRef, output: ProjectCompileOutput, - env: revm::Env, + env: revm::primitives::Env, evm_opts: EvmOpts, ) -> Result where diff --git a/testdata/fork/Transact.t.sol b/testdata/fork/Transact.t.sol index 4eca77507f038..1a79751cdeacf 100644 --- a/testdata/fork/Transact.t.sol +++ b/testdata/fork/Transact.t.sol @@ -19,20 +19,20 @@ contract TransactOnForkTest is DSTest { event Transfer(address indexed from, address indexed to, uint256 value); function testTransact() public { - // A random block https://etherscan.io/block/15596646 - uint256 fork = vm.createFork("rpcAlias", 15596646); + // A random block https://etherscan.io/block/17134913 + uint256 fork = vm.createFork("rpcAlias", 17134913); vm.selectFork(fork); - // a random transfer transaction in the block: https://etherscan.io/tx/0xaba74f25a17cf0d95d1c6d0085d6c83fb8c5e773ffd2573b99a953256f989c89 - bytes32 tx = 0xaba74f25a17cf0d95d1c6d0085d6c83fb8c5e773ffd2573b99a953256f989c89; + // a random transfer transaction in the next block: https://etherscan.io/tx/0xaf6201d435b216a858c580e20512a16136916d894aa33260650e164e3238c771 + bytes32 tx = 0xaf6201d435b216a858c580e20512a16136916d894aa33260650e164e3238c771; - address sender = address(0xa98218cdc4f63aCe91ddDdd24F7A580FD383865b); - address recipient = address(0x0C124046Fa7202f98E4e251B50488e34416Fc306); + address sender = address(0x9B315A70FEe05a70A9F2c832E93a7095FEb32Bfe); + address recipient = address(0xDB358B93157Df9b3B1eE9Ea5CDB7D0aE9a1D8110); - assertEq(sender.balance, 5764124000000000); - assertEq(recipient.balance, 3936000000000000); + assertEq(sender.balance, 110231651357268209); + assertEq(recipient.balance, 892860016357511); - // transfer amount: 0.000336 Ether - uint256 transferAmount = 3936000000000000; + // transfer amount: 0.015 Ether + uint256 transferAmount = 15000000000000000; uint256 expectedRecipientBalance = recipient.balance + transferAmount; uint256 expectedSenderBalance = sender.balance - transferAmount; diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 95c981f3de581..503983e8695bb 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -14,5 +14,5 @@ eyre = "0.6.8" hex = "0.4.3" ethers = { workspace = true } forge = { path = "../forge" } -revm = { version = "2.3", features = ["std", "k256", "with-serde"] } +revm = { version = "3.1.1", features = ["std", "serde"] } foundry-common = { path = "../common" } diff --git a/ui/src/lib.rs b/ui/src/lib.rs index c27d82ba9bbf7..a619a6e325ed1 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -14,7 +14,7 @@ use forge::{ CallKind, }; use foundry_common::evm::Breakpoints; -use revm::{opcode, SpecId}; +use revm::{interpreter::opcode, primitives::SpecId}; use std::{ cmp::{max, min}, collections::{BTreeMap, HashMap, VecDeque},