diff --git a/Cargo.lock b/Cargo.lock index 01a7f21565..6f5dbff4e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,7 +52,7 @@ version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -86,7 +86,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -109,6 +109,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.5.3" @@ -157,26 +163,11 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bn-rs" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34e20109dce74b02019885a01edc8ca485380a297ed8d6eb9e63e657774074b" -dependencies = [ - "getrandom", - "js-sys", - "primitive-types", - "rustc-hex", - "thiserror", - "uint", - "wasm-bindgen", -] - [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byte-slice-cast" @@ -238,9 +229,9 @@ dependencies = [ [[package]] name = "console" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b6515d269224923b26b5febea2ed42b2d5f2ce37284a4dd670fedd6cb8347a" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", "lazy_static", @@ -314,7 +305,7 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf460bbff5f571bfc762da5102729f59f338be7db17a21fade44c5c4f5005350" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -326,7 +317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "rustc_version", "syn 1.0.107", @@ -395,7 +386,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88bcb3a067a6555d577aba299e75eff9942da276e6506fc6274327daa026132" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -482,7 +473,7 @@ checksum = "a1a9e0597aa6b2fdc810ff58bc95e4eeaa2c219b3e615ed025106ecb027407d8" dependencies = [ "async-trait", "auto_impl", - "base64", + "base64 0.13.1", "ethers-core", "futures-channel", "futures-core", @@ -617,7 +608,7 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -922,7 +913,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -939,9 +930,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.2" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4295cbb7573c16d310e99e713cf9e75101eb190ab31fccd35f2d2691b4352b19" +checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" dependencies = [ "console", "number_prefix", @@ -1186,7 +1177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -1212,7 +1203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -1273,7 +1264,7 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -1353,7 +1344,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", "version_check", @@ -1365,7 +1356,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "version_check", ] @@ -1381,9 +1372,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -1446,7 +1437,7 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", ] [[package]] @@ -1505,9 +1496,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -1535,7 +1526,7 @@ version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ - "base64", + "base64 0.13.1", "bytes", "encoding_rs", "futures-core", @@ -1600,6 +1591,7 @@ dependencies = [ "hex-literal", "proptest", "proptest-derive", + "revm-primitives", "rlp", "ruint", "serde", @@ -1624,6 +1616,27 @@ dependencies = [ "substrate-bn", ] +[[package]] +name = "revm-primitives" +version = "3.0.0" +dependencies = [ + "arbitrary", + "auto_impl", + "bytes", + "derive_more", + "enumn", + "fixed-hash", + "hashbrown 0.13.2", + "hex", + "hex-literal", + "proptest", + "proptest-derive", + "rlp", + "ruint", + "serde", + "sha3", +] + [[package]] name = "revm-test" version = "0.1.0" @@ -1659,21 +1672,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "revmjs" -version = "0.2.0" -dependencies = [ - "bn-rs", - "bytes", - "getrandom", - "hex", - "js-sys", - "primitive-types", - "revm", - "ruint", - "wasm-bindgen", -] - [[package]] name = "rfc6979" version = "0.3.1" @@ -1725,7 +1723,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -1737,7 +1735,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad3a104dc8c3867f653b0fec89c65e00b0ceb752718ad282177a7e0f33257ac" dependencies = [ "arbitrary", - "bn-rs", "derive_more", "primitive-types", "proptest", @@ -1771,9 +1768,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.7" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -1783,11 +1780,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64", + "base64 0.21.0", ] [[package]] @@ -1842,7 +1839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -1922,7 +1919,7 @@ version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -2064,7 +2061,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -2085,7 +2082,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.0", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "rustversion", "syn 1.0.107", @@ -2127,7 +2124,7 @@ version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "unicode-ident", ] @@ -2176,7 +2173,7 @@ version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -2207,9 +2204,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.24.1" +version = "1.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" dependencies = [ "autocfg", "bytes", @@ -2229,7 +2226,7 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", ] @@ -2342,7 +2339,7 @@ version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ - "base64", + "base64 0.13.1", "byteorder", "bytes", "http", @@ -2510,7 +2507,7 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", "wasm-bindgen-shared", @@ -2544,7 +2541,7 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2 1.0.49", + "proc-macro2 1.0.50", "quote 1.0.23", "syn 1.0.107", "wasm-bindgen-backend", @@ -2649,45 +2646,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" diff --git a/bins/revm-test/src/bin/analysis.rs b/bins/revm-test/src/bin/analysis.rs index d77e4508df..573f69f37a 100644 --- a/bins/revm-test/src/bin/analysis.rs +++ b/bins/revm-test/src/bin/analysis.rs @@ -1,7 +1,12 @@ use std::time::Instant; use bytes::Bytes; -use revm::{db::BenchmarkDB, Bytecode, TransactTo}; +use revm::{ + analysis::to_analysed, + db::BenchmarkDB, + primitives::{Bytecode, LondonSpec}, + TransactTo, +}; extern crate alloc; fn main() { @@ -25,18 +30,18 @@ fn main() { let bytecode_raw = Bytecode::new_raw(contract_data.clone()); let bytecode_checked = Bytecode::new_raw(contract_data.clone()).to_checked(); - let bytecode_analysed = Bytecode::new_raw(contract_data).to_analysed::(); + let bytecode_analysed = to_analysed::(Bytecode::new_raw(contract_data)); evm.database(BenchmarkDB::new_bytecode(bytecode_raw)); // just to spead up processor. for _ in 0..10000 { - let (_, _) = evm.transact(); + let _ = evm.transact().unwrap(); } let timer = Instant::now(); for _ in 0..30000 { - let (_, _) = evm.transact(); + let _ = evm.transact().unwrap(); } println!("Raw elapsed time: {:?}", timer.elapsed()); @@ -44,7 +49,7 @@ fn main() { let timer = Instant::now(); for _ in 0..30000 { - let (_, _) = evm.transact(); + let _ = evm.transact().unwrap(); } println!("Checked elapsed time: {:?}", timer.elapsed()); @@ -52,7 +57,7 @@ fn main() { let timer = Instant::now(); for _ in 0..30000 { - let (_, _) = evm.transact(); + let _ = evm.transact().unwrap(); } println!("Analysed elapsed time: {:?}", timer.elapsed()); } diff --git a/bins/revm-test/src/bin/snailtracer.rs b/bins/revm-test/src/bin/snailtracer.rs index 96e841044e..8d9d595065 100644 --- a/bins/revm-test/src/bin/snailtracer.rs +++ b/bins/revm-test/src/bin/snailtracer.rs @@ -1,9 +1,12 @@ use std::time::Duration; use bytes::Bytes; -use revm::{db::BenchmarkDB, Bytecode, TransactTo}; - -use revm_interpreter::{specification::BerlinSpec, DummyHost}; +use revm::{ + analysis::to_analysed, + db::BenchmarkDB, + primitives::{BerlinSpec, Bytecode}, + BytecodeLocked, DummyHost, TransactTo, +}; extern crate alloc; pub fn simple_example() { @@ -11,7 +14,7 @@ pub fn simple_example() { // BenchmarkDB is dummy state that implements Database trait. let mut evm = revm::new(); - let bytecode = Bytecode::new_raw(contract_data).to_analysed::(); + let bytecode = to_analysed::(Bytecode::new_raw(contract_data)); evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); // execution globals block hash/gas_limit/coinbase/timestamp.. @@ -33,14 +36,14 @@ pub fn simple_example() { &bench_options, "Snailtracer Host+Interpreter benchmark", || { - let (_, _) = evm.transact(); + let _ = evm.transact().unwrap(); }, ); // revm interpreter let contract = revm_interpreter::Contract { input: evm.env.tx.data, - bytecode: bytecode.lock_analysed().unwrap(), + bytecode: BytecodeLocked::try_from(bytecode).unwrap(), ..Default::default() }; diff --git a/bins/revme/src/statetest/merkle_trie.rs b/bins/revme/src/statetest/merkle_trie.rs index 7ff6116580..af3328dbd3 100644 --- a/bins/revme/src/statetest/merkle_trie.rs +++ b/bins/revme/src/statetest/merkle_trie.rs @@ -2,7 +2,10 @@ use bytes::Bytes; use hash_db::Hasher; use plain_hasher::PlainHasher; use primitive_types::{H160, H256}; -use revm::{common::keccak256, db::DbAccount, Log, B160, B256, U256}; +use revm::{ + db::DbAccount, + primitives::{keccak256, Log, B160, B256, U256}, +}; use rlp::RlpStream; use sha3::{Digest, Keccak256}; use triehash::sec_trie_root; diff --git a/bins/revme/src/statetest/models/deserializer.rs b/bins/revme/src/statetest/models/deserializer.rs index fdc5e8d2ba..a10bae0bc3 100644 --- a/bins/revme/src/statetest/models/deserializer.rs +++ b/bins/revme/src/statetest/models/deserializer.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use bytes::Bytes; -use revm::{B160, U256}; +use revm::primitives::{B160, U256}; use serde::{ de::{self, Error}, Deserialize, diff --git a/bins/revme/src/statetest/models/mod.rs b/bins/revme/src/statetest/models/mod.rs index fa8ca7a95e..140f065c11 100644 --- a/bins/revme/src/statetest/models/mod.rs +++ b/bins/revme/src/statetest/models/mod.rs @@ -1,5 +1,5 @@ use bytes::Bytes; -use revm::{B160, B256, U256}; +use revm::primitives::{B160, B256, U256}; use std::collections::{BTreeMap, HashMap}; mod deserializer; mod spec; @@ -100,7 +100,7 @@ pub type AccessList = Vec; mod tests { use super::*; - use revm::B160; + use revm::primitives::B160; use serde_json::Error; #[test] diff --git a/bins/revme/src/statetest/models/spec.rs b/bins/revme/src/statetest/models/spec.rs index b7e33a2b62..0d91e6ea06 100644 --- a/bins/revme/src/statetest/models/spec.rs +++ b/bins/revme/src/statetest/models/spec.rs @@ -1,4 +1,4 @@ -use revm::SpecId; +use revm::primitives::SpecId; use serde::Deserialize; #[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Ord, Deserialize)] diff --git a/bins/revme/src/statetest/runner.rs b/bins/revme/src/statetest/runner.rs index 5b9e68c45d..0a49d57cb9 100644 --- a/bins/revme/src/statetest/runner.rs +++ b/bins/revme/src/statetest/runner.rs @@ -19,7 +19,7 @@ use super::{ models::{SpecName, TestSuit}, }; use hex_literal::hex; -use revm::common::keccak256; +use revm::primitives::keccak256; use thiserror::Error; #[derive(Debug, Error)] @@ -64,6 +64,11 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< return Ok(()); } + // Need to handle Test errors + if path.file_name() == Some(OsStr::new("transactionIntinsicBug.json")) { + return Ok(()); + } + // Test check if gas price overflows, we handle this correctly but does not match tests specific exception. if path.file_name() == Some(OsStr::new("HighGasPrice.json")) { return Ok(()); @@ -138,7 +143,7 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< // Create database and insert cache let mut database = revm::InMemoryDB::default(); for (address, info) in unit.pre.iter() { - let acc_info = revm::AccountInfo { + let acc_info = revm::primitives::AccountInfo { balance: info.balance, code_hash: keccak256(&info.code), // try with dummy hash. code: Some(Bytecode::new_raw(info.code.clone())), @@ -240,18 +245,15 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< // do the deed let timer = Instant::now(); - let ExecutionResult { - exit_reason, - gas_used, - gas_refunded, - logs, - .. - } = evm.transact_commit(); + let out = evm.transact_commit(); let timer = timer.elapsed(); *elapsed.lock().unwrap() += timer; - let is_legacy = !SpecId::enabled(evm.env.cfg.spec_id, SpecId::SPURIOUS_DRAGON); + let is_legacy = !SpecId::enabled( + evm.env.cfg.spec_id, + revm::primitives::SpecId::SPURIOUS_DRAGON, + ); let db = evm.db().unwrap(); let state_root = state_merkle_trie_root( db.accounts @@ -264,6 +266,10 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< }) .map(|(k, v)| (*k, v.clone())), ); + let logs = match &out { + Ok(ExecutionResult::Success { logs, .. }) => logs.clone(), + _ => Vec::new(), + }; let logs_root = log_rlp_hash(logs); if test.hash != state_root || test.logs != logs_root { println!( @@ -272,12 +278,10 @@ pub fn execute_test_suit(path: &Path, elapsed: &Arc>) -> Result< ); let mut database_cloned = database.clone(); evm.database(&mut database_cloned); - evm.inspect_commit(CustomPrintTracer::default()); + let _ = evm.inspect_commit(CustomPrintTracer::default()); let db = evm.db().unwrap(); println!("{path:?} UNIT_TEST:{name}\n"); - println!( - "failed reason: {exit_reason:?} {path:?} UNIT_TEST:{name}\n gas:{gas_used:?} ({gas_refunded:?} refunded)", - ); + println!("Output: {out:?}"); println!("\nApplied state:{db:?}\n"); println!("\nStateroot: {state_root:?}\n"); return Err(TestError::RootMissmatch { diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index f23ba710ac..e5fca23dcf 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -10,6 +10,8 @@ version = "3.0.0" readme = "../../README.md" [dependencies] +revm-primitives = { path = "../primitives", default-features = false } + bytes = { version = "1.1", default-features = false } hashbrown = { version = "0.13" } hex = { version = "0.4", default-features = false } @@ -56,12 +58,12 @@ dev = [ "optional_eip3607", "optional_gas_refund", ] -memory_limit = [] -no_gas_measuring = [] -optional_balance_check = [] -optional_block_gas_limit = [] -optional_eip3607 = [] -optional_gas_refund = [] +memory_limit = ["revm-primitives/memory_limit"] +no_gas_measuring = ["revm-primitives/no_gas_measuring"] +optional_balance_check = ["revm-primitives/optional_balance_check"] +optional_block_gas_limit = ["revm-primitives/optional_block_gas_limit"] +optional_eip3607 = ["revm-primitives/optional_eip3607"] +optional_gas_refund = ["revm-primitives/optional_gas_refund"] std = ["bytes/std", "rlp/std", "hex/std"] serde = [ "dep:serde", @@ -69,11 +71,13 @@ serde = [ "hashbrown/serde", "ruint/serde", "bytes/serde", + "revm-primitives/serde" ] arbitrary = [ "ruint/arbitrary", "ruint/proptest", "dep:arbitrary", "dep:proptest", - "dep:proptest-derive" + "dep:proptest-derive", + "revm-primitives/arbitrary" ] diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 453dabcac3..8f40c54692 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -3,88 +3,3 @@ mod constants; pub use calc::*; pub use constants::*; -#[derive(Clone, Copy, Debug)] -pub struct Gas { - /// Gas Limit - limit: u64, - /// used+memory gas. - all_used_gas: u64, - /// Used gas without memory - used: u64, - /// Used gas for memory expansion - memory: u64, - /// Refunded gas. This gas is used only at the end of execution. - refunded: i64, -} -impl Gas { - pub fn new(limit: u64) -> Self { - Self { - limit, - used: 0, - memory: 0, - refunded: 0, - all_used_gas: 0, - } - } - - pub fn limit(&self) -> u64 { - self.limit - } - - pub fn memory(&self) -> u64 { - self.memory - } - - pub fn refunded(&self) -> i64 { - self.refunded - } - - pub fn spend(&self) -> u64 { - self.all_used_gas - } - - pub fn remaining(&self) -> u64 { - self.limit - self.all_used_gas - } - - pub fn erase_cost(&mut self, returned: u64) { - self.used -= returned; - self.all_used_gas -= returned; - } - - pub fn record_refund(&mut self, refund: i64) { - self.refunded += refund; - } - - /// Record an explicit cost. - #[inline(always)] - pub fn record_cost(&mut self, cost: u64) -> bool { - let (all_used_gas, overflow) = self.all_used_gas.overflowing_add(cost); - if overflow || self.limit < all_used_gas { - return false; - } - - self.used += cost; - self.all_used_gas = all_used_gas; - true - } - - /// used in memory_resize! macro to record gas used for memory expansion. - pub fn record_memory(&mut self, gas_memory: u64) -> bool { - if gas_memory > self.memory { - let (all_used_gas, overflow) = self.used.overflowing_add(gas_memory); - if overflow || self.limit < all_used_gas { - return false; - } - self.memory = gas_memory; - self.all_used_gas = all_used_gas; - } - true - } - - /// used in gas_refund! macro to record refund value. - /// Refund can be negative but self.refunded is always positive. - pub fn gas_refund(&mut self, refund: i64) { - self.refunded += refund; - } -} diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index f4c601eb44..d72a063cd1 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -1,5 +1,5 @@ use super::constants::*; -use crate::{models::SelfDestructResult, Spec, SpecId::*, U256}; +use crate::{inner_models::SelfDestructResult, primitives::Spec, primitives::SpecId::*, U256}; #[allow(clippy::collapsible_else_if)] pub fn sstore_refund(original: U256, current: U256, new: U256) -> i64 { diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index 2e4ab58165..c32acef35a 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -1,15 +1,21 @@ mod dummy_host; +use crate::primitives::Bytecode; use crate::{ - Bytecode, Bytes, CallInputs, CreateInputs, Env, Gas, Interpreter, Return, SelfDestructResult, - B160, B256, U256, + primitives::{Bytes, Env, Gas, B160, B256, U256}, + CallInputs, CreateInputs, InstructionResult, Interpreter, SelfDestructResult, }; pub use dummy_host::DummyHost; /// EVM context host. pub trait Host { - fn step(&mut self, interpreter: &mut Interpreter, is_static: bool) -> Return; - fn step_end(&mut self, interpreter: &mut Interpreter, is_static: bool, ret: Return) -> Return; + fn step(&mut self, interpreter: &mut Interpreter, is_static: bool) -> InstructionResult; + fn step_end( + &mut self, + interpreter: &mut Interpreter, + is_static: bool, + ret: InstructionResult, + ) -> InstructionResult; fn env(&mut self) -> &mut Env; @@ -38,7 +44,10 @@ pub trait Host { /// Mark an address to be deleted, with funds transferred to target. fn selfdestruct(&mut self, address: B160, target: B160) -> Option; /// Invoke a create operation. - fn create(&mut self, inputs: &mut CreateInputs) -> (Return, Option, Gas, Bytes); + fn create( + &mut self, + inputs: &mut CreateInputs, + ) -> (InstructionResult, Option, Gas, Bytes); /// Invoke a call operation. - fn call(&mut self, input: &mut CallInputs) -> (Return, Gas, Bytes); + fn call(&mut self, input: &mut CallInputs) -> (InstructionResult, Gas, Bytes); } diff --git a/crates/interpreter/src/host/dummy_host.rs b/crates/interpreter/src/host/dummy_host.rs index d532150939..5dac961129 100644 --- a/crates/interpreter/src/host/dummy_host.rs +++ b/crates/interpreter/src/host/dummy_host.rs @@ -1,9 +1,10 @@ use hashbrown::{hash_map::Entry, HashMap}; use ruint::aliases::U256; +use crate::primitives::Bytecode; use crate::{ - Bytecode, CallInputs, CreateInputs, Env, Gas, Host, Interpreter, Log, Return, - SelfDestructResult, B160, B256, KECCAK_EMPTY, + primitives::{Env, Gas, Log, B160, B256, KECCAK_EMPTY}, + CallInputs, CreateInputs, Host, InstructionResult, Interpreter, SelfDestructResult, }; pub struct DummyHost { @@ -27,12 +28,17 @@ impl DummyHost { } impl Host for DummyHost { - fn step(&mut self, _interp: &mut Interpreter, _is_static: bool) -> Return { - Return::Continue + fn step(&mut self, _interp: &mut Interpreter, _is_static: bool) -> InstructionResult { + InstructionResult::Continue } - fn step_end(&mut self, _interp: &mut Interpreter, _is_static: bool, _ret: Return) -> Return { - Return::Continue + fn step_end( + &mut self, + _interp: &mut Interpreter, + _is_static: bool, + _ret: InstructionResult, + ) -> InstructionResult { + InstructionResult::Continue } fn env(&mut self) -> &mut Env { @@ -107,11 +113,14 @@ impl Host for DummyHost { panic!("Create is not supported for this host") } - fn create(&mut self, _inputs: &mut CreateInputs) -> (Return, Option, Gas, bytes::Bytes) { + fn create( + &mut self, + _inputs: &mut CreateInputs, + ) -> (InstructionResult, Option, Gas, bytes::Bytes) { panic!("Create is not supported for this host") } - fn call(&mut self, _input: &mut CallInputs) -> (Return, Gas, bytes::Bytes) { + fn call(&mut self, _input: &mut CallInputs) -> (InstructionResult, Gas, bytes::Bytes) { panic!("Call is not supported for this host") } } diff --git a/crates/interpreter/src/inner_models.rs b/crates/interpreter/src/inner_models.rs new file mode 100644 index 0000000000..5467876995 --- /dev/null +++ b/crates/interpreter/src/inner_models.rs @@ -0,0 +1,99 @@ +pub use crate::primitives::CreateScheme; +use crate::primitives::{Bytes, B160, U256}; + +/// Inputs for a call. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CallInputs { + /// The target of the call. + pub contract: B160, + /// The transfer, if any, in this call. + pub transfer: Transfer, + /// The call data of the call. + #[cfg_attr( + feature = "serde", + serde(with = "crate::primitives::utilities::serde_hex_bytes") + )] + pub input: Bytes, + /// The gas limit of the call. + pub gas_limit: u64, + /// The context of the call. + pub context: CallContext, + /// Is static call + pub is_static: bool, +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CreateInputs { + pub caller: B160, + pub scheme: CreateScheme, + pub value: U256, + #[cfg_attr( + feature = "serde", + serde(with = "crate::primitives::utilities::serde_hex_bytes") + )] + pub init_code: Bytes, + pub gas_limit: u64, +} + +/// Call schemes. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum CallScheme { + /// `CALL` + Call, + /// `CALLCODE` + CallCode, + /// `DELEGATECALL` + DelegateCall, + /// `STATICCALL` + StaticCall, +} + +/// CallContext of the runtime. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CallContext { + /// Execution address. + pub address: B160, + /// Caller of the EVM. + pub caller: B160, + /// The address the contract code was loaded from, if any. + pub code_address: B160, + /// Apparent value of the EVM. + pub apparent_value: U256, + /// The scheme used for the call. + pub scheme: CallScheme, +} + +impl Default for CallContext { + fn default() -> Self { + CallContext { + address: B160::default(), + caller: B160::default(), + code_address: B160::default(), + apparent_value: U256::default(), + scheme: CallScheme::Call, + } + } +} + +/// Transfer from source to target, with given value. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Transfer { + /// Source address. + pub source: B160, + /// Target address. + pub target: B160, + /// Transfer value. + pub value: U256, +} + +#[derive(Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SelfDestructResult { + pub had_value: bool, + pub target_exists: bool, + pub is_cold: bool, + pub previously_destroyed: bool, +} diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs new file mode 100644 index 0000000000..db130fb0ce --- /dev/null +++ b/crates/interpreter/src/instruction_result.rs @@ -0,0 +1,97 @@ +use revm_primitives::{Eval, Halt}; + +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum InstructionResult { + //success codes + Continue = 0x00, + Stop = 0x01, + Return = 0x02, + SelfDestruct = 0x03, + + // revert code + Revert = 0x20, // revert opcode + CallTooDeep = 0x21, + OutOfFund = 0x22, + + // error codes + OutOfGas = 0x50, + OpcodeNotFound, + CallNotAllowedInsideStatic, + InvalidFEOpcode, + InvalidJump, + NotActivated, + StackUnderflow, + StackOverflow, + OutOfOffset, + CreateCollision, + OverflowPayment, + PrecompileError, + NonceOverflow, + /// Create init code size exceeds limit (runtime). + CreateContractSizeLimit, + /// Error on created contract that begins with EF + CreateContractStartingWithEF, + + // Fatal external error. Returned by database. + FatalExternalError, +} + +pub enum SuccessOrHalt { + Success(Eval), + Revert, + Halt(Halt), + FatalExternalError, + // this is internal opcode. + Internal, +} + +impl From for SuccessOrHalt { + fn from(result: InstructionResult) -> Self { + match result { + InstructionResult::Continue => Self::Internal, // used only in interpreter loop + InstructionResult::Stop => Self::Success(Eval::Stop), + InstructionResult::Return => Self::Success(Eval::Return), + InstructionResult::SelfDestruct => Self::Success(Eval::SelfDestruct), + InstructionResult::Revert => Self::Revert, + InstructionResult::CallTooDeep => Self::Internal, // not gonna happen for first call + InstructionResult::OutOfFund => Self::Internal, // Check for first call is done separately. + InstructionResult::OutOfGas => Self::Halt(Halt::OutOfGas), + InstructionResult::OpcodeNotFound => Self::Halt(Halt::OpcodeNotFound), + InstructionResult::CallNotAllowedInsideStatic => Self::Internal, // first call is not static call + InstructionResult::InvalidFEOpcode => Self::Halt(Halt::InvalidFEOpcode), + InstructionResult::InvalidJump => Self::Halt(Halt::InvalidJump), + InstructionResult::NotActivated => Self::Halt(Halt::NotActivated), + InstructionResult::StackUnderflow => Self::Halt(Halt::StackUnderflow), + InstructionResult::StackOverflow => Self::Halt(Halt::StackOverflow), + InstructionResult::OutOfOffset => Self::Halt(Halt::OutOfOffset), + InstructionResult::CreateCollision => Self::Halt(Halt::CreateCollision), + InstructionResult::OverflowPayment => Self::Internal, // Check for first call is done separately. + InstructionResult::PrecompileError => Self::Halt(Halt::PrecompileError), + InstructionResult::NonceOverflow => Self::Halt(Halt::NonceOverflow), + InstructionResult::CreateContractSizeLimit => Self::Halt(Halt::CreateContractSizeLimit), + InstructionResult::CreateContractStartingWithEF => { + Self::Halt(Halt::CreateContractSizeLimit) + } + InstructionResult::FatalExternalError => Self::FatalExternalError, + } + } +} + +#[macro_export] +macro_rules! return_ok { + () => { + InstructionResult::Continue + | InstructionResult::Stop + | InstructionResult::Return + | InstructionResult::SelfDestruct + }; +} + +#[macro_export] +macro_rules! return_revert { + () => { + InstructionResult::Revert | InstructionResult::CallTooDeep | InstructionResult::OutOfFund + }; +} diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index a3cb11ee60..dc37062f62 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -11,76 +11,19 @@ pub mod opcode; mod stack; mod system; -use crate::{interpreter::Interpreter, Host, Spec}; +use crate::{interpreter::Interpreter, primitives::Spec, Host}; pub use opcode::{OpCode, OPCODE_JUMPMAP}; -#[macro_export] -macro_rules! return_ok { - () => { - Return::Continue | Return::Stop | Return::Return | Return::SelfDestruct - }; -} - -#[macro_export] -macro_rules! return_revert { - () => { - Return::Revert | Return::CallTooDeep | Return::OutOfFund - }; -} - -#[repr(u8)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Return { - //success codes - Continue = 0x00, - Stop = 0x01, - Return = 0x02, - SelfDestruct = 0x03, - - // revert code - Revert = 0x20, // revert opcode - CallTooDeep = 0x21, - OutOfFund = 0x22, - - // error codes - OutOfGas = 0x50, - OpcodeNotFound, - CallNotAllowedInsideStatic, - InvalidOpcode, - InvalidJump, - InvalidMemoryRange, - NotActivated, - StackUnderflow, - StackOverflow, - OutOfOffset, - FatalExternalError, - GasMaxFeeGreaterThanPriorityFee, - PrevrandaoNotSet, - GasPriceLessThenBasefee, - CallerGasLimitMoreThenBlock, - /// EIP-3607 Reject transactions from senders with deployed code - RejectCallerWithCode, - LackOfFundForGasLimit, - CreateCollision, - OverflowPayment, - PrecompileError, - NonceOverflow, - /// Create init code exceeds limit (runtime). - CreateContractLimit, - /// Error on created contract that begins with EF - CreateContractWithEF, -} - +pub use crate::{return_ok, return_revert, InstructionResult}; pub fn return_stop(interpreter: &mut Interpreter, _host: &mut dyn Host) { - interpreter.instruction_result = Return::Stop; + interpreter.instruction_result = InstructionResult::Stop; } pub fn return_invalid(interpreter: &mut Interpreter, _host: &mut dyn Host) { - interpreter.instruction_result = Return::InvalidOpcode; + interpreter.instruction_result = InstructionResult::InvalidFEOpcode; } pub fn return_not_found(interpreter: &mut Interpreter, _host: &mut dyn Host) { - interpreter.instruction_result = Return::OpcodeNotFound; + interpreter.instruction_result = InstructionResult::OpcodeNotFound; } #[inline(always)] diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index e7388e1d78..4b9574d221 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -1,5 +1,5 @@ use super::i256::{i256_div, i256_mod}; -use crate::{gas, Host, Interpreter, Return, Spec, U256}; +use crate::{gas, primitives::Spec, Host, InstructionResult, Interpreter, U256}; pub fn wrapped_add(interpreter: &mut Interpreter, _host: &mut dyn Host) { pop_top!(interpreter, op1, op2); diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 02a7c3897b..4db1ba26ed 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -1,5 +1,8 @@ use super::i256::{i256_cmp, i256_sign, two_compl, Sign}; -use crate::{Host, Interpreter, Return, Spec, SpecId::CONSTANTINOPLE, U256}; +use crate::{ + primitives::Spec, primitives::SpecId::CONSTANTINOPLE, Host, InstructionResult, Interpreter, + U256, +}; use core::cmp::Ordering; use core::ops::{BitAnd, BitOr, BitXor}; diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 0682dc9dc6..13cc302d66 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -1,16 +1,19 @@ -use crate::{gas, interpreter::Interpreter, Host, Return, Spec, SpecId::*, U256}; +use crate::{ + gas, interpreter::Interpreter, primitives::Spec, primitives::SpecId::*, Host, + InstructionResult, U256, +}; pub fn jump(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::MID); pop!(interpreter, dest); - let dest = as_usize_or_fail!(interpreter, dest, Return::InvalidJump); + let dest = as_usize_or_fail!(interpreter, dest, InstructionResult::InvalidJump); if interpreter.contract.is_valid_jump(dest) { // Safety: In analysis we are checking create our jump table and we do check above to be // sure that jump is safe to execute. interpreter.instruction_pointer = unsafe { interpreter.contract.bytecode.as_ptr().add(dest) }; } else { - interpreter.instruction_result = Return::InvalidJump; + interpreter.instruction_result = InstructionResult::InvalidJump; } } @@ -18,14 +21,14 @@ pub fn jumpi(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::HIGH); pop!(interpreter, dest, value); if value != U256::ZERO { - let dest = as_usize_or_fail!(interpreter, dest, Return::InvalidJump); + let dest = as_usize_or_fail!(interpreter, dest, InstructionResult::InvalidJump); if interpreter.contract.is_valid_jump(dest) { // Safety: In analysis we are checking if jump is valid destination and // this `if` makes this unsafe block safe. interpreter.instruction_pointer = unsafe { interpreter.contract.bytecode.as_ptr().add(dest) }; } else { - interpreter.instruction_result = Return::InvalidJump + interpreter.instruction_result = InstructionResult::InvalidJump } } else if let Some(ret) = interpreter.add_next_gas_block(interpreter.program_counter() - 1) { // if we are not doing jump, add next gas block. @@ -48,15 +51,15 @@ pub fn pc(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn ret(interpreter: &mut Interpreter, _host: &mut dyn Host) { // zero gas cost gas!(interp,gas::ZERO); pop!(interpreter, start, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); if len == 0 { interpreter.return_range = usize::MAX..usize::MAX; } else { - let offset = as_usize_or_fail!(interpreter, start, Return::OutOfGas); + let offset = as_usize_or_fail!(interpreter, start, InstructionResult::OutOfGas); memory_resize!(interpreter, offset, len); interpreter.return_range = offset..(offset + len); } - interpreter.instruction_result = Return::Return; + interpreter.instruction_result = InstructionResult::Return; } pub fn revert(interpreter: &mut Interpreter, _host: &mut dyn Host) { @@ -64,13 +67,13 @@ pub fn revert(interpreter: &mut Interpreter, _host: &mut dyn Host) { // EIP-140: REVERT instruction check!(interpreter, SPEC::enabled(BYZANTIUM)); pop!(interpreter, start, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); if len == 0 { interpreter.return_range = usize::MAX..usize::MAX; } else { - let offset = as_usize_or_fail!(interpreter, start, Return::OutOfGas); + let offset = as_usize_or_fail!(interpreter, start, InstructionResult::OutOfGas); memory_resize!(interpreter, offset, len); interpreter.return_range = offset..(offset + len); } - interpreter.instruction_result = Return::Revert; + interpreter.instruction_result = InstructionResult::Revert; } diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 97a766af54..6c85669e42 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -1,12 +1,10 @@ +use crate::primitives::{Spec, SpecId::*, B160, B256, U256}; use crate::{ alloc::vec::Vec, - bits::{B160, B256}, gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, interpreter::Interpreter, return_ok, return_revert, CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, - Host, Return, Spec, - SpecId::*, - Transfer, U256, + Host, InstructionResult, Transfer, }; use bytes::Bytes; use core::cmp::min; @@ -15,7 +13,7 @@ pub fn balance(interpreter: &mut Interpreter, host: &mut dyn Host) { pop_address!(interpreter, address); let ret = host.balance(address); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (balance, is_cold) = ret.unwrap(); @@ -39,7 +37,7 @@ pub fn selfbalance(interpreter: &mut Interpreter, host: &mut dyn Hos check!(interpreter, SPEC::enabled(ISTANBUL)); let ret = host.balance(interpreter.contract.address); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (balance, _) = ret.unwrap(); @@ -50,7 +48,7 @@ pub fn extcodesize(interpreter: &mut Interpreter, host: &mut dyn Hos pop_address!(interpreter, address); let ret = host.code(address); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (code, is_cold) = ret.unwrap(); @@ -70,7 +68,7 @@ pub fn extcodehash(interpreter: &mut Interpreter, host: &mut dyn Hos pop_address!(interpreter, address); let ret = host.code_hash(address); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (code_hash, is_cold) = ret.unwrap(); @@ -90,12 +88,12 @@ pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut dyn Hos let ret = host.code(address); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (code, is_cold) = ret.unwrap(); - let len = as_usize_or_fail!(interpreter, len_u256, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len_u256, InstructionResult::OutOfGas); gas_or_fail!( interpreter, gas::extcodecopy_cost::(len as u64, is_cold) @@ -103,7 +101,7 @@ pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut dyn Hos if len == 0 { return; } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset, Return::OutOfGas); + let memory_offset = as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); let code_offset = min(as_usize_saturated!(code_offset), code.len()); memory_resize!(interpreter, memory_offset, len); @@ -123,7 +121,7 @@ pub fn blockhash(interpreter: &mut Interpreter, host: &mut dyn Host) { if diff <= 256 && diff != 0 { let ret = host.block_hash(*number); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } *number = U256::from_be_bytes(*ret.unwrap()); @@ -138,7 +136,7 @@ pub fn sload(interpreter: &mut Interpreter, host: &mut dyn Host) { let ret = host.sload(interpreter.contract.address, index); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (value, is_cold) = ret.unwrap(); @@ -152,7 +150,7 @@ pub fn sstore(interpreter: &mut Interpreter, host: &mut dyn Host) { pop!(interpreter, index, value); let ret = host.sstore(interpreter.contract.address, index, value); if ret.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (original, old, new, is_cold) = ret.unwrap(); @@ -170,18 +168,18 @@ pub fn log(interpreter: &mut Interpreter, host: &mut dy check!(interpreter, !interpreter.is_static); pop!(interpreter, offset, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); gas_or_fail!(interpreter, gas::log_cost(N, len as u64)); let data = if len == 0 { Bytes::new() } else { - let offset = as_usize_or_fail!(interpreter, offset, Return::OutOfGas); + let offset = as_usize_or_fail!(interpreter, offset, InstructionResult::OutOfGas); memory_resize!(interpreter, offset, len); Bytes::copy_from_slice(interpreter.memory.get_slice(offset, len)) }; let n = N as usize; if interpreter.stack.len() < n { - interpreter.instruction_result = Return::StackUnderflow; + interpreter.instruction_result = InstructionResult::StackUnderflow; return; } @@ -202,7 +200,7 @@ pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut dyn Ho let res = host.selfdestruct(interpreter.contract.address, target); if res.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let res = res.unwrap(); @@ -213,7 +211,7 @@ pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut dyn Ho } gas!(interpreter, gas::selfdestruct_cost::(res)); - interpreter.instruction_result = Return::SelfDestruct; + interpreter.instruction_result = InstructionResult::SelfDestruct; } pub fn create( @@ -229,12 +227,12 @@ pub fn create( interpreter.return_data_buffer = Bytes::new(); pop!(interpreter, value, code_offset, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); let code = if len == 0 { Bytes::new() } else { - let code_offset = as_usize_or_fail!(interpreter, code_offset, Return::OutOfGas); + let code_offset = as_usize_or_fail!(interpreter, code_offset, InstructionResult::OutOfGas); memory_resize!(interpreter, code_offset, len); Bytes::copy_from_slice(interpreter.memory.get_slice(code_offset, len)) }; @@ -283,8 +281,8 @@ pub fn create( push_b256!(interpreter, B256::zero()); interpreter.gas.erase_cost(gas.remaining()); } - Return::FatalExternalError => { - interpreter.instruction_result = Return::FatalExternalError; + InstructionResult::FatalExternalError => { + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } _ => { @@ -336,7 +334,7 @@ pub fn call_inner( CallScheme::Call => { pop!(interpreter, value); if interpreter.is_static && value != U256::ZERO { - interpreter.instruction_result = Return::CallNotAllowedInsideStatic; + interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; return; } value @@ -346,18 +344,18 @@ pub fn call_inner( pop!(interpreter, in_offset, in_len, out_offset, out_len); - let in_len = as_usize_or_fail!(interpreter, in_len, Return::OutOfGas); + let in_len = as_usize_or_fail!(interpreter, in_len, InstructionResult::OutOfGas); let input = if in_len != 0 { - let in_offset = as_usize_or_fail!(interpreter, in_offset, Return::OutOfGas); + let in_offset = as_usize_or_fail!(interpreter, in_offset, InstructionResult::OutOfGas); memory_resize!(interpreter, in_offset, in_len); Bytes::copy_from_slice(interpreter.memory.get_slice(in_offset, in_len)) } else { Bytes::new() }; - let out_len = as_usize_or_fail!(interpreter, out_len, Return::OutOfGas); + let out_len = as_usize_or_fail!(interpreter, out_len, InstructionResult::OutOfGas); let out_offset = if out_len != 0 { - let out_offset = as_usize_or_fail!(interpreter, out_offset, Return::OutOfGas); + let out_offset = as_usize_or_fail!(interpreter, out_offset, InstructionResult::OutOfGas); memory_resize!(interpreter, out_offset, out_len); out_offset } else { @@ -412,7 +410,7 @@ pub fn call_inner( // load account and calculate gas cost. let res = host.load_account(to); if res.is_none() { - interpreter.instruction_result = Return::FatalExternalError; + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } let (is_cold, exist) = res.unwrap(); @@ -479,8 +477,8 @@ pub fn call_inner( .set(out_offset, &interpreter.return_data_buffer[..target_len]); push!(interpreter, U256::ZERO); } - Return::FatalExternalError => { - interpreter.instruction_result = Return::FatalExternalError; + InstructionResult::FatalExternalError => { + interpreter.instruction_result = InstructionResult::FatalExternalError; return; } _ => { diff --git a/crates/interpreter/src/instructions/host_env.rs b/crates/interpreter/src/instructions/host_env.rs index 6374b359ea..24a370d747 100644 --- a/crates/interpreter/src/instructions/host_env.rs +++ b/crates/interpreter/src/instructions/host_env.rs @@ -1,4 +1,6 @@ -use crate::{interpreter::Interpreter, Host, Return, Spec, SpecId::*}; +use crate::{ + interpreter::Interpreter, primitives::Spec, primitives::SpecId::*, Host, InstructionResult, +}; pub fn chainid(interpreter: &mut Interpreter, host: &mut dyn Host) { // gas!(interp, gas::BASE); diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 6b7d30dbf3..f6a751662d 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -1,9 +1,9 @@ -pub use crate::Return; +pub use crate::InstructionResult; macro_rules! check { ($interp:expr, $expresion:expr) => { if !$expresion { - $interp.instruction_result = Return::NotActivated; + $interp.instruction_result = InstructionResult::NotActivated; return; } }; @@ -13,7 +13,7 @@ macro_rules! gas { ($interp:expr, $gas:expr) => { if crate::USE_GAS { if !$interp.gas.record_cost(($gas)) { - $interp.instruction_result = Return::OutOfGas; + $interp.instruction_result = InstructionResult::OutOfGas; return; } } @@ -34,7 +34,7 @@ macro_rules! gas_or_fail { match $gas { Some(gas_used) => gas!($interp, gas_used), None => { - $interp.instruction_result = Return::OutOfGas; + $interp.instruction_result = InstructionResult::OutOfGas; return; } } @@ -51,7 +51,7 @@ macro_rules! memory_resize { { #[cfg(feature = "memory_limit")] if new_size > ($interp.memory_limit as usize) { - $interp.instruction_result = Return::OutOfGas; + $interp.instruction_result = InstructionResult::OutOfGas; return; } @@ -59,14 +59,14 @@ macro_rules! memory_resize { if crate::USE_GAS { let num_bytes = new_size / 32; if !$interp.gas.record_memory(crate::gas::memory_gas(num_bytes)) { - $interp.instruction_result = Return::OutOfGas; + $interp.instruction_result = InstructionResult::OutOfGas; return; } } $interp.memory.resize(new_size); } } else { - $interp.instruction_result = Return::OutOfGas; + $interp.instruction_result = InstructionResult::OutOfGas; return; } }}; @@ -75,7 +75,7 @@ macro_rules! memory_resize { macro_rules! pop_address { ( $interp:expr, $x1:ident) => { if $interp.stack.len() < 1 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -87,7 +87,7 @@ macro_rules! pop_address { }; ( $interp:expr, $x1:ident, $x2:ident) => { if $interp.stack.len() < 2 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } let mut temp = H256::zero(); @@ -108,7 +108,7 @@ macro_rules! pop_address { macro_rules! pop { ( $interp:expr, $x1:ident) => { if $interp.stack.len() < 1 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -116,7 +116,7 @@ macro_rules! pop { }; ( $interp:expr, $x1:ident, $x2:ident) => { if $interp.stack.len() < 2 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -124,7 +124,7 @@ macro_rules! pop { }; ( $interp:expr, $x1:ident, $x2:ident, $x3:ident) => { if $interp.stack.len() < 3 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -133,7 +133,7 @@ macro_rules! pop { ( $interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident) => { if $interp.stack.len() < 4 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -144,7 +144,7 @@ macro_rules! pop { macro_rules! pop_top { ( $interp:expr, $x1:ident) => { if $interp.stack.len() < 1 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -152,7 +152,7 @@ macro_rules! pop_top { }; ( $interp:expr, $x1:ident, $x2:ident) => { if $interp.stack.len() < 2 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -160,7 +160,7 @@ macro_rules! pop_top { }; ( $interp:expr, $x1:ident, $x2:ident, $x3:ident) => { if $interp.stack.len() < 3 { - $interp.instruction_result = Return::StackUnderflow; + $interp.instruction_result = InstructionResult::StackUnderflow; return; } // Safety: Length is checked above. @@ -213,7 +213,7 @@ macro_rules! as_usize_saturated { macro_rules! as_usize_or_fail { ( $interp:expr, $v:expr ) => {{ - as_usize_or_fail!($interp, $v, Return::OutOfGas) + as_usize_or_fail!($interp, $v, InstructionResult::OutOfGas) }}; ( $interp:expr, $v:expr, $reason:expr ) => {{ diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index 06a482f6f6..ca0ff33e6f 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -1,9 +1,9 @@ -use crate::{interpreter::Interpreter, Host, Return, U256}; +use crate::{interpreter::Interpreter, Host, InstructionResult, U256}; pub fn mload(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::VERYLOW); pop!(interpreter, index); - let index = as_usize_or_fail!(interpreter, index, Return::OutOfGas); + let index = as_usize_or_fail!(interpreter, index, InstructionResult::OutOfGas); memory_resize!(interpreter, index, 32); push!( interpreter, @@ -16,7 +16,7 @@ pub fn mload(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn mstore(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::VERYLOW); pop!(interpreter, index, value); - let index = as_usize_or_fail!(interpreter, index, Return::OutOfGas); + let index = as_usize_or_fail!(interpreter, index, InstructionResult::OutOfGas); memory_resize!(interpreter, index, 32); interpreter.memory.set_u256(index, value); } @@ -24,7 +24,7 @@ pub fn mstore(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn mstore8(interpreter: &mut Interpreter, _host: &mut dyn Host) { // gas!(interp, gas::VERYLOW); pop!(interpreter, index, value); - let index = as_usize_or_fail!(interpreter, index, Return::OutOfGas); + let index = as_usize_or_fail!(interpreter, index, InstructionResult::OutOfGas); memory_resize!(interpreter, index, 1); let value = value.as_le_bytes()[0]; // Safety: we resized our memory two lines above. diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index 3d02c5c545..320f1bca14 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -1,5 +1,5 @@ use crate::gas; -use crate::SpecId; +use crate::primitives::SpecId; #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct OpCode(u8); diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 088dfb535f..5098119496 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -1,17 +1,19 @@ use crate::{ - common::keccak256, gas, interpreter::Interpreter, Host, Return, Spec, SpecId::*, B256, - KECCAK_EMPTY, U256, + gas, + interpreter::Interpreter, + primitives::{keccak256, Spec, SpecId::*, B256, KECCAK_EMPTY, U256}, + Host, InstructionResult, }; use std::cmp::min; pub fn sha3(interpreter: &mut Interpreter, _host: &mut dyn Host) { pop!(interpreter, from, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); gas_or_fail!(interpreter, gas::sha3_cost(len as u64)); let hash = if len == 0 { KECCAK_EMPTY } else { - let from = as_usize_or_fail!(interpreter, from, Return::OutOfGas); + let from = as_usize_or_fail!(interpreter, from, InstructionResult::OutOfGas); memory_resize!(interpreter, from, len); keccak256(interpreter.memory.get_slice(from, len)) }; @@ -36,12 +38,12 @@ pub fn codesize(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn codecopy(interpreter: &mut Interpreter, _host: &mut dyn Host) { pop!(interpreter, memory_offset, code_offset, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); if len == 0 { return; } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset, Return::OutOfGas); + let memory_offset = as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); let code_offset = as_usize_saturated!(code_offset); memory_resize!(interpreter, memory_offset, len); @@ -83,12 +85,12 @@ pub fn callvalue(interpreter: &mut Interpreter, _host: &mut dyn Host) { pub fn calldatacopy(interpreter: &mut Interpreter, _host: &mut dyn Host) { pop!(interpreter, memory_offset, data_offset, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); if len == 0 { return; } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset, Return::OutOfGas); + let memory_offset = as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); let data_offset = as_usize_saturated!(data_offset); memory_resize!(interpreter, memory_offset, len); @@ -112,16 +114,17 @@ pub fn returndatacopy(interpreter: &mut Interpreter, _host: &mut dyn // EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY check!(interpreter, SPEC::enabled(BYZANTIUM)); pop!(interpreter, memory_offset, offset, len); - let len = as_usize_or_fail!(interpreter, len, Return::OutOfGas); + let len = as_usize_or_fail!(interpreter, len, InstructionResult::OutOfGas); gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); let data_offset = as_usize_saturated!(offset); let (data_end, overflow) = data_offset.overflowing_add(len); if overflow || data_end > interpreter.return_data_buffer.len() { - interpreter.instruction_result = Return::OutOfOffset; + interpreter.instruction_result = InstructionResult::OutOfOffset; return; } if len != 0 { - let memory_offset = as_usize_or_fail!(interpreter, memory_offset, Return::OutOfGas); + let memory_offset = + as_usize_or_fail!(interpreter, memory_offset, InstructionResult::OutOfGas); memory_resize!(interpreter, memory_offset, len); interpreter.memory.set( memory_offset, diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index ce597b4781..66d5aea7ac 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -1,16 +1,17 @@ -pub mod bytecode; +pub mod analysis; mod contract; pub(crate) mod memory; mod stack; -pub use bytecode::{Bytecode, BytecodeLocked, BytecodeState}; +pub use analysis::BytecodeLocked; pub use contract::Contract; pub use memory::Memory; pub use stack::Stack; +use crate::primitives::{Gas, Spec}; use crate::{ - instructions::{eval, Return}, - Gas, Host, Spec, USE_GAS, + instructions::{eval, InstructionResult}, + Host, USE_GAS, }; use bytes::Bytes; use core::ops::Range; @@ -22,7 +23,7 @@ pub struct Interpreter { /// Instruction pointer. pub instruction_pointer: *const u8, /// Return is main control flag, it tell us if we should continue interpreter or break from it - pub instruction_result: Return, + pub instruction_result: InstructionResult, /// left gas. Memory gas can be found in Memory field. pub gas: Gas, /// Memory. @@ -59,7 +60,7 @@ impl Interpreter { stack: Stack::new(), return_data_buffer: Bytes::new(), contract, - instruction_result: Return::Continue, + instruction_result: InstructionResult::Continue, is_static, gas: Gas::new(gas_limit), } @@ -85,7 +86,7 @@ impl Interpreter { stack: Stack::new(), return_data_buffer: Bytes::new(), contract, - instruction_result: Return::Continue, + instruction_result: InstructionResult::Continue, is_static, gas: Gas::new(gas_limit), memory_limit, @@ -111,11 +112,11 @@ impl Interpreter { } #[inline(always)] - pub fn add_next_gas_block(&mut self, pc: usize) -> Option { + pub fn add_next_gas_block(&mut self, pc: usize) -> Option { if USE_GAS { let gas_block = self.contract.gas_block(pc); if !self.gas.record_cost(gas_block) { - return Some(Return::OutOfGas); + return Some(InstructionResult::OutOfGas); } } None @@ -143,34 +144,34 @@ impl Interpreter { } /// loop steps until we are finished with execution - pub fn run(&mut self, host: &mut H) -> Return { + pub fn run(&mut self, host: &mut H) -> InstructionResult { // add first gas_block if USE_GAS && !self.gas.record_cost(self.contract.first_gas_block()) { - return Return::OutOfGas; + return InstructionResult::OutOfGas; } - while self.instruction_result == Return::Continue { + while self.instruction_result == InstructionResult::Continue { self.step::(host) } self.instruction_result } /// loop steps until we are finished with execution - pub fn run_inspect(&mut self, host: &mut H) -> Return { + pub fn run_inspect(&mut self, host: &mut H) -> InstructionResult { // add first gas_block if USE_GAS && !self.gas.record_cost(self.contract.first_gas_block()) { - return Return::OutOfGas; + return InstructionResult::OutOfGas; } - while self.instruction_result == Return::Continue { + while self.instruction_result == InstructionResult::Continue { // step let ret = host.step(self, self.is_static); - if ret != Return::Continue { + if ret != InstructionResult::Continue { return ret; } self.step::(host); // step ends let ret = host.step_end(self, self.is_static, self.instruction_result); - if ret != Return::Continue { + if ret != InstructionResult::Continue { return ret; } } diff --git a/crates/interpreter/src/interpreter/analysis.rs b/crates/interpreter/src/interpreter/analysis.rs new file mode 100644 index 0000000000..24436c59be --- /dev/null +++ b/crates/interpreter/src/interpreter/analysis.rs @@ -0,0 +1,164 @@ +use crate::primitives::{AnalysisData, Bytecode, BytecodeState, Spec, ValidJumpAddress, B256}; +use crate::{opcode, spec_opcode_gas}; +use bytes::Bytes; +use std::sync::Arc; + +pub fn to_analysed(bytecode: Bytecode) -> Bytecode { + let hash = bytecode.hash; + let (bytecode, len) = match bytecode.state { + BytecodeState::Raw => { + let len = bytecode.bytecode.len(); + let checked = bytecode.to_checked(); + (checked.bytecode, len) + } + BytecodeState::Checked { len } => (bytecode.bytecode, len), + _ => return bytecode, + }; + let jumptable = analyze::(bytecode.as_ref()); + + Bytecode { + bytecode, + hash, + state: BytecodeState::Analysed { len, jumptable }, + } +} + +/// Analyze bytecode to get jumptable and gas blocks. +fn analyze(code: &[u8]) -> ValidJumpAddress { + let opcode_gas = spec_opcode_gas(SPEC::SPEC_ID); + + let mut analysis = ValidJumpAddress { + first_gas_block: 0, + analysis: Arc::new(vec![AnalysisData::none(); code.len()]), + }; + let jumps = Arc::get_mut(&mut analysis.analysis).unwrap(); + + let mut index = 0; + let mut gas_in_block: u32 = 0; + let mut block_start: usize = 0; + + // first gas block + while index < code.len() { + let opcode = *code.get(index).unwrap(); + let info = opcode_gas.get(opcode as usize).unwrap(); + analysis.first_gas_block += info.get_gas(); + + index += if info.is_push() { + ((opcode - opcode::PUSH1) + 2) as usize + } else { + 1 + }; + + if info.is_gas_block_end() { + block_start = index - 1; + if info.is_jump() { + jumps.get_mut(block_start).unwrap().set_is_jump(); + } + break; + } + } + + while index < code.len() { + let opcode = *code.get(index).unwrap(); + let info = opcode_gas.get(opcode as usize).unwrap(); + gas_in_block += info.get_gas(); + + if info.is_gas_block_end() { + if info.is_jump() { + jumps.get_mut(index).unwrap().set_is_jump(); + } + jumps + .get_mut(block_start) + .unwrap() + .set_gas_block(gas_in_block); + block_start = index; + gas_in_block = 0; + index += 1; + } else { + index += if info.is_push() { + ((opcode - opcode::PUSH1) + 2) as usize + } else { + 1 + }; + } + } + if gas_in_block != 0 { + jumps + .get_mut(block_start) + .unwrap() + .set_gas_block(gas_in_block); + } + analysis +} + +#[derive(Clone)] +pub struct BytecodeLocked { + bytecode: Bytes, + len: usize, + hash: B256, + jumptable: ValidJumpAddress, +} + +impl Default for BytecodeLocked { + fn default() -> Self { + Bytecode::default() + .try_into() + .expect("Bytecode default is analysed code") + } +} + +impl TryFrom for BytecodeLocked { + type Error = (); + + fn try_from(bytecode: Bytecode) -> Result { + if let BytecodeState::Analysed { len, jumptable } = bytecode.state { + Ok(BytecodeLocked { + bytecode: bytecode.bytecode, + len, + hash: bytecode.hash, + jumptable, + }) + } else { + Err(()) + } + } +} + +impl BytecodeLocked { + pub fn as_ptr(&self) -> *const u8 { + self.bytecode.as_ptr() + } + pub fn len(&self) -> usize { + self.len + } + + pub fn hash(&self) -> B256 { + self.hash + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + pub fn unlock(self) -> Bytecode { + Bytecode { + bytecode: self.bytecode, + hash: self.hash, + state: BytecodeState::Analysed { + len: self.len, + jumptable: self.jumptable, + }, + } + } + pub fn bytecode(&self) -> &[u8] { + self.bytecode.as_ref() + } + + pub fn original_bytecode_slice(&self) -> &[u8] { + &self.bytecode.as_ref()[..self.len] + } + + pub fn jumptable(&self) -> &ValidJumpAddress { + &self.jumptable + } +} diff --git a/crates/interpreter/src/interpreter/bytecode.rs b/crates/interpreter/src/interpreter/bytecode.rs deleted file mode 100644 index 7245933001..0000000000 --- a/crates/interpreter/src/interpreter/bytecode.rs +++ /dev/null @@ -1,318 +0,0 @@ -use super::contract::{AnalysisData, ValidJumpAddress}; -use crate::{common::keccak256, opcode, spec_opcode_gas, Spec, B256, KECCAK_EMPTY}; -use bytes::Bytes; -use std::sync::Arc; - -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum BytecodeState { - Raw, - Checked { - len: usize, - }, - Analysed { - len: usize, - jumptable: ValidJumpAddress, - }, -} - -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Bytecode { - #[cfg_attr(feature = "serde", serde(with = "crate::common::serde_hex_bytes"))] - bytecode: Bytes, - hash: B256, - state: BytecodeState, -} - -impl Default for Bytecode { - fn default() -> Self { - Bytecode::new() - } -} - -impl Bytecode { - pub fn new() -> Self { - // bytecode with one STOP opcode - Bytecode { - bytecode: vec![0].into(), - hash: KECCAK_EMPTY, - state: BytecodeState::Analysed { - len: 0, - jumptable: ValidJumpAddress::new(Arc::new(vec![AnalysisData::none()]), 0), - }, - } - } - - pub fn new_raw(bytecode: Bytes) -> Self { - let hash = if bytecode.is_empty() { - KECCAK_EMPTY - } else { - keccak256(&bytecode) - }; - Self { - bytecode, - hash, - state: BytecodeState::Raw, - } - } - - /// Create new raw Bytecode with hash - /// - /// # Safety - /// Hash need to be appropriate keccak256 over bytecode. - pub unsafe fn new_raw_with_hash(bytecode: Bytes, hash: B256) -> Self { - Self { - bytecode, - hash, - state: BytecodeState::Raw, - } - } - - /// Create new checked bytecode - /// - /// # Safety - /// Bytecode need to end with STOP (0x00) opcode as checked bytecode assumes - /// that it is safe to iterate over bytecode without checking lengths - pub unsafe fn new_checked(bytecode: Bytes, len: usize, hash: Option) -> Self { - let hash = match hash { - None if len == 0 => KECCAK_EMPTY, - None => keccak256(&bytecode), - Some(hash) => hash, - }; - Self { - bytecode, - hash, - state: BytecodeState::Checked { len }, - } - } - - /// Create new analysed bytecode - /// - /// # Safety - /// Same as new_checked, bytecode needs to end with STOP (0x00) opcode as checked bytecode assumes - /// that it is safe to iterate over bytecode without checking length. - /// And that ValidJumpAddress is valid. - pub unsafe fn new_analysed( - bytecode: Bytes, - len: usize, - jumptable: ValidJumpAddress, - hash: Option, - ) -> Self { - let hash = match hash { - None if len == 0 => KECCAK_EMPTY, - None => keccak256(&bytecode), - Some(hash) => hash, - }; - Self { - bytecode, - hash, - state: BytecodeState::Analysed { len, jumptable }, - } - } - - pub fn bytes(&self) -> &Bytes { - &self.bytecode - } - - pub fn hash(&self) -> B256 { - self.hash - } - - pub fn state(&self) -> &BytecodeState { - &self.state - } - - pub fn is_empty(&self) -> bool { - match self.state { - BytecodeState::Raw => self.bytecode.is_empty(), - BytecodeState::Checked { len } => len == 0, - BytecodeState::Analysed { len, .. } => len == 0, - } - } - - pub fn len(&self) -> usize { - match self.state { - BytecodeState::Raw => self.bytecode.len(), - BytecodeState::Checked { len, .. } => len, - BytecodeState::Analysed { len, .. } => len, - } - } - - pub fn to_checked(self) -> Self { - match self.state { - BytecodeState::Raw => { - let len = self.bytecode.len(); - let mut bytecode: Vec = Vec::from(self.bytecode.as_ref()); - bytecode.resize(len + 33, 0); - Self { - bytecode: bytecode.into(), - hash: self.hash, - state: BytecodeState::Checked { len }, - } - } - _ => self, - } - } - - pub fn to_analysed(self) -> Self { - let hash = self.hash; - let (bytecode, len) = match self.state { - BytecodeState::Raw => { - let len = self.bytecode.len(); - let checked = self.to_checked(); - (checked.bytecode, len) - } - BytecodeState::Checked { len } => (self.bytecode, len), - _ => return self, - }; - let jumptable = Self::analyze::(bytecode.as_ref()); - - Self { - bytecode, - hash, - state: BytecodeState::Analysed { len, jumptable }, - } - } - - pub fn lock_analysed(self) -> Option { - if let BytecodeState::Analysed { len, jumptable } = self.state { - Some(BytecodeLocked { - bytecode: self.bytecode, - len, - hash: self.hash, - jumptable, - }) - } else { - None - } - } - - pub fn lock(self) -> BytecodeLocked { - let bytecode = self.to_analysed::(); - bytecode.lock_analysed().expect("We have analysed bytecode") - } - - /// Analyze bytecode to get jumptable and gas blocks. - fn analyze(code: &[u8]) -> ValidJumpAddress { - let opcode_gas = spec_opcode_gas(SPEC::SPEC_ID); - - let mut analysis = ValidJumpAddress { - first_gas_block: 0, - analysis: Arc::new(vec![AnalysisData::none(); code.len()]), - }; - let jumps = Arc::get_mut(&mut analysis.analysis).unwrap(); - - let mut index = 0; - let mut gas_in_block: u32 = 0; - let mut block_start: usize = 0; - - // first gas block - while index < code.len() { - let opcode = *code.get(index).unwrap(); - let info = opcode_gas.get(opcode as usize).unwrap(); - analysis.first_gas_block += info.get_gas(); - - index += if info.is_push() { - ((opcode - opcode::PUSH1) + 2) as usize - } else { - 1 - }; - - if info.is_gas_block_end() { - block_start = index - 1; - if info.is_jump() { - jumps.get_mut(block_start).unwrap().set_is_jump(); - } - break; - } - } - - while index < code.len() { - let opcode = *code.get(index).unwrap(); - let info = opcode_gas.get(opcode as usize).unwrap(); - gas_in_block += info.get_gas(); - - if info.is_gas_block_end() { - if info.is_jump() { - jumps.get_mut(index).unwrap().set_is_jump(); - } - jumps - .get_mut(block_start) - .unwrap() - .set_gas_block(gas_in_block); - block_start = index; - gas_in_block = 0; - index += 1; - } else { - index += if info.is_push() { - ((opcode - opcode::PUSH1) + 2) as usize - } else { - 1 - }; - } - } - if gas_in_block != 0 { - jumps - .get_mut(block_start) - .unwrap() - .set_gas_block(gas_in_block); - } - analysis - } -} - -#[derive(Clone)] -pub struct BytecodeLocked { - bytecode: Bytes, - len: usize, - hash: B256, - jumptable: ValidJumpAddress, -} - -impl Default for BytecodeLocked { - fn default() -> Self { - Bytecode::default() - .lock_analysed() - .expect("Bytecode default is analysed code") - } -} - -impl BytecodeLocked { - pub fn as_ptr(&self) -> *const u8 { - self.bytecode.as_ptr() - } - pub fn len(&self) -> usize { - self.len - } - - pub fn hash(&self) -> B256 { - self.hash - } - - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - pub fn unlock(self) -> Bytecode { - Bytecode { - bytecode: self.bytecode, - hash: self.hash, - state: BytecodeState::Analysed { - len: self.len, - jumptable: self.jumptable, - }, - } - } - pub fn bytecode(&self) -> &[u8] { - self.bytecode.as_ref() - } - - pub fn original_bytecode_slice(&self) -> &[u8] { - &self.bytecode.as_ref()[..self.len] - } - - pub fn jumptable(&self) -> &ValidJumpAddress { - &self.jumptable - } -} diff --git a/crates/interpreter/src/interpreter/contract.rs b/crates/interpreter/src/interpreter/contract.rs index 4397d9ca47..efb8239a0e 100644 --- a/crates/interpreter/src/interpreter/contract.rs +++ b/crates/interpreter/src/interpreter/contract.rs @@ -1,7 +1,8 @@ -use super::bytecode::{Bytecode, BytecodeLocked}; -use crate::{alloc::vec::Vec, CallContext, Spec, B160, U256}; +use super::analysis::{to_analysed, BytecodeLocked}; +use crate::primitives::{Bytecode, Spec, B160, U256}; +use crate::CallContext; use bytes::Bytes; -use std::sync::Arc; +use revm_primitives::{Env, TransactTo}; #[derive(Clone, Default)] pub struct Contract { @@ -68,7 +69,10 @@ impl Contract { caller: B160, value: U256, ) -> Self { - let bytecode = bytecode.lock::(); + let bytecode = to_analysed::(bytecode) + .try_into() + .expect("it is analyzed"); + Self { input, bytecode, @@ -78,6 +82,22 @@ impl Contract { } } + /// Create new contract from environment + /// TODO: Add spec related match to analyze bytecode by env.cfg.spec_id variable + pub fn new_env(env: &Env, bytecode: Bytecode) -> Self { + let contract_address = match env.tx.transact_to { + TransactTo::Call(caller) => caller, + TransactTo::Create(..) => B160::zero(), + }; + Self::new::( + env.tx.data.clone(), + bytecode, + contract_address, + env.tx.caller, + env.tx.value, + ) + } + pub fn is_valid_jump(&self, possition: usize) -> bool { self.bytecode.jumptable().is_valid(possition) } @@ -103,51 +123,6 @@ impl Contract { ) } } - -/// Mapping of valid jump destination from code. -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ValidJumpAddress { - pub first_gas_block: u32, - /// Rc is used here so that we dont need to copy vector. We can move it to more suitable more accessable structure - /// without copying underlying vec. - pub analysis: Arc>, -} - -impl ValidJumpAddress { - pub fn new(analysis: Arc>, first_gas_block: u32) -> Self { - Self { - analysis, - first_gas_block, - } - } - /// Get the length of the valid mapping. This is the same as the - /// code bytes. - - pub fn len(&self) -> usize { - self.analysis.len() - } - - /// Returns true if the valid list is empty - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns `true` if the position is a valid jump destination. If - /// not, returns `false`. - pub fn is_valid(&self, position: usize) -> bool { - if position >= self.analysis.len() { - return false; - } - self.analysis[position].is_jump() - } - - pub fn gas_block(&self, position: usize) -> u64 { - self.analysis[position].gas_block() - } -} - #[cfg(test)] mod tests { use super::AnalysisData; diff --git a/crates/interpreter/src/interpreter/memory.rs b/crates/interpreter/src/interpreter/memory.rs index b63bbe48a8..517bb1be73 100644 --- a/crates/interpreter/src/interpreter/memory.rs +++ b/crates/interpreter/src/interpreter/memory.rs @@ -7,7 +7,7 @@ use core::{ /// A sequencial memory. It uses Rust's `Vec` for internal /// representation. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Memory { data: Vec, } diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 9f61458c63..7b20e8e758 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -1,10 +1,11 @@ -use crate::{alloc::vec::Vec, Return, B256, U256}; +use crate::primitives::{B256, U256}; +use crate::{alloc::vec::Vec, InstructionResult}; pub const STACK_LIMIT: usize = 1024; /// EVM stack. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Stack { data: Vec, } @@ -61,10 +62,10 @@ impl Stack { } #[inline(always)] - pub fn reduce_one(&mut self) -> Option { + pub fn reduce_one(&mut self) -> Option { let len = self.data.len(); if len < 1 { - return Some(Return::StackUnderflow); + return Some(InstructionResult::StackUnderflow); } unsafe { self.data.set_len(len - 1); @@ -75,8 +76,8 @@ impl Stack { #[inline] /// Pop a value from the stack. If the stack is already empty, returns the /// `StackUnderflow` error. - pub fn pop(&mut self) -> Result { - self.data.pop().ok_or(Return::StackUnderflow) + pub fn pop(&mut self) -> Result { + self.data.pop().ok_or(InstructionResult::StackUnderflow) } #[inline(always)] @@ -181,9 +182,9 @@ impl Stack { #[inline] /// Push a new value into the stack. If it will exceed the stack limit, /// returns `StackOverflow` error and leaves the stack unchanged. - pub fn push_b256(&mut self, value: B256) -> Result<(), Return> { + pub fn push_b256(&mut self, value: B256) -> Result<(), InstructionResult> { if self.data.len() + 1 > STACK_LIMIT { - return Err(Return::StackOverflow); + return Err(InstructionResult::StackOverflow); } self.data.push(U256::from_be_bytes(value.0)); Ok(()) @@ -192,9 +193,9 @@ impl Stack { #[inline] /// Push a new value into the stack. If it will exceed the stack limit, /// returns `StackOverflow` error and leaves the stack unchanged. - pub fn push(&mut self, value: U256) -> Result<(), Return> { + pub fn push(&mut self, value: U256) -> Result<(), InstructionResult> { if self.data.len() + 1 > STACK_LIMIT { - return Err(Return::StackOverflow); + return Err(InstructionResult::StackOverflow); } self.data.push(value); Ok(()) @@ -204,21 +205,21 @@ impl Stack { /// Peek a value at given index for the stack, where the top of /// the stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. - pub fn peek(&self, no_from_top: usize) -> Result { + pub fn peek(&self, no_from_top: usize) -> Result { if self.data.len() > no_from_top { Ok(self.data[self.data.len() - no_from_top - 1]) } else { - Err(Return::StackUnderflow) + Err(InstructionResult::StackUnderflow) } } #[inline(always)] - pub fn dup(&mut self) -> Option { + pub fn dup(&mut self) -> Option { let len = self.data.len(); if len < N { - Some(Return::StackUnderflow) + Some(InstructionResult::StackUnderflow) } else if len + 1 > STACK_LIMIT { - Some(Return::StackOverflow) + Some(InstructionResult::StackOverflow) } else { // Safety: check for out of bounds is done above and it makes this safe to do. unsafe { @@ -230,10 +231,10 @@ impl Stack { } #[inline(always)] - pub fn swap(&mut self) -> Option { + pub fn swap(&mut self) -> Option { let len = self.data.len(); if len <= N { - return Some(Return::StackUnderflow); + return Some(InstructionResult::StackUnderflow); } // Safety: length is checked before so we are okay to switch bytes in unsafe way. unsafe { @@ -246,10 +247,10 @@ impl Stack { /// push slice onto memory it is expected to be max 32 bytes and be contains inside B256 #[inline(always)] - pub fn push_slice(&mut self, slice: &[u8]) -> Option { + pub fn push_slice(&mut self, slice: &[u8]) -> Option { let new_len = self.data.len() + 1; if new_len > STACK_LIMIT { - return Some(Return::StackOverflow); + return Some(InstructionResult::StackOverflow); } let slot; @@ -305,13 +306,13 @@ impl Stack { /// Set a value at given index for the stack, where the top of the /// stack is at index `0`. If the index is too large, /// `StackError::Underflow` is returned. - pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), Return> { + pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), InstructionResult> { if self.data.len() > no_from_top { let len = self.data.len(); self.data[len - no_from_top - 1] = val; Ok(()) } else { - Err(Return::StackUnderflow) + Err(InstructionResult::StackUnderflow) } } } diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 1b773b3ea0..0928443723 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -1,33 +1,28 @@ #![allow(dead_code)] //#![no_std] -pub mod bits; -pub mod common; pub mod gas; mod host; +pub mod inner_models; +pub mod instruction_result; mod instructions; mod interpreter; -pub mod models; -pub mod specification; extern crate alloc; pub(crate) const USE_GAS: bool = !cfg!(feature = "no_gas_measuring"); // Reexport primary types. -pub use bits::{B160, B256}; -pub use bytes::Bytes; -pub use gas::Gas; pub use host::{DummyHost, Host}; -pub use instructions::{ - opcode::{self, spec_opcode_gas, OpCode, OPCODE_JUMPMAP}, - Return, -}; +pub use inner_models::*; +pub use instruction_result::InstructionResult; +pub use instructions::opcode::{self, spec_opcode_gas, OpCode, OPCODE_JUMPMAP}; pub use interpreter::*; -pub use interpreter::{ - Bytecode, BytecodeLocked, BytecodeState, Contract, Interpreter, Memory, Stack, -}; -pub use models::*; +pub use interpreter::{BytecodeLocked, Contract, Interpreter, Memory, Stack}; pub use ruint; pub use ruint::aliases::U256; -pub use specification::*; + +pub use hashbrown::hash_map; +pub use hashbrown::HashMap; + +pub use revm_primitives as primitives; diff --git a/crates/interpreter/src/models.rs b/crates/interpreter/src/models.rs deleted file mode 100644 index dc15eb8b00..0000000000 --- a/crates/interpreter/src/models.rs +++ /dev/null @@ -1,470 +0,0 @@ -use core::cmp::min; - -use crate::{alloc::vec::Vec, interpreter::bytecode::Bytecode, Return, SpecId, B160, B256, U256}; -use bytes::Bytes; -use hashbrown::HashMap as Map; -use hex_literal::hex; - -pub const KECCAK_EMPTY: B256 = B256(hex!( - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" -)); - -#[derive(Debug, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Account { - /// Balance of the account. - pub info: AccountInfo, - /// storage cache - pub storage: Map, - /// If account is newly created, we will not ask database for storage values - pub storage_cleared: bool, - /// if account is destroyed it will be scheduled for removal. - pub is_destroyed: bool, - /// if account is touched - pub is_touched: bool, - /// used only for pre spurious dragon hardforks where exisnting and empty was two saparate states. - /// it became same state after EIP-161: State trie clearing - pub is_not_existing: bool, -} - -impl Account { - pub fn is_empty(&self) -> bool { - self.info.is_empty() - } - pub fn new_not_existing() -> Self { - Self { - info: AccountInfo::default(), - storage: Map::new(), - storage_cleared: false, - is_destroyed: false, - is_touched: false, - is_not_existing: true, - } - } -} - -impl From for Account { - fn from(info: AccountInfo) -> Self { - Self { - info, - storage: Map::new(), - storage_cleared: false, - is_destroyed: false, - is_touched: false, - is_not_existing: false, - } - } -} - -#[derive(Debug, Clone, Default, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct StorageSlot { - pub original_value: U256, - /// When loaded with sload present value is set to original value - pub present_value: U256, -} - -impl StorageSlot { - pub fn new(original: U256) -> Self { - Self { - original_value: original, - present_value: original, - } - } - - /// Returns true if the present value differs from the original value - pub fn is_changed(&self) -> bool { - self.original_value != self.present_value - } - - pub fn original_value(&self) -> U256 { - self.original_value - } - - pub fn present_value(&self) -> U256 { - self.present_value - } -} - -/// AccountInfo account information. -#[derive(Clone, Debug, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct AccountInfo { - /// Account balance. - pub balance: U256, - /// Account nonce. - pub nonce: u64, - /// code hash, - pub code_hash: B256, - /// code: if None, `code_by_hash` will be used to fetch it if code needs to be loaded from - /// inside of revm. - pub code: Option, -} - -impl Default for AccountInfo { - fn default() -> Self { - Self { - balance: U256::ZERO, - code_hash: KECCAK_EMPTY, - code: Some(Bytecode::new()), - nonce: 0, - } - } -} - -impl PartialEq for AccountInfo { - fn eq(&self, other: &Self) -> bool { - self.balance == other.balance - && self.nonce == other.nonce - && self.code_hash == other.code_hash - } -} - -impl AccountInfo { - pub fn new(balance: U256, nonce: u64, code: Bytecode) -> Self { - let code_hash = code.hash(); - Self { - balance, - nonce, - code: Some(code), - code_hash, - } - } - - pub fn is_empty(&self) -> bool { - let code_empty = self.code_hash == KECCAK_EMPTY || self.code_hash == B256::zero(); - self.balance == U256::ZERO && self.nonce == 0 && code_empty - } - - pub fn exists(&self) -> bool { - !self.is_empty() - } - - pub fn from_balance(balance: U256) -> Self { - AccountInfo { - balance, - ..Default::default() - } - } -} - -/// Inputs for a call. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CallInputs { - /// The target of the call. - pub contract: B160, - /// The transfer, if any, in this call. - pub transfer: Transfer, - /// The call data of the call. - #[cfg_attr(feature = "serde", serde(with = "crate::common::serde_hex_bytes"))] - pub input: Bytes, - /// The gas limit of the call. - pub gas_limit: u64, - /// The context of the call. - pub context: CallContext, - /// Is static call - pub is_static: bool, -} - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CreateInputs { - pub caller: B160, - pub scheme: CreateScheme, - pub value: U256, - #[cfg_attr(feature = "serde", serde(with = "crate::common::serde_hex_bytes"))] - pub init_code: Bytes, - pub gas_limit: u64, -} - -pub struct CreateData {} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TransactTo { - Call(B160), - Create(CreateScheme), -} - -impl TransactTo { - pub fn create() -> Self { - Self::Create(CreateScheme::Create) - } -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TransactOut { - None, - #[cfg_attr(feature = "serde", serde(with = "crate::common::serde_hex_bytes"))] - Call(Bytes), - Create( - #[cfg_attr(feature = "serde", serde(with = "crate::common::serde_hex_bytes"))] Bytes, - Option, - ), -} - -/// Create scheme. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum CreateScheme { - /// Legacy create scheme of `CREATE`. - Create, - /// Create scheme of `CREATE2`. - Create2 { - /// Salt. - salt: U256, - }, -} - -/// Call schemes. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum CallScheme { - /// `CALL` - Call, - /// `CALLCODE` - CallCode, - /// `DELEGATECALL` - DelegateCall, - /// `STATICCALL` - StaticCall, -} - -/// CallContext of the runtime. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CallContext { - /// Execution address. - pub address: B160, - /// Caller of the EVM. - pub caller: B160, - /// The address the contract code was loaded from, if any. - pub code_address: B160, - /// Apparent value of the EVM. - pub apparent_value: U256, - /// The scheme used for the call. - pub scheme: CallScheme, -} - -impl Default for CallContext { - fn default() -> Self { - CallContext { - address: B160::default(), - caller: B160::default(), - code_address: B160::default(), - apparent_value: U256::default(), - scheme: CallScheme::Call, - } - } -} - -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Env { - pub cfg: CfgEnv, - pub block: BlockEnv, - pub tx: TxEnv, -} -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BlockEnv { - pub number: U256, - /// Coinbase or miner or address that created and signed the block. - /// Address where we are going to send gas spend - pub coinbase: B160, - pub timestamp: U256, - /// Difficulty is removed and not used after Paris (aka TheMerge). Value is replaced with prevrandao. - pub difficulty: U256, - /// Prevrandao is used after Paris (aka TheMerge) instead of the difficulty value. - /// NOTE: prevrandao can be found in block in place of mix_hash. - pub prevrandao: Option, - /// basefee is added in EIP1559 London upgrade - pub basefee: U256, - pub gas_limit: U256, -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TxEnv { - /// Caller or Author or tx signer - pub caller: B160, - pub gas_limit: u64, - pub gas_price: U256, - pub gas_priority_fee: Option, - pub transact_to: TransactTo, - pub value: U256, - #[cfg_attr(feature = "serde", serde(with = "crate::common::serde_hex_bytes"))] - pub data: Bytes, - pub chain_id: Option, - pub nonce: Option, - pub access_list: Vec<(B160, Vec)>, -} -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CfgEnv { - pub chain_id: U256, - pub spec_id: SpecId, - /// If all precompiles have some balance we can skip initially fetching them from the database. - /// This is is not really needed on mainnet, and defaults to false, but in most cases it is - /// safe to be set to `true`, depending on the chain. - pub perf_all_precompiles_have_balance: bool, - /// Bytecode that is created with CREATE/CREATE2 is by default analysed and jumptable is created. - /// This is very benefitial for testing and speeds up execution of that bytecode when. - /// It will have side effect if it is enabled in client that switches between forks. - /// Default: Analyse - pub perf_analyse_created_bytecodes: AnalysisKind, - /// If some it will effects EIP-170: Contract code size limit. Usefull to increase this because of tests. - /// By default it is 0x6000 (~25kb). - pub limit_contract_code_size: Option, - /// A hard memory limit in bytes beyond which [Memory] cannot be resized. - /// - /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to - /// a sane value to prevent memory allocation panics. Defaults to `2^32 - 1` bytes per - /// EIP-1985. - #[cfg(feature = "memory_limit")] - pub memory_limit: u64, - /// Skip balance checks if true. Adds transaction cost to balance to ensure execution doesn't fail. - #[cfg(feature = "optional_balance_check")] - pub disable_balance_check: bool, - /// There are use cases where it's allowed to provide a gas limit that's higher than a block's gas limit. To that - /// end, you can disable the block gas limit validation. - /// By default, it is set to `false`. - #[cfg(feature = "optional_block_gas_limit")] - pub disable_block_gas_limit: bool, - /// EIP-3607 rejects transactions from senders with deployed code. In development, it can be desirable to simulate - /// calls from contracts, which this setting allows. - /// By default, it is set to `false`. - #[cfg(feature = "optional_eip3607")] - pub disable_eip3607: bool, - /// Disables all gas refunds. This is useful when using chains that have gas refunds disabled e.g. Avalanche. - /// Reasoning behind removing gas refunds can be found in EIP-3298. - /// By default, it is set to `false`. - #[cfg(feature = "optional_gas_refund")] - pub disable_gas_refund: bool, -} - -#[derive(Clone, Default, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum AnalysisKind { - Raw, - Check, - #[default] - Analyse, -} - -impl Default for CfgEnv { - fn default() -> CfgEnv { - CfgEnv { - chain_id: U256::from(1), - spec_id: SpecId::LATEST, - perf_all_precompiles_have_balance: false, - perf_analyse_created_bytecodes: Default::default(), - limit_contract_code_size: None, - #[cfg(feature = "memory_limit")] - memory_limit: 2u64.pow(32) - 1, - #[cfg(feature = "optional_balance_check")] - disable_balance_check: false, - #[cfg(feature = "optional_block_gas_limit")] - disable_block_gas_limit: false, - #[cfg(feature = "optional_eip3607")] - disable_eip3607: false, - #[cfg(feature = "optional_gas_refund")] - disable_gas_refund: false, - } - } -} - -impl Default for BlockEnv { - fn default() -> BlockEnv { - BlockEnv { - gas_limit: U256::MAX, - number: U256::ZERO, - coinbase: B160::zero(), - timestamp: U256::from(1), - difficulty: U256::ZERO, - prevrandao: Some(B256::zero()), - basefee: U256::ZERO, - } - } -} - -impl Default for TxEnv { - fn default() -> TxEnv { - TxEnv { - caller: B160::zero(), - gas_limit: u64::MAX, - gas_price: U256::ZERO, - gas_priority_fee: None, - transact_to: TransactTo::Call(B160::zero()), //will do nothing - value: U256::ZERO, - data: Bytes::new(), - chain_id: None, - nonce: None, - access_list: Vec::new(), - } - } -} - -impl Env { - pub fn effective_gas_price(&self) -> U256 { - if self.tx.gas_priority_fee.is_none() { - self.tx.gas_price - } else { - min( - self.tx.gas_price, - self.block.basefee + self.tx.gas_priority_fee.unwrap(), - ) - } - } -} - -/// Transfer from source to target, with given value. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Transfer { - /// Source address. - pub source: B160, - /// Target address. - pub target: B160, - /// Transfer value. - pub value: U256, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Log { - pub address: B160, - pub topics: Vec, - #[cfg_attr(feature = "serde", serde(with = "crate::common::serde_hex_bytes"))] - pub data: Bytes, -} - -#[derive(Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct SelfDestructResult { - pub had_value: bool, - pub target_exists: bool, - pub is_cold: bool, - pub previously_destroyed: bool, -} - -#[derive(Clone, Debug)] -pub struct ExecutionResult { - pub exit_reason: Return, - pub out: TransactOut, - pub gas_used: u64, - pub gas_refunded: u64, - pub logs: Vec, -} - -impl ExecutionResult { - pub fn new_with_reason(reason: Return) -> ExecutionResult { - ExecutionResult { - exit_reason: reason, - out: TransactOut::None, - gas_used: 0, - gas_refunded: 0, - logs: Vec::new(), - } - } -} diff --git a/crates/primitives/CHANGELOG.md b/crates/primitives/CHANGELOG.md new file mode 100644 index 0000000000..7e7c3799e5 --- /dev/null +++ b/crates/primitives/CHANGELOG.md @@ -0,0 +1,3 @@ +# v3.0.0 + +Interpreter was extracted from main REVM crate. Before v3.0.0 version, this code was part of REVM. \ No newline at end of file diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml new file mode 100644 index 0000000000..a159fa3a40 --- /dev/null +++ b/crates/primitives/Cargo.toml @@ -0,0 +1,79 @@ +[package] +authors = ["Dragan Rakita "] +description = "REVM primitives" +edition = "2021" +keywords = ["no_std", "ethereum", "vm", "evm", "revm", "primitives", "types"] +license = "MIT" +name = "revm-primitives" +repository = "https://github.com/bluealloy/revm" +version = "3.0.0" +readme = "../../README.md" + +[dependencies] +bytes = { version = "1.1", default-features = false } +hashbrown = { version = "0.13" } +hex = { version = "0.4", default-features = false } +rlp = { version = "0.5", default-features = false } # used for create2 address calculation +ruint = { version = "1.7.0", features = ["primitive-types", "rlp"] } +auto_impl = "1.0" + +# bits B256 B160 crate +fixed-hash = { version = "0.8", default-features = false, features = [ + "rustc-hex", +] } + +#utility +hex-literal = "0.3" +derive_more = "0.99" +enumn = "0.1" + +# sha3 keccak hasher +sha3 = { version = "0.10", default-features = false, features = [] } + +# optional +serde = { version = "1.0", features = ["derive", "rc"], optional = true } +arbitrary = { version = "1.2", features = ["derive"], optional = true } +proptest = { version = "1.0", optional = true } +proptest-derive = { version = "0.3", optional = true } + +[dev-dependencies] +arbitrary = { version = "1.2", features = ["derive"] } +proptest = { version = "1.0" } +proptest-derive = "0.3" +ruint = { version = "1.7.0", features = [ + "primitive-types", + "rlp", + "proptest", + "arbitrary", +] } + +[features] +default = ["std"] +dev = [ + "memory_limit", + "optional_balance_check", + "optional_block_gas_limit", + "optional_eip3607", + "optional_gas_refund", +] +memory_limit = [] +no_gas_measuring = [] +optional_balance_check = [] +optional_block_gas_limit = [] +optional_eip3607 = [] +optional_gas_refund = [] +std = ["bytes/std", "rlp/std", "hex/std"] +serde = [ + "dep:serde", + "hex/serde", + "hashbrown/serde", + "ruint/serde", + "bytes/serde", +] +arbitrary = [ + "ruint/arbitrary", + "ruint/proptest", + "dep:arbitrary", + "dep:proptest", + "dep:proptest-derive", +] \ No newline at end of file diff --git a/crates/revmjs/LICENSE b/crates/primitives/LICENSE similarity index 100% rename from crates/revmjs/LICENSE rename to crates/primitives/LICENSE diff --git a/crates/interpreter/src/bits.rs b/crates/primitives/src/bits.rs similarity index 85% rename from crates/interpreter/src/bits.rs rename to crates/primitives/src/bits.rs index 095dc6ebf4..5f76562c7a 100644 --- a/crates/interpreter/src/bits.rs +++ b/crates/primitives/src/bits.rs @@ -80,37 +80,12 @@ impl<'de> serde::Deserialize<'de> for B160 { #[cfg(feature = "serde")] mod serialize { - use alloc::{string::String, vec::Vec}; + use alloc::string::String; use core::{fmt, result::Result}; use serde::{de, Deserializer, Serializer}; static CHARS: &[u8] = b"0123456789abcdef"; - /// Serialize given bytes to a 0x-prefixed hex string. - /// - /// If `skip_leading_zero` initial 0s will not be printed out, - /// unless the byte string is empty, in which case `0x0` will be returned. - /// The results are consistent with `serialize_uint` output if the flag is - /// on and `serialize_raw` if the flag is off. - pub fn to_hex(bytes: &[u8], skip_leading_zero: bool) -> String { - let bytes = if skip_leading_zero { - let non_zero = bytes.iter().take_while(|b| **b == 0).count(); - let bytes = &bytes[non_zero..]; - if bytes.is_empty() { - return "0x0".into(); - } else { - bytes - } - } else if bytes.is_empty() { - return "0x".into(); - } else { - bytes - }; - - let mut slice = vec![0u8; (bytes.len() + 1) * 2]; - to_hex_raw(&mut slice, bytes, skip_leading_zero).into() - } - fn to_hex_raw<'a>(v: &'a mut [u8], bytes: &[u8], skip_leading_zero: bool) -> &'a str { assert!(v.len() > 1 + bytes.len() * 2); @@ -161,17 +136,6 @@ mod serialize { } } - /// Decode given (both 0x-prefixed or not) hex string into a vector of bytes. - /// - /// Returns an error if non-hex characters are present. - pub fn from_hex(v: &str) -> Result, FromHexError> { - let (v, stripped) = v.strip_prefix("0x").map_or((v, false), |v| (v, true)); - - let mut bytes = vec![0u8; (v.len() + 1) / 2]; - from_hex_raw(v, &mut bytes, stripped)?; - Ok(bytes) - } - /// Decode given 0x-prefix-stripped hex string into provided slice. /// Used internally by `from_hex` and `deserialize_check_len`. /// @@ -234,6 +198,7 @@ mod serialize { /// Exact length in bytes. Exact(&'a mut [u8]), /// A bytes length between (min; slice.len()]. + #[allow(dead_code)] Between(usize, &'a mut [u8]), } diff --git a/crates/primitives/src/bytecode.rs b/crates/primitives/src/bytecode.rs new file mode 100644 index 0000000000..7d0d041105 --- /dev/null +++ b/crates/primitives/src/bytecode.rs @@ -0,0 +1,135 @@ +mod jump_table; + +use crate::{keccak256, B256, KECCAK_EMPTY}; +use bytes::Bytes; +pub use jump_table::{Analysis, AnalysisData, ValidJumpAddress}; +use std::sync::Arc; + +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum BytecodeState { + Raw, + Checked { + len: usize, + }, + Analysed { + len: usize, + jumptable: ValidJumpAddress, + }, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Bytecode { + #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] + pub bytecode: Bytes, + pub hash: B256, + pub state: BytecodeState, +} + +impl Default for Bytecode { + fn default() -> Self { + Bytecode::new() + } +} + +impl Bytecode { + /// Create [`Bytecode`] with one STOP opcode. + pub fn new() -> Self { + Bytecode { + bytecode: vec![0].into(), + hash: KECCAK_EMPTY, + state: BytecodeState::Analysed { + len: 0, + jumptable: ValidJumpAddress::new(Arc::new(vec![AnalysisData::none()]), 0), + }, + } + } + + pub fn new_raw(bytecode: Bytes) -> Self { + let hash = if bytecode.is_empty() { + KECCAK_EMPTY + } else { + keccak256(&bytecode) + }; + Self { + bytecode, + hash, + state: BytecodeState::Raw, + } + } + + /// Create new raw Bytecode with hash + /// + /// # Safety + /// Hash need to be appropriate keccak256 over bytecode. + pub unsafe fn new_raw_with_hash(bytecode: Bytes, hash: B256) -> Self { + Self { + bytecode, + hash, + state: BytecodeState::Raw, + } + } + + /// Create new checked bytecode + /// + /// # Safety + /// Bytecode need to end with STOP (0x00) opcode as checked bytecode assumes + /// that it is safe to iterate over bytecode without checking lengths + pub unsafe fn new_checked(bytecode: Bytes, len: usize, hash: Option) -> Self { + let hash = match hash { + None if len == 0 => KECCAK_EMPTY, + None => keccak256(&bytecode), + Some(hash) => hash, + }; + Self { + bytecode, + hash, + state: BytecodeState::Checked { len }, + } + } + + pub fn bytes(&self) -> &Bytes { + &self.bytecode + } + + pub fn hash(&self) -> B256 { + self.hash + } + + pub fn state(&self) -> &BytecodeState { + &self.state + } + + pub fn is_empty(&self) -> bool { + match self.state { + BytecodeState::Raw => self.bytecode.is_empty(), + BytecodeState::Checked { len } => len == 0, + BytecodeState::Analysed { len, .. } => len == 0, + } + } + + pub fn len(&self) -> usize { + match self.state { + BytecodeState::Raw => self.bytecode.len(), + BytecodeState::Checked { len, .. } => len, + BytecodeState::Analysed { len, .. } => len, + } + } + + pub fn to_checked(self) -> Self { + match self.state { + BytecodeState::Raw => { + let len = self.bytecode.len(); + let mut bytecode: Vec = Vec::from(self.bytecode.as_ref()); + bytecode.resize(len + 33, 0); + Self { + bytecode: bytecode.into(), + hash: self.hash, + state: BytecodeState::Checked { len }, + } + } + _ => self, + } + } +} diff --git a/crates/primitives/src/bytecode/jump_table.rs b/crates/primitives/src/bytecode/jump_table.rs new file mode 100644 index 0000000000..3372927796 --- /dev/null +++ b/crates/primitives/src/bytecode/jump_table.rs @@ -0,0 +1,87 @@ +use alloc::sync::Arc; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Analysis { + JumpDest, + GasBlockEnd, //contains gas for next block + None, +} + +const JUMP_MASK: u32 = 0x80000000; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct AnalysisData { + /// This variable packs two informations: + /// IS_JUMP (1bit) | gas block ( 31bits) + is_jump_and_gas_block: u32, +} + +impl AnalysisData { + pub fn none() -> Self { + AnalysisData { + is_jump_and_gas_block: 0, + } + } + + pub fn set_is_jump(&mut self) { + self.is_jump_and_gas_block |= JUMP_MASK; + } + + pub fn set_gas_block(&mut self, gas_block: u32) { + let jump = self.is_jump_and_gas_block & JUMP_MASK; + self.is_jump_and_gas_block = gas_block | jump; + } + + pub fn is_jump(&self) -> bool { + self.is_jump_and_gas_block & JUMP_MASK == JUMP_MASK + } + + pub fn gas_block(&self) -> u64 { + (self.is_jump_and_gas_block & (!JUMP_MASK)) as u64 + } +} + +/// Mapping of valid jump destination from code. +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ValidJumpAddress { + pub first_gas_block: u32, + /// Rc is used here so that we dont need to copy vector. We can move it to more suitable more accessable structure + /// without copying underlying vec. + pub analysis: Arc>, +} + +impl ValidJumpAddress { + pub fn new(analysis: Arc>, first_gas_block: u32) -> Self { + Self { + analysis, + first_gas_block, + } + } + /// Get the length of the valid mapping. This is the same as the + /// code bytes. + + pub fn len(&self) -> usize { + self.analysis.len() + } + + /// Returns true if the valid list is empty + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns `true` if the position is a valid jump destination. If + /// not, returns `false`. + pub fn is_valid(&self, position: usize) -> bool { + if position >= self.analysis.len() { + return false; + } + self.analysis[position].is_jump() + } + + pub fn gas_block(&self, position: usize) -> u64 { + self.analysis[position].gas_block() + } +} diff --git a/crates/primitives/src/db.rs b/crates/primitives/src/db.rs new file mode 100644 index 0000000000..8ae75316ad --- /dev/null +++ b/crates/primitives/src/db.rs @@ -0,0 +1,79 @@ +pub mod components; + +use crate::AccountInfo; +use crate::U256; +use crate::{Account, Bytecode}; +use crate::{B160, B256}; +use auto_impl::auto_impl; +use hashbrown::HashMap as Map; + +pub use components::{ + BlockHash, BlockHashRef, DatabaseComponentError, DatabaseComponentRefError, DatabaseComponents, + State, StateRef, +}; + +#[auto_impl(& mut, Box)] +pub trait Database { + type Error; + /// Get basic account information. + fn basic(&mut self, address: B160) -> Result, Self::Error>; + /// Get account code by its hash + fn code_by_hash(&mut self, code_hash: B256) -> Result; + /// Get storage value of address at index. + fn storage(&mut self, address: B160, index: U256) -> Result; + + // History related + fn block_hash(&mut self, number: U256) -> Result; +} + +#[auto_impl(& mut, Box)] +pub trait DatabaseCommit { + fn commit(&mut self, changes: Map); +} + +#[auto_impl(&, Box, Arc)] +pub trait DatabaseRef { + type Error; + /// Whether account at address exists. + //fn exists(&self, address: B160) -> Option; + /// Get basic account information. + fn basic(&self, address: B160) -> Result, Self::Error>; + /// Get account code by its hash + fn code_by_hash(&self, code_hash: B256) -> Result; + /// Get storage value of address at index. + fn storage(&self, address: B160, index: U256) -> Result; + + // History related + fn block_hash(&self, number: U256) -> Result; +} + +pub struct RefDBWrapper<'a, Error> { + pub db: &'a dyn DatabaseRef, +} + +impl<'a, Error> RefDBWrapper<'a, Error> { + pub fn new(db: &'a dyn DatabaseRef) -> Self { + Self { db } + } +} + +impl<'a, Error> Database for RefDBWrapper<'a, Error> { + type Error = Error; + /// Get basic account information. + fn basic(&mut self, address: B160) -> Result, Self::Error> { + self.db.basic(address) + } + /// Get account code by its hash + fn code_by_hash(&mut self, code_hash: B256) -> Result { + self.db.code_by_hash(code_hash) + } + /// Get storage value of address at index. + fn storage(&mut self, address: B160, index: U256) -> Result { + self.db.storage(address, index) + } + + // History related + fn block_hash(&mut self, number: U256) -> Result { + self.db.block_hash(number) + } +} diff --git a/crates/primitives/src/db/components.rs b/crates/primitives/src/db/components.rs new file mode 100644 index 0000000000..7f636692f7 --- /dev/null +++ b/crates/primitives/src/db/components.rs @@ -0,0 +1,78 @@ +//! Database that is split on State and BlockHash traits. +pub mod block_hash; +pub mod state; + +pub use block_hash::{BlockHash, BlockHashRef}; +pub use state::{State, StateRef}; + +use crate::{ + db::{Database, DatabaseRef}, + AccountInfo, Bytecode, B160, B256, U256, +}; + +pub struct DatabaseComponents { + state: S, + block_hash: BH, +} + +pub enum DatabaseComponentError { + State(S::Error), + BlockHash(BH::Error), +} + +pub enum DatabaseComponentRefError { + State(S::Error), + BlockHash(BH::Error), +} + +impl Database for DatabaseComponents { + type Error = DatabaseComponentError; + + fn basic(&mut self, address: B160) -> Result, Self::Error> { + self.state.basic(address).map_err(Self::Error::State) + } + + fn code_by_hash(&mut self, code_hash: B256) -> Result { + self.state + .code_by_hash(code_hash) + .map_err(Self::Error::State) + } + + fn storage(&mut self, address: B160, index: U256) -> Result { + self.state + .storage(address, index) + .map_err(Self::Error::State) + } + + fn block_hash(&mut self, number: U256) -> Result { + self.block_hash + .block_hash(number) + .map_err(Self::Error::BlockHash) + } +} + +impl DatabaseRef for DatabaseComponents { + type Error = DatabaseComponentRefError; + + fn basic(&self, address: B160) -> Result, Self::Error> { + self.state.basic(address).map_err(Self::Error::State) + } + + fn code_by_hash(&self, code_hash: B256) -> Result { + self.state + .code_by_hash(code_hash) + .map_err(Self::Error::State) + } + + fn storage(&self, address: B160, index: U256) -> Result { + self.state + .storage(address, index) + .map_err(Self::Error::State) + } + + fn block_hash(&self, number: U256) -> Result { + self.block_hash + .block_hash(number) + .map_err(Self::Error::BlockHash) + } +} diff --git a/crates/primitives/src/db/components/block_hash.rs b/crates/primitives/src/db/components/block_hash.rs new file mode 100644 index 0000000000..55c7dd1919 --- /dev/null +++ b/crates/primitives/src/db/components/block_hash.rs @@ -0,0 +1,32 @@ +//! BlockHash database component from [`crate::db::Database`] +//! it is used inside [crate::db::DatabaseComponents`] + +use crate::{B256, U256}; +use auto_impl::auto_impl; + +#[auto_impl(& mut, Box)] +pub trait BlockHash { + type Error; + + /// Get block hash by block number + fn block_hash(&mut self, number: U256) -> Result; +} + +#[auto_impl(&, Box, Arc)] +pub trait BlockHashRef { + type Error; + + /// Get block hash by block number + fn block_hash(&self, number: U256) -> Result; +} + +impl BlockHash for &T +where + T: BlockHashRef, +{ + type Error = ::Error; + + fn block_hash(&mut self, number: U256) -> Result { + BlockHashRef::block_hash(*self, number) + } +} diff --git a/crates/primitives/src/db/components/state.rs b/crates/primitives/src/db/components/state.rs new file mode 100644 index 0000000000..3f9d3e5f6c --- /dev/null +++ b/crates/primitives/src/db/components/state.rs @@ -0,0 +1,50 @@ +//! State database component from [`crate::db::Database`] +//! it is used inside [crate::db::DatabaseComponents`] + +use crate::{AccountInfo, Bytecode, B160, B256, U256}; +use auto_impl::auto_impl; + +#[auto_impl(& mut, Box)] +pub trait State { + type Error; + + /// Get basic account information. + fn basic(&mut self, address: B160) -> Result, Self::Error>; + /// Get account code by its hash + fn code_by_hash(&mut self, code_hash: B256) -> Result; + /// Get storage value of address at index. + fn storage(&mut self, address: B160, index: U256) -> Result; +} + +#[auto_impl(&, Box, Arc)] +pub trait StateRef { + type Error; + + /// Whether account at address exists. + //fn exists(&self, address: B160) -> Option; + /// Get basic account information. + fn basic(&self, address: B160) -> Result, Self::Error>; + /// Get account code by its hash + fn code_by_hash(&self, code_hash: B256) -> Result; + /// Get storage value of address at index. + fn storage(&self, address: B160, index: U256) -> Result; +} + +impl State for &T +where + T: StateRef, +{ + type Error = ::Error; + + fn basic(&mut self, address: B160) -> Result, Self::Error> { + StateRef::basic(*self, address) + } + + fn code_by_hash(&mut self, code_hash: B256) -> Result { + StateRef::code_by_hash(*self, code_hash) + } + + fn storage(&mut self, address: B160, index: U256) -> Result { + StateRef::storage(*self, address, index) + } +} diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs new file mode 100644 index 0000000000..5ffc615d15 --- /dev/null +++ b/crates/primitives/src/env.rs @@ -0,0 +1,190 @@ +use crate::{alloc::vec::Vec, SpecId, B160, B256, U256}; +use bytes::Bytes; +use core::cmp::min; + +#[derive(Clone, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Env { + pub cfg: CfgEnv, + pub block: BlockEnv, + pub tx: TxEnv, +} +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BlockEnv { + pub number: U256, + /// Coinbase or miner or address that created and signed the block. + /// Address where we are going to send gas spend + pub coinbase: B160, + pub timestamp: U256, + /// Difficulty is removed and not used after Paris (aka TheMerge). Value is replaced with prevrandao. + pub difficulty: U256, + /// Prevrandao is used after Paris (aka TheMerge) instead of the difficulty value. + /// NOTE: prevrandao can be found in block in place of mix_hash. + pub prevrandao: Option, + /// basefee is added in EIP1559 London upgrade + pub basefee: U256, + pub gas_limit: U256, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TxEnv { + /// Caller or Author or tx signer + pub caller: B160, + pub gas_limit: u64, + pub gas_price: U256, + pub gas_priority_fee: Option, + pub transact_to: TransactTo, + pub value: U256, + #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] + pub data: Bytes, + pub chain_id: Option, + pub nonce: Option, + pub access_list: Vec<(B160, Vec)>, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum TransactTo { + Call(B160), + Create(CreateScheme), +} + +impl TransactTo { + pub fn create() -> Self { + Self::Create(CreateScheme::Create) + } +} + +/// Create scheme. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum CreateScheme { + /// Legacy create scheme of `CREATE`. + Create, + /// Create scheme of `CREATE2`. + Create2 { + /// Salt. + salt: U256, + }, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CfgEnv { + pub chain_id: U256, + pub spec_id: SpecId, + /// If all precompiles have some balance we can skip initially fetching them from the database. + /// This is is not really needed on mainnet, and defaults to false, but in most cases it is + /// safe to be set to `true`, depending on the chain. + pub perf_all_precompiles_have_balance: bool, + /// Bytecode that is created with CREATE/CREATE2 is by default analysed and jumptable is created. + /// This is very benefitial for testing and speeds up execution of that bytecode when. + /// It will have side effect if it is enabled in client that switches between forks. + /// Default: Analyse + pub perf_analyse_created_bytecodes: AnalysisKind, + /// If some it will effects EIP-170: Contract code size limit. Usefull to increase this because of tests. + /// By default it is 0x6000 (~25kb). + pub limit_contract_code_size: Option, + /// A hard memory limit in bytes beyond which [Memory] cannot be resized. + /// + /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to + /// a sane value to prevent memory allocation panics. Defaults to `2^32 - 1` bytes per + /// EIP-1985. + #[cfg(feature = "memory_limit")] + pub memory_limit: u64, + /// Skip balance checks if true. Adds transaction cost to balance to ensure execution doesn't fail. + #[cfg(feature = "optional_balance_check")] + pub disable_balance_check: bool, + /// There are use cases where it's allowed to provide a gas limit that's higher than a block's gas limit. To that + /// end, you can disable the block gas limit validation. + /// By default, it is set to `false`. + #[cfg(feature = "optional_block_gas_limit")] + pub disable_block_gas_limit: bool, + /// EIP-3607 rejects transactions from senders with deployed code. In development, it can be desirable to simulate + /// calls from contracts, which this setting allows. + /// By default, it is set to `false`. + #[cfg(feature = "optional_eip3607")] + pub disable_eip3607: bool, + /// Disables all gas refunds. This is useful when using chains that have gas refunds disabled e.g. Avalanche. + /// Reasoning behind removing gas refunds can be found in EIP-3298. + /// By default, it is set to `false`. + #[cfg(feature = "optional_gas_refund")] + pub disable_gas_refund: bool, +} + +#[derive(Clone, Default, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum AnalysisKind { + Raw, + Check, + #[default] + Analyse, +} + +impl Default for CfgEnv { + fn default() -> CfgEnv { + CfgEnv { + chain_id: U256::from(1), + spec_id: SpecId::LATEST, + perf_all_precompiles_have_balance: false, + perf_analyse_created_bytecodes: Default::default(), + limit_contract_code_size: None, + #[cfg(feature = "memory_limit")] + memory_limit: 2u64.pow(32) - 1, + #[cfg(feature = "optional_balance_check")] + disable_balance_check: false, + #[cfg(feature = "optional_block_gas_limit")] + disable_block_gas_limit: false, + #[cfg(feature = "optional_eip3607")] + disable_eip3607: false, + #[cfg(feature = "optional_gas_refund")] + disable_gas_refund: false, + } + } +} + +impl Default for BlockEnv { + fn default() -> BlockEnv { + BlockEnv { + gas_limit: U256::MAX, + number: U256::ZERO, + coinbase: B160::zero(), + timestamp: U256::from(1), + difficulty: U256::ZERO, + prevrandao: Some(B256::zero()), + basefee: U256::ZERO, + } + } +} + +impl Default for TxEnv { + fn default() -> TxEnv { + TxEnv { + caller: B160::zero(), + gas_limit: u64::MAX, + gas_price: U256::ZERO, + gas_priority_fee: None, + transact_to: TransactTo::Call(B160::zero()), //will do nothing + value: U256::ZERO, + data: Bytes::new(), + chain_id: None, + nonce: None, + access_list: Vec::new(), + } + } +} + +impl Env { + pub fn effective_gas_price(&self) -> U256 { + if self.tx.gas_priority_fee.is_none() { + self.tx.gas_price + } else { + min( + self.tx.gas_price, + self.block.basefee + self.tx.gas_priority_fee.unwrap(), + ) + } + } +} diff --git a/crates/primitives/src/gas.rs b/crates/primitives/src/gas.rs new file mode 100644 index 0000000000..83c9b04019 --- /dev/null +++ b/crates/primitives/src/gas.rs @@ -0,0 +1,85 @@ +#[derive(Clone, Copy, Debug)] +pub struct Gas { + /// Gas Limit + limit: u64, + /// used+memory gas. + all_used_gas: u64, + /// Used gas without memory + used: u64, + /// Used gas for memory expansion + memory: u64, + /// Refunded gas. This gas is used only at the end of execution. + refunded: i64, +} +impl Gas { + pub fn new(limit: u64) -> Self { + Self { + limit, + used: 0, + memory: 0, + refunded: 0, + all_used_gas: 0, + } + } + + pub fn limit(&self) -> u64 { + self.limit + } + + pub fn memory(&self) -> u64 { + self.memory + } + + pub fn refunded(&self) -> i64 { + self.refunded + } + + pub fn spend(&self) -> u64 { + self.all_used_gas + } + + pub fn remaining(&self) -> u64 { + self.limit - self.all_used_gas + } + + pub fn erase_cost(&mut self, returned: u64) { + self.used -= returned; + self.all_used_gas -= returned; + } + + pub fn record_refund(&mut self, refund: i64) { + self.refunded += refund; + } + + /// Record an explicit cost. + #[inline(always)] + pub fn record_cost(&mut self, cost: u64) -> bool { + let (all_used_gas, overflow) = self.all_used_gas.overflowing_add(cost); + if overflow || self.limit < all_used_gas { + return false; + } + + self.used += cost; + self.all_used_gas = all_used_gas; + true + } + + /// used in memory_resize! macro to record gas used for memory expansion. + pub fn record_memory(&mut self, gas_memory: u64) -> bool { + if gas_memory > self.memory { + let (all_used_gas, overflow) = self.used.overflowing_add(gas_memory); + if overflow || self.limit < all_used_gas { + return false; + } + self.memory = gas_memory; + self.all_used_gas = all_used_gas; + } + true + } + + /// used in gas_refund! macro to record refund value. + /// Refund can be negative but self.refunded is always positive. + pub fn gas_refund(&mut self, refund: i64) { + self.refunded += refund; + } +} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs new file mode 100644 index 0000000000..6cf7e2fa78 --- /dev/null +++ b/crates/primitives/src/lib.rs @@ -0,0 +1,34 @@ +pub mod bits; +pub mod bytecode; +pub mod db; +pub mod env; +mod gas; +pub mod log; +pub mod models; +pub mod result; +pub mod specification; +pub mod state; +pub mod utilities; + +extern crate alloc; + +pub use bits::B160; +pub use bits::B256; +pub use bytes; +pub use bytes::Bytes; +/// Address type is first 20 bytes of hash of ethereum account +pub type Address = B160; +/// Hash, in Ethereum usually kecack256. +pub type Hash = B256; + +pub use bytecode::*; +pub use env::*; +pub use gas::Gas; +pub use log::Log; +pub use models::*; +pub use result::*; +pub use ruint; +pub use ruint::aliases::U256; +pub use specification::*; +pub use state::*; +pub use utilities::*; diff --git a/crates/primitives/src/log.rs b/crates/primitives/src/log.rs new file mode 100644 index 0000000000..1d672e544c --- /dev/null +++ b/crates/primitives/src/log.rs @@ -0,0 +1,10 @@ +use crate::{bytes::Bytes, B160, B256}; + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Log { + pub address: B160, + pub topics: Vec, + #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] + pub data: Bytes, +} diff --git a/crates/primitives/src/models.rs b/crates/primitives/src/models.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/primitives/src/models.rs @@ -0,0 +1 @@ + diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs new file mode 100644 index 0000000000..514214ed46 --- /dev/null +++ b/crates/primitives/src/result.rs @@ -0,0 +1,134 @@ +use crate::{Log, State, B160}; +use bytes::Bytes; + +pub type EVMResult = std::result::Result>; + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ResultAndState { + /// Status of execution + pub result: ExecutionResult, + /// State that got updated + pub state: State, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum ExecutionResult { + /// Returned successfully + Success { + reason: Eval, + gas_used: u64, + gas_refunded: u64, + logs: Vec, + output: Output, + }, + /// Reverted by `REVERT` opcode that doesn't spend all gas. + Revert { gas_used: u64 }, + /// Reverted for various reasons and spend all gas. + Halt { + reason: Halt, + /// Halting will spend all the gas, and will be equal to gas_limit. + gas_used: u64, + }, +} + +impl ExecutionResult { + /// Returns if transaction execution is successful. + /// 1 indicates success, 0 indicates revert. + /// https://eips.ethereum.org/EIPS/eip-658 + pub fn is_success(&self) -> bool { + matches!(self, Self::Success { .. }) + } + + /// Return logs, if execution is not successful, function will return empty vec. + pub fn logs(&self) -> Vec { + match self { + Self::Success { logs, .. } => logs.clone(), + _ => Vec::new(), + } + } + + pub fn gas_used(&self) -> u64 { + let (Self::Success { gas_used, .. } + | Self::Revert { gas_used } + | Self::Halt { gas_used, .. }) = self; + + *gas_used + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum Output { + #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] + Call(Bytes), + Create( + #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] Bytes, + Option, + ), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum EVMError { + Transaction(InvalidTransaction), + /// REVM specific and related to environment. + PrevrandaoNotSet, + Database(DB), +} + +impl From for EVMError { + fn from(invalid: InvalidTransaction) -> Self { + EVMError::Transaction(invalid) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum InvalidTransaction { + GasMaxFeeGreaterThanPriorityFee, + GasPriceLessThenBasefee, + CallerGasLimitMoreThenBlock, + CallGasCostMoreThenGasLimit, + /// EIP-3607 Reject transactions from senders with deployed code + RejectCallerWithCode, + /// Transaction account does not have enough amount of ether to cover transferred value and gas_limit*gas_price. + LackOfFundForGasLimit, + /// Overflow payment in transaction. + OverflowPaymentInTransaction, + /// Nonce overflows in transaction, + NonceOverflowInTransaction, +} + +/// When transaction return successfully without halts. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum Eval { + Stop, + Return, + SelfDestruct, +} + +/// Indicates that the EVM has experienced an exceptional halt. This causes execution to +/// immediately end with all gas being consumed. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum Halt { + OutOfGas, + OpcodeNotFound, + InvalidFEOpcode, + InvalidJump, + NotActivated, + StackUnderflow, + StackOverflow, + OutOfOffset, + CreateCollision, + OverflowPayment, + PrecompileError, + NonceOverflow, + /// Create init code size exceeds limit (runtime). + CreateContractSizeLimit, + /// Error on created contract that begins with EF + CreateContractStartingWithEF, +} diff --git a/crates/interpreter/src/specification.rs b/crates/primitives/src/specification.rs similarity index 100% rename from crates/interpreter/src/specification.rs rename to crates/primitives/src/specification.rs diff --git a/crates/primitives/src/state.rs b/crates/primitives/src/state.rs new file mode 100644 index 0000000000..21e6f66eb4 --- /dev/null +++ b/crates/primitives/src/state.rs @@ -0,0 +1,144 @@ +use crate::{Bytecode, B160, B256, KECCAK_EMPTY, U256}; +use hashbrown::HashMap; + +#[derive(Debug, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Account { + /// Balance of the account. + pub info: AccountInfo, + /// storage cache + pub storage: HashMap, + /// If account is newly created, we will not ask database for storage values + pub storage_cleared: bool, + /// if account is destroyed it will be scheduled for removal. + pub is_destroyed: bool, + /// if account is touched + pub is_touched: bool, + /// used only for pre spurious dragon hardforks where exisnting and empty was two saparate states. + /// it became same state after EIP-161: State trie clearing + pub is_not_existing: bool, +} + +pub type State = HashMap; +pub type Storage = HashMap; + +impl Account { + pub fn is_empty(&self) -> bool { + self.info.is_empty() + } + pub fn new_not_existing() -> Self { + Self { + info: AccountInfo::default(), + storage: HashMap::new(), + storage_cleared: false, + is_destroyed: false, + is_touched: false, + is_not_existing: true, + } + } +} + +impl From for Account { + fn from(info: AccountInfo) -> Self { + Self { + info, + storage: HashMap::new(), + storage_cleared: false, + is_destroyed: false, + is_touched: false, + is_not_existing: false, + } + } +} + +#[derive(Debug, Clone, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct StorageSlot { + pub original_value: U256, + /// When loaded with sload present value is set to original value + pub present_value: U256, +} + +impl StorageSlot { + pub fn new(original: U256) -> Self { + Self { + original_value: original, + present_value: original, + } + } + + /// Returns true if the present value differs from the original value + pub fn is_changed(&self) -> bool { + self.original_value != self.present_value + } + + pub fn original_value(&self) -> U256 { + self.original_value + } + + pub fn present_value(&self) -> U256 { + self.present_value + } +} + +/// AccountInfo account information. +#[derive(Clone, Debug, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct AccountInfo { + /// Account balance. + pub balance: U256, + /// Account nonce. + pub nonce: u64, + /// code hash, + pub code_hash: B256, + /// code: if None, `code_by_hash` will be used to fetch it if code needs to be loaded from + /// inside of revm. + pub code: Option, +} + +impl Default for AccountInfo { + fn default() -> Self { + Self { + balance: U256::ZERO, + code_hash: KECCAK_EMPTY, + code: Some(Bytecode::new()), + nonce: 0, + } + } +} + +impl PartialEq for AccountInfo { + fn eq(&self, other: &Self) -> bool { + self.balance == other.balance + && self.nonce == other.nonce + && self.code_hash == other.code_hash + } +} + +impl AccountInfo { + pub fn new(balance: U256, nonce: u64, code: Bytecode) -> Self { + let code_hash = code.hash(); + Self { + balance, + nonce, + code: Some(code), + code_hash, + } + } + + pub fn is_empty(&self) -> bool { + let code_empty = self.code_hash == KECCAK_EMPTY || self.code_hash == B256::zero(); + self.balance == U256::ZERO && self.nonce == 0 && code_empty + } + + pub fn exists(&self) -> bool { + !self.is_empty() + } + + pub fn from_balance(balance: U256) -> Self { + AccountInfo { + balance, + ..Default::default() + } + } +} diff --git a/crates/interpreter/src/common.rs b/crates/primitives/src/utilities.rs similarity index 91% rename from crates/interpreter/src/common.rs rename to crates/primitives/src/utilities.rs index b658f06967..09cb3d3dbd 100644 --- a/crates/interpreter/src/common.rs +++ b/crates/primitives/src/utilities.rs @@ -1,6 +1,11 @@ use crate::{B160, B256, U256}; +use hex_literal::hex; use sha3::{Digest, Keccak256}; +pub const KECCAK_EMPTY: B256 = B256(hex!( + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" +)); + #[inline(always)] pub fn keccak256(input: &[u8]) -> B256 { B256::from_slice(Keccak256::digest(input).as_slice()) @@ -28,7 +33,7 @@ pub fn create2_address(caller: B160, code_hash: B256, salt: U256) -> B160 { /// Serde functions to serde as [bytes::Bytes] hex string #[cfg(feature = "serde")] -pub(crate) mod serde_hex_bytes { +pub mod serde_hex_bytes { use serde::{Deserialize, Deserializer, Serializer}; pub fn serialize(x: T, s: S) -> Result diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 959d775b1d..a5109118f0 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -10,12 +10,13 @@ version = "2.3.1" readme = "../../README.md" [dependencies] +revm-precompiles = { path = "../precompiles", version = "1.1.2", default-features = false } +revm-interpreter = { path = "../interpreter", default-features = false } + auto_impl = { version = "1.0", default-features = false } bytes = { version = "1.1", default-features = false } hashbrown = { version = "0.13" } hex = { version = "0.4", default-features = false } -revm-precompiles = { path = "../precompiles", version = "1.1.2", default-features = false } -revm-interpreter = { path = "../interpreter", default-features = false } # Optional serde = { version = "1.0", features = ["derive", "rc"], optional = true } diff --git a/crates/revm/src/db.rs b/crates/revm/src/db.rs index efbd39e47e..d3ea17c6d1 100644 --- a/crates/revm/src/db.rs +++ b/crates/revm/src/db.rs @@ -1,4 +1,4 @@ -mod in_memory_db; +pub mod in_memory_db; #[cfg(feature = "ethersdb")] pub mod ethersdb; @@ -10,76 +10,5 @@ compile_error!( "`web3db` feature is deprecated, drop-in replacement can be found with feature `ethersdb`" ); -use crate::AccountInfo; -use crate::U256; -use crate::{interpreter::bytecode::Bytecode, Account}; -use crate::{B160, B256}; -use auto_impl::auto_impl; -use hashbrown::HashMap as Map; -pub use in_memory_db::{AccountState, BenchmarkDB, CacheDB, DbAccount, EmptyDB, InMemoryDB}; - -#[auto_impl(& mut, Box)] -pub trait Database { - type Error; - /// Get basic account information. - fn basic(&mut self, address: B160) -> Result, Self::Error>; - /// Get account code by its hash - fn code_by_hash(&mut self, code_hash: B256) -> Result; - /// Get storage value of address at index. - fn storage(&mut self, address: B160, index: U256) -> Result; - - // History related - fn block_hash(&mut self, number: U256) -> Result; -} - -#[auto_impl(& mut, Box)] -pub trait DatabaseCommit { - fn commit(&mut self, changes: Map); -} - -#[auto_impl(&, Box, Arc)] -pub trait DatabaseRef { - type Error; - /// Whether account at address exists. - //fn exists(&self, address: B160) -> Option; - /// Get basic account information. - fn basic(&self, address: B160) -> Result, Self::Error>; - /// Get account code by its hash - fn code_by_hash(&self, code_hash: B256) -> Result; - /// Get storage value of address at index. - fn storage(&self, address: B160, index: U256) -> Result; - - // History related - fn block_hash(&self, number: U256) -> Result; -} - -pub struct RefDBWrapper<'a, Error> { - pub db: &'a dyn DatabaseRef, -} - -impl<'a, Error> RefDBWrapper<'a, Error> { - pub fn new(db: &'a dyn DatabaseRef) -> Self { - Self { db } - } -} - -impl<'a, Error> Database for RefDBWrapper<'a, Error> { - type Error = Error; - /// Get basic account information. - fn basic(&mut self, address: B160) -> Result, Self::Error> { - self.db.basic(address) - } - /// Get account code by its hash - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.db.code_by_hash(code_hash) - } - /// Get storage value of address at index. - fn storage(&mut self, address: B160, index: U256) -> Result { - self.db.storage(address, index) - } - - // History related - fn block_hash(&mut self, number: U256) -> Result { - self.db.block_hash(number) - } -} +pub use crate::primitives::db::*; +pub use in_memory_db::*; diff --git a/crates/revm/src/db/ethersdb.rs b/crates/revm/src/db/ethersdb.rs index a17664ffce..123c187ad2 100644 --- a/crates/revm/src/db/ethersdb.rs +++ b/crates/revm/src/db/ethersdb.rs @@ -1,11 +1,8 @@ -use std::sync::Arc; - -use crate::{ - interpreter::bytecode::Bytecode, AccountInfo, Database, B160, B256, KECCAK_EMPTY, U256, -}; - +use crate::primitives::{AccountInfo, Bytecode, B160, B256, KECCAK_EMPTY, U256}; +use crate::Database; use ethers_core::types::{BlockId, H160 as eH160, H256, U64 as eU64}; use ethers_providers::Middleware; +use std::sync::Arc; use tokio::runtime::{Handle, Runtime}; pub struct EthersDB diff --git a/crates/revm/src/db/in_memory_db.rs b/crates/revm/src/db/in_memory_db.rs index 1b7d630e13..de999202b0 100644 --- a/crates/revm/src/db/in_memory_db.rs +++ b/crates/revm/src/db/in_memory_db.rs @@ -1,8 +1,8 @@ use super::{DatabaseCommit, DatabaseRef}; -use crate::common::keccak256; -use crate::{interpreter::bytecode::Bytecode, Database, KECCAK_EMPTY}; -use crate::{Account, AccountInfo, Log}; -use crate::{B160, B256, U256}; +use crate::primitives::{ + keccak256, Account, AccountInfo, Bytecode, Log, B160, B256, KECCAK_EMPTY, U256, +}; +use crate::Database; use alloc::vec::Vec; use core::convert::Infallible; use hashbrown::{hash_map::Entry, HashMap as Map}; @@ -393,7 +393,7 @@ impl Database for BenchmarkDB { #[cfg(test)] mod tests { use super::{CacheDB, EmptyDB}; - use crate::{AccountInfo, Database, U256}; + use crate::primitives::{db::Database, AccountInfo, U256}; #[test] pub fn test_insert_account_storage() { diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 48aa13a60d..940a342938 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,12 +1,12 @@ +use crate::primitives::{specification, EVMError, EVMResult, ExecutionResult, SpecId}; use crate::{ db::{Database, DatabaseCommit, DatabaseRef, RefDBWrapper}, evm_impl::{EVMImpl, Transact}, inspectors::NoOpInspector, - journaled_state::State, - Env, ExecutionResult, Inspector, + Env, Inspector, }; use alloc::boxed::Box; -use revm_interpreter::{specification, SpecId}; +use revm_interpreter::primitives::ResultAndState; use revm_precompiles::Precompiles; /// Struct that takes Database and enabled transact to update state directly to database. @@ -43,22 +43,25 @@ impl Default for EVM { impl EVM { /// Execute transaction and apply result to database - pub fn transact_commit(&mut self) -> ExecutionResult { - let (exec_result, state) = self.transact(); + pub fn transact_commit(&mut self) -> Result> { + let ResultAndState { result, state } = self.transact()?; self.db.as_mut().unwrap().commit(state); - exec_result + Ok(result) } /// Inspect transaction and commit changes to database. - pub fn inspect_commit>(&mut self, inspector: INSP) -> ExecutionResult { - let (exec_result, state) = self.inspect(inspector); + pub fn inspect_commit>( + &mut self, + inspector: INSP, + ) -> Result> { + let ResultAndState { result, state } = self.inspect(inspector)?; self.db.as_mut().unwrap().commit(state); - exec_result + Ok(result) } } impl EVM { /// Execute transaction without writing to DB, return change state. - pub fn transact(&mut self) -> (ExecutionResult, State) { + pub fn transact(&mut self) -> EVMResult { if let Some(db) = self.db.as_mut() { let mut noop = NoOpInspector {}; let out = evm_inner::(&mut self.env, db, &mut noop).transact(); @@ -69,10 +72,7 @@ impl EVM { } /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>( - &mut self, - mut inspector: INSP, - ) -> (ExecutionResult, State) { + pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { if let Some(db) = self.db.as_mut() { evm_inner::(&mut self.env, db, &mut inspector).transact() } else { @@ -83,7 +83,7 @@ impl EVM { impl<'a, DB: DatabaseRef> EVM { /// Execute transaction without writing to DB, return change state. - pub fn transact_ref(&self) -> (ExecutionResult, State) { + pub fn transact_ref(&self) -> EVMResult { if let Some(db) = self.db.as_ref() { let mut noop = NoOpInspector {}; let mut db = RefDBWrapper::new(db); @@ -101,7 +101,7 @@ impl<'a, DB: DatabaseRef> EVM { pub fn inspect_ref>>( &'a self, mut inspector: INSP, - ) -> (ExecutionResult, State) { + ) -> EVMResult { if let Some(db) = self.db.as_ref() { let mut db = RefDBWrapper::new(db); let db = &mut db; @@ -146,7 +146,7 @@ macro_rules! create_evm { $env, $inspector, Precompiles::new(to_precompile_id($spec::SPEC_ID)).clone(), - )) as Box + )) as Box + 'a> }; } @@ -176,7 +176,7 @@ pub fn evm_inner<'a, DB: Database, const INSPECT: bool>( env: &'a mut Env, db: &'a mut DB, insp: &'a mut dyn Inspector, -) -> Box { +) -> Box + 'a> { use specification::*; match env.cfg.spec_id { SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec, db, env, insp), diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index 8ca4ce1b65..0adaa3d92d 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -1,21 +1,28 @@ +use crate::interpreter::gas; +use crate::primitives::{ + create2_address, create_address, keccak256, Account, Bytecode, EVMError, EVMResult, + ExecutionResult, Gas, Output, + SpecId::{self, *}, + B160, B256, KECCAK_EMPTY, U256, +}; use crate::{ - common::keccak256, db::Database, - gas, - interpreter::{self, Bytecode, Host}, - interpreter::{Account, Contract, Interpreter}, - journaled_state::{JournaledState, State}, - models::SelfDestructResult, - precompiles, return_ok, return_revert, AnalysisKind, CallContext, CallInputs, CallScheme, - CreateInputs, CreateScheme, Env, ExecutionResult, Gas, Inspector, Log, Return, Spec, - SpecId::{self, *}, - TransactOut, TransactTo, Transfer, B160, B256, KECCAK_EMPTY, U256, + inner_models::SelfDestructResult, + interpreter::{self, Host}, + interpreter::{Contract, Interpreter}, + journaled_state::JournaledState, + precompiles, + primitives::Spec, + return_ok, return_revert, AnalysisKind, CallContext, CallInputs, CallScheme, CreateInputs, + CreateScheme, Env, Inspector, InstructionResult, Log, TransactTo, Transfer, }; use alloc::vec::Vec; use bytes::Bytes; use core::{cmp::min, marker::PhantomData}; use hashbrown::HashMap as Map; -use revm_interpreter::common::{create2_address, create_address}; +use interpreter::analysis::to_analysed; +use interpreter::instruction_result::SuccessOrHalt; +use interpreter::primitives::{InvalidTransaction, ResultAndState}; use revm_precompiles::{Precompile, Precompiles}; pub struct EVMData<'a, DB: Database> { @@ -32,41 +39,40 @@ pub struct EVMImpl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> { _phantomdata: PhantomData, } -pub trait Transact { +pub trait Transact { /// Do transaction. - /// Return Return, Output for call or Address if we are creating contract, gas spend, gas refunded, State that needs to be applied. - fn transact(&mut self) -> (ExecutionResult, State); + /// InstructionResult InstructionResult, Output for call or Address if we are creating contract, gas spend, gas refunded, State that needs to be applied. + fn transact(&mut self) -> EVMResult; } -impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact +impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact for EVMImpl<'a, GSPEC, DB, INSPECT> { - fn transact(&mut self) -> (ExecutionResult, State) { + fn transact(&mut self) -> EVMResult { let caller = self.data.env.tx.caller; let value = self.data.env.tx.value; let data = self.data.env.tx.data.clone(); let gas_limit = self.data.env.tx.gas_limit; - let exit = |reason: Return| (ExecutionResult::new_with_reason(reason), State::new()); + let effective_gas_price = self.data.env.effective_gas_price(); if GSPEC::enabled(MERGE) && self.data.env.block.prevrandao.is_none() { - return exit(Return::PrevrandaoNotSet); + return Err(EVMError::PrevrandaoNotSet); } if GSPEC::enabled(LONDON) { if let Some(priority_fee) = self.data.env.tx.gas_priority_fee { if priority_fee > self.data.env.tx.gas_price { // or gas_max_fee for eip1559 - return exit(Return::GasMaxFeeGreaterThanPriorityFee); + return Err(InvalidTransaction::GasMaxFeeGreaterThanPriorityFee.into()); } } - let effective_gas_price = self.data.env.effective_gas_price(); let basefee = self.data.env.block.basefee; // check minimal cost against basefee // TODO maybe do this checks when creating evm. We already have all data there // or should be move effective_gas_price inside transact fn if effective_gas_price < basefee { - return exit(Return::GasPriceLessThenBasefee); + return Err(InvalidTransaction::GasPriceLessThenBasefee.into()); } // check if priority fee is lower then max fee } @@ -78,24 +84,14 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact // unusual to be found here, but check if gas_limit is more then block_gas_limit if !disable_block_gas_limit && U256::from(gas_limit) > self.data.env.block.gas_limit { - return exit(Return::CallerGasLimitMoreThenBlock); - } - - let mut gas = Gas::new(gas_limit); - // record initial gas cost. if not using gas metering init will return 0 - if !gas.record_cost(self.initialization::()) { - return exit(Return::OutOfGas); + return Err(InvalidTransaction::CallerGasLimitMoreThenBlock.into()); } // load acc - if self - .data + self.data .journaled_state .load_account(caller, self.data.db) - .is_err() - { - return exit(Return::FatalExternalError); - } + .map_err(EVMError::Database)?; #[cfg(feature = "optional_eip3607")] let disable_eip3607 = self.env().cfg.disable_eip3607; @@ -108,7 +104,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact if !disable_eip3607 && self.data.journaled_state.account(caller).info.code_hash != KECCAK_EMPTY { - return exit(Return::RejectCallerWithCode); + return Err(InvalidTransaction::RejectCallerWithCode.into()); } #[cfg(feature = "optional_balance_check")] @@ -116,74 +112,82 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact #[cfg(not(feature = "optional_balance_check"))] let disable_balance_check = false; - // substract gas_limit*gas_price from current account. - if let Some(payment_value) = - U256::from(gas_limit).checked_mul(self.data.env.effective_gas_price()) - { - let balance = &mut self - .data - .journaled_state - .state - .get_mut(&caller) - .unwrap() - .info - .balance; - - if payment_value > *balance { - if disable_balance_check { - *balance = U256::ZERO; - } else { - return exit(Return::LackOfFundForGasLimit); - } - } else { - *balance -= payment_value; + let caller_balance = &mut self + .data + .journaled_state + .state + .get_mut(&caller) + .unwrap() + .info + .balance; + + // Check and reduce gas_limit*gas_price amount of caller account. + // And check if balance has enough of ether to cover value transfer. + if !disable_balance_check { + let payment_value = U256::from(gas_limit) + .checked_mul(effective_gas_price) + .ok_or(EVMError::Transaction( + InvalidTransaction::OverflowPaymentInTransaction, + ))?; + + if payment_value > *caller_balance { + return Err(InvalidTransaction::LackOfFundForGasLimit.into()); + } + *caller_balance -= payment_value; + // Check if account has enough balance for value transfer. + // Transfer will be done inside `*_inner` functions. + if value > *caller_balance { + // add back the payment value + *caller_balance += payment_value; + return Err(InvalidTransaction::LackOfFundForGasLimit.into()); } - } else { - return exit(Return::OverflowPayment); } - // check if we have enought balance for value transfer. - let difference = self.data.env.tx.gas_price - self.data.env.effective_gas_price(); - if !disable_balance_check - && difference + value > self.data.journaled_state.account(caller).info.balance - { - return exit(Return::OutOfFund); + let mut gas = Gas::new(gas_limit); + // record initial gas cost. if not using gas metering init will return. + if !gas.record_cost(self.initialization::()?) { + return Err(InvalidTransaction::CallGasCostMoreThenGasLimit.into()); } - // record all as cost; + // record all as cost. Gas limit here is reduced by init cost of bytes and access lists. let gas_limit = gas.remaining(); if crate::USE_GAS { gas.record_cost(gas_limit); } // call inner handling of call/create - let (exit_reason, ret_gas, out) = match self.data.env.tx.transact_to { + // TODO can probably be refactored to look nicer. + let (exit_reason, ret_gas, output) = match self.data.env.tx.transact_to { TransactTo::Call(address) => { - if self.data.journaled_state.inc_nonce(caller).is_none() { - // overflow - return exit(Return::NonceOverflow); + if self.data.journaled_state.inc_nonce(caller).is_some() { + let context = CallContext { + caller, + address, + code_address: address, + apparent_value: value, + scheme: CallScheme::Call, + }; + let mut call_input = CallInputs { + contract: address, + transfer: Transfer { + source: caller, + target: address, + value, + }, + input: data, + gas_limit, + context, + is_static: false, + }; + let (exit, gas, bytes) = self.call_inner(&mut call_input); + (exit, gas, Output::Call(bytes)) + } else { + ( + InstructionResult::NonceOverflow, + gas, + Output::Call(Bytes::new()), + ) } - let context = CallContext { - caller, - address, - code_address: address, - apparent_value: value, - scheme: CallScheme::Call, - }; - let mut call_input = CallInputs { - contract: address, - transfer: Transfer { - source: caller, - target: address, - value, - }, - input: data, - gas_limit, - context, - is_static: false, - }; - let (exit, gas, bytes) = self.call_inner(&mut call_input); - (exit, gas, TransactOut::Call(bytes)) } TransactTo::Create(scheme) => { let mut create_input = CreateInputs { @@ -194,7 +198,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact gas_limit, }; let (exit, address, ret_gas, bytes) = self.create_inner(&mut create_input); - (exit, ret_gas, TransactOut::Create(bytes, address)) + (exit, ret_gas, Output::Create(bytes, address)) } }; @@ -212,16 +216,26 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact } let (state, logs, gas_used, gas_refunded) = self.finalize::(caller, &gas); - ( - ExecutionResult { - exit_reason, - out, + + let result = match exit_reason.into() { + SuccessOrHalt::Success(reason) => ExecutionResult::Success { + reason, gas_used, gas_refunded, logs, + output, }, - state, - ) + SuccessOrHalt::Revert => ExecutionResult::Revert { gas_used }, + SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { reason, gas_used }, + SuccessOrHalt::FatalExternalError => { + return Err(EVMError::Database(self.data.error.take().unwrap())) + } + SuccessOrHalt::Internal => { + panic!("Internal return flags should remain internal {exit_reason:?}") + } + }; + + Ok(ResultAndState { result, state }) } } @@ -336,7 +350,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, (new_state, logs, gas_used, gas_refunded) } - fn initialization(&mut self) -> u64 { + fn initialization(&mut self) -> Result> { let is_create = matches!(self.data.env.tx.transact_to, TransactTo::Create(_)); let input = &self.data.env.tx.data; @@ -348,18 +362,17 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, let mut accessed_slots = 0_u64; for (address, slots) in self.data.env.tx.access_list.iter() { - // TODO return - let _ = self - .data + self.data .journaled_state - .load_account(*address, self.data.db); + .load_account(*address, self.data.db) + .map_err(EVMError::Database)?; accessed_slots += slots.len() as u64; - // TODO return + for slot in slots { - let _ = self - .data + self.data .journaled_state - .sload(*address, *slot, self.data.db); + .sload(*address, *slot, self.data.db) + .map_err(EVMError::Database)?; } } (self.data.env.tx.access_list.len() as u64, accessed_slots) @@ -382,21 +395,24 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, // EIP-2028: Transaction data gas cost reduction let gas_transaction_non_zero_data = if SPEC::enabled(ISTANBUL) { 16 } else { 68 }; - transact + Ok(transact + zero_data_len * gas::TRANSACTION_ZERO_DATA + non_zero_data_len * gas_transaction_non_zero_data + accessed_accounts * gas::ACCESS_LIST_ADDRESS - + accessed_slots * gas::ACCESS_LIST_STORAGE_KEY + + accessed_slots * gas::ACCESS_LIST_STORAGE_KEY) } else { - 0 + Ok(0) } } - fn create_inner(&mut self, inputs: &mut CreateInputs) -> (Return, Option, Gas, Bytes) { + fn create_inner( + &mut self, + inputs: &mut CreateInputs, + ) -> (InstructionResult, Option, Gas, Bytes) { // Call inspector if INSPECT { let (ret, address, gas, out) = self.inspector.create(&mut self.data, inputs); - if ret != Return::Continue { + if ret != InstructionResult::Continue { return self .inspector .create_end(&mut self.data, inputs, ret, address, gas, out); @@ -408,13 +424,22 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, // Check depth of calls if self.data.journaled_state.depth() > interpreter::CALL_STACK_LIMIT { - return (Return::CallTooDeep, None, gas, Bytes::new()); + return (InstructionResult::CallTooDeep, None, gas, Bytes::new()); } // Check balance of caller and value. Do this before increasing nonce match self.balance(inputs.caller) { - Some(i) if i.0 < inputs.value => return (Return::OutOfFund, None, gas, Bytes::new()), + Some(i) if i.0 < inputs.value => { + return (InstructionResult::OutOfFund, None, gas, Bytes::new()) + } Some(_) => (), - _ => return (Return::FatalExternalError, None, gas, Bytes::new()), + _ => { + return ( + InstructionResult::FatalExternalError, + None, + gas, + Bytes::new(), + ) + } } // Increase nonce of caller and check if it overflows @@ -422,7 +447,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, if let Some(nonce) = self.data.journaled_state.inc_nonce(inputs.caller) { old_nonce = nonce - 1; } else { - return (Return::Return, None, gas, Bytes::new()); + return (InstructionResult::Return, None, gas, Bytes::new()); } // Create address @@ -447,11 +472,16 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, ) { Ok(false) => { self.data.journaled_state.checkpoint_revert(checkpoint); - return (Return::CreateCollision, ret, gas, Bytes::new()); + return (InstructionResult::CreateCollision, ret, gas, Bytes::new()); } Err(err) => { self.data.error = Some(err); - return (Return::FatalExternalError, ret, gas, Bytes::new()); + return ( + InstructionResult::FatalExternalError, + ret, + gas, + Bytes::new(), + ); } Ok(true) => (), } @@ -477,7 +507,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, { // overflow self.data.journaled_state.checkpoint_revert(checkpoint); - return (Return::Return, None, gas, Bytes::new()); + return (InstructionResult::Return, None, gas, Bytes::new()); } // Create new interpreter and execute initcode @@ -518,7 +548,12 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, // EIP-3541: Reject new contract code starting with the 0xEF byte if GSPEC::enabled(LONDON) && !bytes.is_empty() && bytes.first() == Some(&0xEF) { self.data.journaled_state.checkpoint_revert(checkpoint); - return (Return::CreateContractWithEF, ret, interpreter.gas, bytes); + return ( + InstructionResult::CreateContractStartingWithEF, + ret, + interpreter.gas, + bytes, + ); } // EIP-170: Contract code size limit @@ -527,7 +562,12 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, && bytes.len() > self.data.env.cfg.limit_contract_code_size.unwrap_or(0x6000) { self.data.journaled_state.checkpoint_revert(checkpoint); - return (Return::CreateContractLimit, ret, interpreter.gas, bytes); + return ( + InstructionResult::CreateContractSizeLimit, + ret, + interpreter.gas, + bytes, + ); } if crate::USE_GAS { let gas_for_code = bytes.len() as u64 * crate::gas::CODEDEPOSIT; @@ -538,7 +578,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. if GSPEC::enabled(HOMESTEAD) { self.data.journaled_state.checkpoint_revert(checkpoint); - return (Return::OutOfGas, ret, interpreter.gas, bytes); + return (InstructionResult::OutOfGas, ret, interpreter.gas, bytes); } else { bytes = Bytes::new(); } @@ -550,15 +590,13 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, let bytecode = match self.data.env.cfg.perf_analyse_created_bytecodes { AnalysisKind::Raw => Bytecode::new_raw(bytes.clone()), AnalysisKind::Check => Bytecode::new_raw(bytes.clone()).to_checked(), - AnalysisKind::Analyse => { - Bytecode::new_raw(bytes.clone()).to_analysed::() - } + AnalysisKind::Analyse => to_analysed::(Bytecode::new_raw(bytes.clone())), }; self.data .journaled_state .set_code(created_address, bytecode); - (Return::Continue, ret, interpreter.gas, bytes) + (InstructionResult::Return, ret, interpreter.gas, bytes) } _ => { self.data.journaled_state.checkpoint_revert(checkpoint); @@ -579,13 +617,13 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, } } - fn call_inner(&mut self, inputs: &mut CallInputs) -> (Return, Gas, Bytes) { + fn call_inner(&mut self, inputs: &mut CallInputs) -> (InstructionResult, Gas, Bytes) { // Call the inspector if INSPECT { let (ret, gas, out) = self .inspector .call(&mut self.data, inputs, inputs.is_static); - if ret != Return::Continue { + if ret != InstructionResult::Continue { return self.inspector.call_end( &mut self.data, inputs, @@ -602,12 +640,12 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, let bytecode = if let Some((bytecode, _)) = self.code(inputs.contract) { bytecode } else { - return (Return::FatalExternalError, gas, Bytes::new()); + return (InstructionResult::FatalExternalError, gas, Bytes::new()); }; // Check depth if self.data.journaled_state.depth() > interpreter::CALL_STACK_LIMIT { - let (ret, gas, out) = (Return::CallTooDeep, gas, Bytes::new()); + let (ret, gas, out) = (InstructionResult::CallTooDeep, gas, Bytes::new()); if INSPECT { return self.inspector.call_end( &mut self.data, @@ -664,20 +702,17 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, Ok((gas_used, data)) => { if !crate::USE_GAS || gas.record_cost(gas_used) { self.data.journaled_state.checkpoint_commit(); - (Return::Continue, gas, Bytes::from(data)) + (InstructionResult::Return, gas, Bytes::from(data)) } else { self.data.journaled_state.checkpoint_revert(checkpoint); - (Return::OutOfGas, gas, Bytes::new()) + (InstructionResult::OutOfGas, gas, Bytes::new()) } } Err(e) => { let ret = if let precompiles::Error::OutOfGas = e { - Return::OutOfGas + InstructionResult::OutOfGas } else { - // TODO Consider using precompile errors. - // This would make Return be a litlle bit fatter, but with removal - // of return in instruction this shouldn't be a problem. - Return::PrecompileError + InstructionResult::PrecompileError }; self.data.journaled_state.checkpoint_revert(checkpoint); (ret, gas, Bytes::new()) @@ -734,11 +769,16 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host for EVMImpl<'a, GSPEC, DB, INSPECT> { - fn step(&mut self, interp: &mut Interpreter, is_static: bool) -> Return { + fn step(&mut self, interp: &mut Interpreter, is_static: bool) -> InstructionResult { self.inspector.step(interp, &mut self.data, is_static) } - fn step_end(&mut self, interp: &mut Interpreter, is_static: bool, ret: Return) -> Return { + fn step_end( + &mut self, + interp: &mut Interpreter, + is_static: bool, + ret: InstructionResult, + ) -> InstructionResult { self.inspector .step_end(interp, &mut self.data, is_static, ret) } @@ -854,11 +894,14 @@ impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host .ok() } - fn create(&mut self, inputs: &mut CreateInputs) -> (Return, Option, Gas, Bytes) { + fn create( + &mut self, + inputs: &mut CreateInputs, + ) -> (InstructionResult, Option, Gas, Bytes) { self.create_inner(inputs) } - fn call(&mut self, inputs: &mut CallInputs) -> (Return, Gas, Bytes) { + fn call(&mut self, inputs: &mut CallInputs) -> (InstructionResult, Gas, Bytes) { self.call_inner(inputs) } } diff --git a/crates/revm/src/inspector.rs b/crates/revm/src/inspector.rs index 6565bd971b..2b67aa4179 100644 --- a/crates/revm/src/inspector.rs +++ b/crates/revm/src/inspector.rs @@ -1,6 +1,5 @@ -use crate::{ - evm_impl::EVMData, CallInputs, CreateInputs, Database, Gas, Interpreter, Return, B160, B256, -}; +use crate::primitives::{db::Database, Gas, B160, B256}; +use crate::{evm_impl::EVMData, CallInputs, CreateInputs, InstructionResult, Interpreter}; use auto_impl::auto_impl; use bytes::Bytes; @@ -21,15 +20,15 @@ pub mod inspectors { pub trait Inspector { /// Called Before the interpreter is initialized. /// - /// If anything other than [Return::Continue] is returned then execution of the interpreter is + /// If anything other than [InstructionResult::Continue] is returned then execution of the interpreter is /// skipped. fn initialize_interp( &mut self, _interp: &mut Interpreter, _data: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { - Return::Continue + ) -> InstructionResult { + InstructionResult::Continue } /// Called on each step of the interpreter. @@ -45,8 +44,8 @@ pub trait Inspector { _interp: &mut Interpreter, _data: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { - Return::Continue + ) -> InstructionResult { + InstructionResult::Continue } /// Called when a log is emitted. @@ -61,69 +60,74 @@ pub trait Inspector { /// Called after `step` when the instruction has been executed. /// - /// Returning anything other than [Return::Continue] alters the execution of the interpreter. + /// InstructionResulting anything other than [InstructionResult::Continue] alters the execution of the interpreter. fn step_end( &mut self, _interp: &mut Interpreter, _data: &mut EVMData<'_, DB>, _is_static: bool, - _eval: Return, - ) -> Return { - Return::Continue + _eval: InstructionResult, + ) -> InstructionResult { + InstructionResult::Continue } /// Called whenever a call to a contract is about to start. /// - /// Returning anything other than [Return::Continue] overrides the result of the call. + /// InstructionResulting anything other than [InstructionResult::Continue] overrides the result of the call. fn call( &mut self, _data: &mut EVMData<'_, DB>, _inputs: &mut CallInputs, _is_static: bool, - ) -> (Return, Gas, Bytes) { - (Return::Continue, Gas::new(0), Bytes::new()) + ) -> (InstructionResult, Gas, Bytes) { + (InstructionResult::Continue, Gas::new(0), Bytes::new()) } /// Called when a call to a contract has concluded. /// - /// Returning anything other than the values passed to this function (`(ret, remaining_gas, + /// InstructionResulting anything other than the values passed to this function (`(ret, remaining_gas, /// out)`) will alter the result of the call. fn call_end( &mut self, _data: &mut EVMData<'_, DB>, _inputs: &CallInputs, remaining_gas: Gas, - ret: Return, + ret: InstructionResult, out: Bytes, _is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { (ret, remaining_gas, out) } /// Called when a contract is about to be created. /// - /// Returning anything other than [Return::Continue] overrides the result of the creation. + /// InstructionResulting anything other than [InstructionResult::Continue] overrides the result of the creation. fn create( &mut self, _data: &mut EVMData<'_, DB>, _inputs: &mut CreateInputs, - ) -> (Return, Option, Gas, Bytes) { - (Return::Continue, None, Gas::new(0), Bytes::default()) + ) -> (InstructionResult, Option, Gas, Bytes) { + ( + InstructionResult::Continue, + None, + Gas::new(0), + Bytes::default(), + ) } /// Called when a contract has been created. /// - /// Returning anything other than the values passed to this function (`(ret, remaining_gas, + /// InstructionResulting anything other than the values passed to this function (`(ret, remaining_gas, /// address, out)`) will alter the result of the create. fn create_end( &mut self, _data: &mut EVMData<'_, DB>, _inputs: &CreateInputs, - ret: Return, + ret: InstructionResult, address: Option, remaining_gas: Gas, out: Bytes, - ) -> (Return, Option, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { (ret, address, remaining_gas, out) } diff --git a/crates/revm/src/inspector/customprinter.rs b/crates/revm/src/inspector/customprinter.rs index 9a234b0189..2ef3b8f61a 100644 --- a/crates/revm/src/inspector/customprinter.rs +++ b/crates/revm/src/inspector/customprinter.rs @@ -1,9 +1,10 @@ //! Custom print inspector, it has step level information of execution. //! It is great tool if some debugging is needed. //! +use crate::primitives::{Bytes, Gas, B160}; use crate::{ - inspectors::GasInspector, opcode, Bytes, CallInputs, CreateInputs, Database, EVMData, Gas, - Inspector, Interpreter, Return, B160, + inspectors::GasInspector, opcode, CallInputs, CreateInputs, Database, EVMData, Inspector, + InstructionResult, Interpreter, }; use hex; #[derive(Clone, Default)] @@ -17,10 +18,10 @@ impl Inspector for CustomPrintTracer { 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())`. @@ -30,7 +31,7 @@ impl Inspector for CustomPrintTracer { 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]; @@ -52,7 +53,7 @@ impl Inspector for CustomPrintTracer { self.gas_inspector.step(interp, data, is_static); - Return::Continue + InstructionResult::Continue } fn step_end( @@ -60,10 +61,10 @@ impl Inspector for CustomPrintTracer { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - eval: Return, - ) -> Return { + eval: InstructionResult, + ) -> InstructionResult { self.gas_inspector.step_end(interp, data, is_static, eval); - Return::Continue + InstructionResult::Continue } fn call_end( @@ -71,10 +72,10 @@ impl Inspector for CustomPrintTracer { 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) @@ -84,11 +85,11 @@ impl Inspector for CustomPrintTracer { &mut self, data: &mut EVMData<'_, DB>, inputs: &CreateInputs, - ret: Return, + 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) @@ -99,7 +100,7 @@ impl Inspector for CustomPrintTracer { _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, @@ -108,14 +109,14 @@ impl Inspector for CustomPrintTracer { inputs.transfer, inputs.input.len(), ); - (Return::Continue, Gas::new(0), Bytes::new()) + (InstructionResult::Continue, Gas::new(0), Bytes::new()) } fn create( &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, @@ -124,7 +125,7 @@ impl Inspector for CustomPrintTracer { 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 selfdestruct(&mut self) { @@ -143,23 +144,23 @@ mod test { // checks this usecase let mut evm = crate::new(); let mut database = crate::InMemoryDB::default(); - let code: crate::Bytes = hex_literal::hex!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561").to_vec().into(); + let code: crate::primitives::Bytes = hex_literal::hex!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561").to_vec().into(); - let acc_info = crate::AccountInfo { + let acc_info = crate::primitives::AccountInfo { balance: "0x100c5d668240db8e00".parse().unwrap(), - code_hash: crate::common::keccak256(&code), - code: Some(crate::Bytecode::new_raw(code.clone())), + code_hash: crate::primitives::keccak256(&code), + code: Some(crate::primitives::Bytecode::new_raw(code.clone())), nonce: "1".parse().unwrap(), }; let callee = hex_literal::hex!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); - database.insert_account_info(crate::B160(callee), acc_info); + database.insert_account_info(crate::primitives::B160(callee), acc_info); evm.database(database); - evm.env.tx.caller = crate::B160(hex_literal::hex!( + evm.env.tx.caller = crate::primitives::B160(hex_literal::hex!( "5fdcca53617f4d2b9134b29090c87d01058e27e0" )); - evm.env.tx.transact_to = crate::TransactTo::Call(crate::B160(callee)); - evm.env.tx.data = crate::Bytes::from(hex::decode("").unwrap()); - evm.env.tx.value = crate::U256::ZERO; - evm.inspect_commit(super::CustomPrintTracer::default()); + evm.env.tx.transact_to = crate::TransactTo::Call(crate::primitives::B160(callee)); + evm.env.tx.data = crate::primitives::Bytes::from(hex::decode("").unwrap()); + evm.env.tx.value = crate::primitives::U256::ZERO; + let _ = evm.inspect_commit(super::CustomPrintTracer::default()); } } diff --git a/crates/revm/src/inspector/gas.rs b/crates/revm/src/inspector/gas.rs index 5da2719627..9654a752cf 100644 --- a/crates/revm/src/inspector/gas.rs +++ b/crates/revm/src/inspector/gas.rs @@ -1,6 +1,7 @@ //! GasIspector. Helper Inspector to calculte gas for others. //! -use crate::{evm_impl::EVMData, CallInputs, CreateInputs, Database, Gas, Inspector, Return, B160}; +use crate::primitives::{Gas, B160}; +use crate::{evm_impl::EVMData, CallInputs, CreateInputs, Database, Inspector, InstructionResult}; use bytes::Bytes; #[derive(Clone, Copy, Debug, Default)] @@ -28,10 +29,10 @@ impl Inspector for GasInspector { interp: &mut crate::Interpreter, _data: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { + ) -> InstructionResult { self.full_gas_block = interp.contract.first_gas_block(); self.gas_remaining = interp.gas.limit(); - Return::Continue + InstructionResult::Continue } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. @@ -43,7 +44,7 @@ impl Inspector for GasInspector { interp: &mut crate::Interpreter, data: &mut EVMData<'_, DB>, _is_static: bool, - ) -> Return { + ) -> InstructionResult { let op = interp.current_opcode(); // calculate gas_block @@ -61,7 +62,7 @@ impl Inspector for GasInspector { self.reduced_gas_block += info.get_gas() as u64; } - Return::Continue + InstructionResult::Continue } #[cfg(not(feature = "no_gas_measuring"))] @@ -70,8 +71,8 @@ impl Inspector for GasInspector { interp: &mut crate::Interpreter, _data: &mut EVMData<'_, DB>, _is_static: bool, - _eval: Return, - ) -> Return { + _eval: InstructionResult, + ) -> InstructionResult { let pc = interp.program_counter(); if let Some(was_pc) = self.was_jumpi { if let Some(new_pc) = pc.checked_sub(1) { @@ -90,7 +91,7 @@ impl Inspector for GasInspector { } self.gas_remaining = interp.gas.remaining() + (self.full_gas_block - self.reduced_gas_block); - Return::Continue + InstructionResult::Continue } fn call_end( @@ -98,10 +99,10 @@ impl Inspector for GasInspector { _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.was_return = true; (ret, remaining_gas, out) } @@ -110,11 +111,11 @@ impl Inspector for GasInspector { &mut self, _data: &mut EVMData<'_, DB>, _inputs: &CreateInputs, - ret: Return, + ret: InstructionResult, address: Option, remaining_gas: Gas, out: Bytes, - ) -> (Return, Option, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { self.was_return = true; (ret, address, remaining_gas, out) } @@ -123,12 +124,14 @@ impl Inspector for GasInspector { #[cfg(test)] mod tests { use crate::db::BenchmarkDB; + use crate::primitives::{Bytecode, Gas, B160, B256}; use crate::{ - inspectors::GasInspector, opcode, Bytecode, CallInputs, CreateInputs, Database, EVMData, - Gas, Inspector, Interpreter, OpCode, Return, TransactTo, B160, B256, + inspectors::GasInspector, opcode, CallInputs, CreateInputs, Database, EVMData, Inspector, + InstructionResult, Interpreter, OpCode, TransactTo, }; use bytes::Bytes; use hex_literal::hex; + use revm_interpreter::primitives::ResultAndState; #[derive(Default, Debug)] struct StackInspector { @@ -143,10 +146,10 @@ mod tests { 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 } fn step( @@ -154,10 +157,10 @@ mod tests { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - ) -> Return { + ) -> InstructionResult { self.pc = interp.program_counter(); self.gas_inspector.step(interp, data, is_static); - Return::Continue + InstructionResult::Continue } fn log( @@ -175,8 +178,8 @@ mod tests { interp: &mut Interpreter, data: &mut EVMData<'_, DB>, is_static: bool, - eval: Return, - ) -> Return { + eval: InstructionResult, + ) -> InstructionResult { self.gas_inspector.step_end(interp, data, is_static, eval); self.gas_remaining_steps .push((self.pc, self.gas_inspector.gas_remaining())); @@ -188,10 +191,14 @@ mod tests { data: &mut EVMData<'_, DB>, call: &mut CallInputs, is_static: bool, - ) -> (Return, Gas, Bytes) { + ) -> (InstructionResult, Gas, Bytes) { self.gas_inspector.call(data, call, is_static); - (Return::Continue, Gas::new(call.gas_limit), Bytes::new()) + ( + InstructionResult::Continue, + Gas::new(call.gas_limit), + Bytes::new(), + ) } fn call_end( @@ -199,10 +206,10 @@ mod tests { 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) @@ -212,11 +219,11 @@ mod tests { &mut self, data: &mut EVMData<'_, DB>, call: &mut CreateInputs, - ) -> (Return, Option, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { self.gas_inspector.create(data, call); ( - Return::Continue, + InstructionResult::Continue, None, Gas::new(call.gas_limit), Bytes::new(), @@ -227,11 +234,11 @@ mod tests { &mut self, data: &mut EVMData<'_, DB>, inputs: &CreateInputs, - status: Return, + status: InstructionResult, address: Option, gas: Gas, retdata: Bytes, - ) -> (Return, Option, Gas, Bytes) { + ) -> (InstructionResult, Option, Gas, Bytes) { self.gas_inspector .create_end(data, inputs, status, address, gas, retdata.clone()); (status, address, gas, retdata) @@ -265,7 +272,7 @@ mod tests { evm.env.tx.gas_limit = 21100; let mut inspector = StackInspector::default(); - let (result, state) = evm.inspect(&mut inspector); + let ResultAndState { result, state } = evm.inspect(&mut inspector).unwrap(); println!("{result:?} {state:?} {inspector:?}"); for (pc, gas) in inspector.gas_remaining_steps { diff --git a/crates/revm/src/journaled_state.rs b/crates/revm/src/journaled_state.rs index c33fe900ea..3a2746c527 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/revm/src/journaled_state.rs @@ -1,14 +1,11 @@ -use crate::{db::Database, Log, B160}; -use crate::{ - interpreter::{models::SelfDestructResult, Account, Bytecode, Return, StorageSlot, U256}, - KECCAK_EMPTY, -}; +use crate::interpreter::{inner_models::SelfDestructResult, InstructionResult, U256}; +use crate::primitives::{db::Database, Account, Bytecode, Log, StorageSlot, B160, KECCAK_EMPTY}; use alloc::{vec, vec::Vec}; use core::mem::{self}; use hashbrown::{hash_map::Entry, HashMap as Map}; #[derive(Debug, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct JournaledState { /// Current state. pub state: State, @@ -31,7 +28,7 @@ pub type State = Map; pub type Storage = Map; #[derive(Debug, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum JournalEntry { /// Used to mark account that is hot inside EVM in regards to EIP-2929 AccessList. /// Action: We will add Account to state. @@ -182,21 +179,23 @@ impl JournaledState { to: &B160, balance: U256, db: &mut DB, - ) -> Result<(bool, bool), Return> { + ) -> Result<(bool, bool), InstructionResult> { // load accounts let (_, from_is_cold) = self .load_account(*from, db) - .map_err(|_| Return::FatalExternalError)?; + .map_err(|_| InstructionResult::FatalExternalError)?; let (_, to_is_cold) = self .load_account(*to, db) - .map_err(|_| Return::FatalExternalError)?; + .map_err(|_| InstructionResult::FatalExternalError)?; // sub balance from let from_account = &mut self.state.get_mut(from).unwrap(); Self::touch_account(self.journal.last_mut().unwrap(), from, from_account); let from_balance = &mut from_account.info.balance; - *from_balance = from_balance.checked_sub(balance).ok_or(Return::OutOfFund)?; + *from_balance = from_balance + .checked_sub(balance) + .ok_or(InstructionResult::OutOfFund)?; // add balance to let to_account = &mut self.state.get_mut(to).unwrap(); @@ -204,7 +203,7 @@ impl JournaledState { let to_balance = &mut to_account.info.balance; *to_balance = to_balance .checked_add(balance) - .ok_or(Return::OverflowPayment)?; + .ok_or(InstructionResult::OverflowPayment)?; // Overflow of U256 balance is not possible to happen on mainnet. We dont bother to return funds from from_acc. self.journal diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 2c85445fd6..27fc0803ba 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -16,18 +16,19 @@ pub use db::{Database, DatabaseCommit, InMemoryDB}; pub use evm::{evm_inner, new, EVM}; pub use evm_impl::EVMData; pub use journaled_state::{JournalEntry, JournaledState}; +pub use revm_interpreter::primitives::*; pub use revm_interpreter::*; extern crate alloc; /// reexport `revm_precompiles` -pub mod precompiles { - pub use revm_precompiles::*; -} +pub use revm_precompiles as precompiles; + // reexport `revm_interpreter` -pub mod interpreter { - pub use revm_interpreter::*; -} +pub use revm_interpreter as interpreter; + +// reexport `revm_primitives` +pub use revm_interpreter::primitives; /// Reexport Inspector implementations pub use inspector::inspectors; diff --git a/crates/revmjs/CHANGELOG.md b/crates/revmjs/CHANGELOG.md deleted file mode 100644 index a40247c381..0000000000 --- a/crates/revmjs/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ - -# v0.2.0 - -* Added bn-rs lib that converts BN.js to primitive-types. -# v0.1.0 - -Initial release with usable example of usage. \ No newline at end of file diff --git a/crates/revmjs/Cargo.toml b/crates/revmjs/Cargo.toml deleted file mode 100644 index 6caf27c561..0000000000 --- a/crates/revmjs/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["Dragan Rakita "] -description = "REVM WASM - Rust Ethereum Virtual Machine Web Assembly lib" -edition = "2021" -keywords = ["ethereum", "evm", "rust"] -license = "MIT" -name = "revmjs" -repository = "https://github.com/bluealloy/revm" -version = "0.2.0" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -bn-rs = "0.2.3" -bytes = "1.2" -getrandom = { version = "0.2", features = ["js"] } -hex = "0.4" -js-sys = "0.3" -revm = { path = "../revm", version = "2.0", default-features = false } -ruint = { version = "1.7.0", features = ["bn-rs"] } -wasm-bindgen = "0.2" -primitive-types = "0.12" \ No newline at end of file diff --git a/crates/revmjs/README.md b/crates/revmjs/README.md deleted file mode 100644 index 6cc3475b05..0000000000 --- a/crates/revmjs/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# REVM library for javascript - -It is currently very restricted but it is working and it is good example of usage of revm as js lib. - -Dev problems: -* windows build with `cc` does not pass and it will not be builded: https://github.com/bluealloy/revm/issues/3 \ No newline at end of file diff --git a/crates/revmjs/index.html b/crates/revmjs/index.html deleted file mode 100644 index e27d558e97..0000000000 --- a/crates/revmjs/index.html +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/crates/revmjs/src/lib.rs b/crates/revmjs/src/lib.rs deleted file mode 100644 index 74eec5745d..0000000000 --- a/crates/revmjs/src/lib.rs +++ /dev/null @@ -1,183 +0,0 @@ -use core::convert::TryInto; - -use bn_rs::BN; -use bytes::Bytes; -use primitive_types::H160; -use revm::{ - AccountInfo, Bytecode, DatabaseCommit, ExecutionResult, InMemoryDB, SpecId, TransactTo, B160, - EVM as rEVM, U256, -}; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -extern "C" { - // Use `js_namespace` here to bind `console.log(..)` instead of just - // `log(..)` - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); -} - -// Next let's define a macro that's like `println!`, only it works for -// `console.log`. Note that `println!` doesn't actually work on the wasm target -// because the standard library currently just eats all output. To get -// `println!`-like behavior in your app you'll likely want a macro like this. -macro_rules! console_log { - // Note that this is using the `log` function imported above during - // `bare_bones` - ($($t:tt)*) => (log(&format_args!($($t)*).to_string())) -} - -/// Wrapper around revm with InMemoryDB -#[wasm_bindgen] -pub struct EVM { - revm: rEVM, -} - -impl Default for EVM { - fn default() -> Self { - EVM::new() - } -} - -#[wasm_bindgen] -impl EVM { - pub fn new() -> EVM { - console_log!("EVM created"); - let mut evm = EVM { revm: rEVM::new() }; - evm.revm.database(InMemoryDB::default()); - evm - } - - pub fn transact(&mut self) -> u64 { - let ( - ExecutionResult { - exit_reason, - out, - gas_used, - gas_refunded, - logs, - .. - }, - state, - ) = self.revm.transact(); - console_log!( - "Transact done, exit:{:?}, gas:{:?} ({:?} refunded), data:{:?}\nstate_chage:{:?}\nlogs:{:?}", - exit_reason, - gas_used, - gas_refunded, - out, - state, - logs, - ); - self.revm.db().unwrap().commit(state); - gas_used - } - - /****** DATABASE RELATED ********/ - pub fn insert_account(&mut self, address: BN, nonce: u64, balance: BN, code: &[u8]) { - let address = B160(>::try_into(address).unwrap().0); - let acc_info = AccountInfo::new( - balance.try_into().unwrap(), - nonce, - Bytecode::new_raw(Bytes::copy_from_slice(code)), - ); - console_log!("Added account:{:?} info:{:?}", address, acc_info); - self.revm - .db() - .unwrap() - .insert_account_info(address, acc_info); - } - - /****** ALL CFG ENV SETTERS ********/ - - pub fn cfg_chain_id(&mut self, gas_limit: BN) { - self.revm.env.cfg.chain_id = gas_limit.try_into().unwrap(); - } - pub fn cfg_spec_id(&mut self, spec_id: u8) { - self.revm.env.cfg.spec_id = SpecId::try_from_u8(spec_id).unwrap_or(SpecId::LATEST); - } - - /****** ALL BLOCK ENV SETTERS ********/ - - pub fn block_gas_limit(&mut self, gas_limit: BN) { - self.revm.env.block.gas_limit = gas_limit.try_into().unwrap(); - } - pub fn block_number(&mut self, number: BN) { - self.revm.env.block.number = number.try_into().unwrap(); - } - pub fn block_coinbase(&mut self, coinbase: BN) { - self.revm.env.block.coinbase = B160(>::try_into(coinbase).unwrap().0); - } - pub fn block_timestamp(&mut self, timestamp: BN) { - self.revm.env.block.timestamp = timestamp.try_into().unwrap(); - } - pub fn block_difficulty(&mut self, difficulty: BN) { - self.revm.env.block.difficulty = difficulty.try_into().unwrap(); - } - pub fn block_basefee(&mut self, basefee: BN) { - self.revm.env.block.basefee = basefee.try_into().unwrap(); - } - - /****** ALL TX ENV SETTERS ********/ - - pub fn tx_caller(&mut self, tx_caller: BN) { - self.revm.env.tx.caller = B160(>::try_into(tx_caller).unwrap().0); - } - pub fn tx_gas_limit(&mut self, gas_limit: u64) { - self.revm.env.tx.gas_limit = gas_limit; - } - pub fn tx_gas_price(&mut self, gas_price: BN) { - self.revm.env.tx.gas_price = gas_price.try_into().unwrap(); - } - pub fn tx_gas_priority_fee(&mut self, gas_priority_fee: Option) { - self.revm.env.tx.gas_priority_fee = gas_priority_fee.map(|v| v.try_into().unwrap()); - } - pub fn tx_value(&mut self, value: BN) { - self.revm.env.tx.value = value.try_into().unwrap(); - } - pub fn tx_chain_id(&mut self, chain_id: Option) { - self.revm.env.tx.chain_id = chain_id; - } - pub fn tx_nonce(&mut self, nonce: Option) { - self.revm.env.tx.nonce = nonce; - } - pub fn tx_data(&mut self, data: &[u8]) { - self.revm.env.tx.data = data.to_vec().into(); - } - pub fn tx_transact_to_create(&mut self) { - self.revm.env.tx.transact_to = TransactTo::create(); - } - pub fn tx_transact_to_call(&mut self, to: BN) { - self.revm.env.tx.transact_to = - TransactTo::Call(B160(>::try_into(to).unwrap().0)); - } - pub fn tx_accessed_account(&mut self, account: AccessedAccount) { - self.revm.env.tx.access_list.push(account.into()) - } -} - -/// Struct that allows setting AccessList for transaction. -#[wasm_bindgen] -pub struct AccessedAccount { - account: B160, - slots: Vec, -} - -impl From for (B160, Vec) { - fn from(from: AccessedAccount) -> Self { - (from.account, from.slots) - } -} - -#[wasm_bindgen] -impl AccessedAccount { - pub fn new(account: BN) -> Self { - Self { - account: B160(>::try_into(account).unwrap().0), - slots: Vec::new(), - } - } - pub fn slot(&mut self, slot: BN) { - self.slots.push(slot.try_into().unwrap()) - } -}