diff --git a/Cargo.lock b/Cargo.lock index 2bc61cebf4..5a98d1e54f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,18 +15,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] [[package]] -name = "adler2" -version = "2.0.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" @@ -57,9 +57,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.36" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94c225801d42099570d0674701dddd4142f0ef715282aeb5985042e2ec962df7" +checksum = "1752d7d62e2665da650a36d84abbf239f812534475d51f072a49a533513b7cdd" dependencies = [ "num_enum", "strum", @@ -266,7 +266,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -286,7 +286,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.1", "tracing", "url", ] @@ -346,7 +346,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -362,7 +362,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", "syn-solidity", "tiny-keccak", ] @@ -378,7 +378,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", "syn-solidity", ] @@ -389,7 +389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc85178909a49c8827ffccfc9103a7ce1767ae66a801b69bdc326913870bf8e6" dependencies = [ "serde", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -419,7 +419,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower", + "tower 0.5.1", "tracing", "url", ] @@ -434,7 +434,7 @@ dependencies = [ "alloy-transport", "reqwest", "serde_json", - "tower", + "tower 0.5.1", "tracing", "url", ] @@ -462,9 +462,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" @@ -481,7 +481,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -491,7 +491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -540,7 +540,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "paste", - "rustc_version 0.4.1", + "rustc_version 0.4.0", "zeroize", ] @@ -632,15 +632,15 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-stream" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", @@ -649,24 +649,24 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.6" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] name = "async-trait" -version = "0.1.83" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -693,28 +693,28 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] name = "autocfg" -version = "1.4.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", + "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", - "windows-targets", ] [[package]] @@ -822,9 +822,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ "serde", ] @@ -852,12 +852,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.24" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" -dependencies = [ - "shlex", -] +checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" [[package]] name = "cfg-if" @@ -903,9 +900,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.19" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -913,9 +910,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.19" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -925,21 +922,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" @@ -957,14 +954,14 @@ dependencies = [ "lazy_static", "libc", "unicode-width", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "const-hex" -version = "1.13.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" dependencies = [ "cfg-if", "cpufeatures", @@ -991,15 +988,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -1095,9 +1092,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "6.1.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1136,7 +1133,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -1147,7 +1144,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -1158,7 +1155,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -1178,7 +1175,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", "unicode-xid", ] @@ -1205,9 +1202,9 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.5" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" @@ -1277,7 +1274,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -1293,7 +1290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1468,9 +1465,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fastrlp" @@ -1615,7 +1612,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -1684,9 +1681,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -1707,9 +1704,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" dependencies = [ "atomic-waker", "bytes", @@ -1769,12 +1766,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hermit-abi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" - [[package]] name = "hex" version = "0.4.3" @@ -1812,9 +1803,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", "http", @@ -1835,9 +1826,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" @@ -1861,9 +1852,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", "http", @@ -1894,9 +1885,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.9" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" dependencies = [ "bytes", "futures-channel", @@ -1907,6 +1898,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", + "tower 0.4.13", "tower-service", "tracing", ] @@ -2001,19 +1993,19 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2048,18 +2040,18 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "k256" -version = "0.13.4" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", @@ -2080,9 +2072,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.4" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +checksum = "47a3633291834c4fbebf8673acbc1b04ec9d151418ff9b8e26dcd79129928758" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -2112,9 +2104,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -2146,9 +2138,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ "hashbrown 0.14.5", ] @@ -2173,23 +2165,23 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "adler2", + "adler", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", "wasi", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2300,29 +2292,29 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -2333,21 +2325,18 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.36.5" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -2382,9 +2371,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ "bitflags", "cfg-if", @@ -2403,7 +2392,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -2414,9 +2403,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -2498,9 +2487,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -2537,7 +2526,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -2566,7 +2555,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -2593,9 +2582,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plain_hasher" @@ -2608,9 +2597,9 @@ dependencies = [ [[package]] name = "plotters" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", @@ -2621,33 +2610,30 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" -version = "0.3.7" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] [[package]] name = "portable-atomic" -version = "1.9.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primeorder" @@ -2674,9 +2660,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ "toml_edit", ] @@ -2700,7 +2686,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -2740,7 +2726,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -2751,9 +2737,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.37" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -2826,18 +2812,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.11.0" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -2847,9 +2833,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -2858,9 +2844,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "relative-path" @@ -2870,9 +2856,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.8" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64", "bytes", @@ -2932,6 +2918,7 @@ dependencies = [ "revm-primitives", "revm-specification", "revm-state", + "revm-transaction", "revm-wiring", "rstest", "serde", @@ -3010,6 +2997,7 @@ dependencies = [ "revm-database-interface", "revm-primitives", "revm-specification", + "revm-transaction", "revm-wiring", "serde", "serde_json", @@ -3100,6 +3088,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "revm-transaction" +version = "1.0.0" +dependencies = [ + "auto_impl", + "revm-primitives", + "revm-specification", + "serde", +] + [[package]] name = "revm-wiring" version = "1.0.0" @@ -3113,6 +3111,7 @@ dependencies = [ "revm-primitives", "revm-specification", "revm-state", + "revm-transaction", "serde", ] @@ -3164,7 +3163,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -3207,7 +3206,7 @@ dependencies = [ "futures", "futures-timer", "rstest_macros", - "rustc_version 0.4.1", + "rustc_version 0.4.0", ] [[package]] @@ -3223,8 +3222,8 @@ dependencies = [ "quote", "regex", "relative-path", - "rustc_version 0.4.1", - "syn 2.0.79", + "rustc_version 0.4.0", + "syn 2.0.70", "unicode-ident", ] @@ -3291,31 +3290,31 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver 1.0.23", ] [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" dependencies = [ "once_cell", "rustls-pki-types", @@ -3326,24 +3325,25 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.2.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ + "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.102.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" dependencies = [ "ring", "rustls-pki-types", @@ -3409,11 +3409,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -3448,18 +3448,18 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" dependencies = [ "cc", ] [[package]] name = "security-framework" -version = "2.11.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ "bitflags", "core-foundation", @@ -3470,9 +3470,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -3504,29 +3504,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "indexmap", "itoa", @@ -3570,20 +3570,14 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.4" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +checksum = "a9b57fd861253bff08bb1919e995f90ba8f4889de2726091c8876f3a4e823b40" dependencies = [ "cc", "cfg-if", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signature" version = "2.2.0" @@ -3632,14 +3626,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "sp1-lib" -version = "1.2.0" +version = "1.2.0-rc1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea7811abd2d3a991007fcb284f41152840b8388c171288d0c52c6793956609c" +checksum = "2f600a97b145e66f0f5e56595527c359f5cc8cb1f9dc8a6da1ba34bb845c9d1e" dependencies = [ "anyhow", "bincode", @@ -3711,7 +3705,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -3746,9 +3740,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" dependencies = [ "proc-macro2", "quote", @@ -3764,7 +3758,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -3811,35 +3805,34 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.13.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -3898,7 +3891,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -3909,7 +3902,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -3935,9 +3928,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -3947,9 +3940,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -3960,19 +3953,34 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", ] [[package]] @@ -4020,7 +4028,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -4056,9 +4064,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -4080,36 +4088,36 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.6" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "untrusted" @@ -4148,9 +4156,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wait-timeout" @@ -4188,35 +4196,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", - "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -4226,9 +4233,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4236,28 +4243,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -4265,11 +4272,11 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -4311,15 +4318,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -4386,9 +4384,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.20" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -4408,7 +4415,6 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive", ] @@ -4420,7 +4426,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] [[package]] @@ -4440,5 +4446,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.70", ] diff --git a/Cargo.toml b/Cargo.toml index 63c0ed6bb2..768098dcb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,15 +11,18 @@ members = [ "crates/interpreter", "crates/inspector", "crates/precompile", - "crates/optimism", "crates/database", "crates/database/interface", "crates/bytecode", "crates/state", "crates/wiring", + "crates/wiring/transaction", "crates/specification", "crates/statetest-types", + # variants + "crates/optimism", + # examples "examples/block_traces", "examples/contract_deployment", @@ -32,6 +35,7 @@ members = [ [workspace.dependencies] # revm +revm = { path = "crates/revm", version = "14.0.1", default-features = false } primitives = { path = "crates/primitives", package = "revm-primitives", version = "9.0.1", default-features = false } bytecode = { path = "crates/bytecode", package = "revm-bytecode", version = "1.0.0", default-features = false } database = { path = "crates/database", package = "revm-database", version = "1.0.0", default-features = false } @@ -39,7 +43,7 @@ database-interface = { path = "crates/database/interface", package = "revm-datab specification = { path = "crates/specification", package = "revm-specification", version = "1.0.0", default-features = false } state = { path = "crates/state", package = "revm-state", version = "1.0.0", default-features = false } wiring = { path = "crates/wiring", package = "revm-wiring", version = "1.0.0", default-features = false } -revm = { path = "crates/revm", version = "14.0.1", default-features = false } +transaction = { path = "crates/wiring/transaction", package = "revm-transaction", version = "1.0.0", default-features = false } interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "10.0.1", default-features = false } inspector = { path = "crates/inspector", package = "revm-inspector", version = "1.0.0", default-features = false } precompile = { path = "crates/precompile", package = "revm-precompile", version = "11.0.1", default-features = false } diff --git a/bins/revme/src/cmd/bytecode.rs b/bins/revme/src/cmd/bytecode.rs index a90551b289..62dfa91d56 100644 --- a/bins/revme/src/cmd/bytecode.rs +++ b/bins/revme/src/cmd/bytecode.rs @@ -1,7 +1,8 @@ use clap::Parser; use revm::{ bytecode::eof::{self, validate_eof_inner, CodeType, Eof, EofError}, - primitives::{hex, Bytes, MAX_INITCODE_SIZE}, + primitives::{hex, Bytes}, + specification::constants::MAX_INITCODE_SIZE, }; use std::io; diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index a51bcf6b1c..9dc709ac41 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -335,7 +335,7 @@ pub fn execute_test_suite( .unwrap_or_default(); env.tx.gas_priority_fee = unit.transaction.max_priority_fee_per_gas; // EIP-4844 - env.tx.blob_hashes = unit.transaction.blob_versioned_hashes; + env.tx.blob_hashes = unit.transaction.blob_versioned_hashes.clone(); env.tx.max_fee_per_blob_gas = unit.transaction.max_fee_per_blob_gas; // post and execution @@ -360,6 +360,17 @@ pub fn execute_test_suite( } for (index, test) in tests.into_iter().enumerate() { + // TODO TX TYPE needs to be set + let Some(tx_type) = unit.transaction.tx_type(test.indexes.data) else { + if test.expect_exception.is_some() { + continue; + } else { + panic!("Invalid transaction type without expected exception"); + } + }; + + env.tx.tx_type = tx_type; + env.tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); env.tx.data = unit @@ -378,17 +389,19 @@ pub fn execute_test_suite( .get(test.indexes.data) .and_then(Option::as_deref) .cloned() - .unwrap_or_default(); + .unwrap_or_default() + .into(); - env.tx.authorization_list = - unit.transaction - .authorization_list - .as_ref() - .map(|auth_list| { - AuthorizationList::Recovered( - auth_list.iter().map(|auth| auth.into_recovered()).collect(), - ) - }); + env.tx.authorization_list = unit + .transaction + .authorization_list + .as_ref() + .map(|auth_list| { + AuthorizationList::Recovered( + auth_list.iter().map(|auth| auth.into_recovered()).collect(), + ) + }) + .unwrap_or_default(); let to = match unit.transaction.to { Some(add) => TxKind::Call(add), diff --git a/crates/bytecode/src/eof/verification.rs b/crates/bytecode/src/eof/verification.rs index 1f922e8657..dd14a216b6 100644 --- a/crates/bytecode/src/eof/verification.rs +++ b/crates/bytecode/src/eof/verification.rs @@ -3,8 +3,8 @@ use crate::{ opcode::{self, OPCODE_INFO}, utils::{read_i16, read_u16}, }; -use primitives::{Bytes, MAX_INITCODE_SIZE}; -use specification::constants::STACK_LIMIT; +use primitives::Bytes; +use specification::constants::{MAX_INITCODE_SIZE, STACK_LIMIT}; use core::{convert::identity, mem}; use std::{borrow::Cow, fmt, vec, vec::Vec}; diff --git a/crates/context/CHANGELOG.md b/crates/context/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/context/Cargo.toml b/crates/context/Cargo.toml new file mode 100644 index 0000000000..83b0064a81 --- /dev/null +++ b/crates/context/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "revm-context" +description = "Revm context crates" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unreachable_pub = "warn" +unused_must_use = "deny" +rust_2018_idioms = "deny" + +[lints.rustdoc] +all = "warn" + +[dependencies] +# Optional +serde = { version = "1.0", default-features = false, features = [ + "derive", + "rc", +], optional = true } + +[features] +default = ["std"] +std = ["serde?/std"] +serde = ["dep:serde"] +serde-json = ["serde"] diff --git a/crates/context/src/lib.rs b/crates/context/src/lib.rs new file mode 100644 index 0000000000..b3ceb57058 --- /dev/null +++ b/crates/context/src/lib.rs @@ -0,0 +1,8 @@ +//! Optimism-specific constants, types, and helpers. +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc as std; + + diff --git a/crates/inspector/Cargo.toml b/crates/inspector/Cargo.toml index 458b8c45eb..393b30659f 100644 --- a/crates/inspector/Cargo.toml +++ b/crates/inspector/Cargo.toml @@ -39,6 +39,7 @@ serde_json = { version = "1.0", default-features = false, features = [ ], optional = true } [dev-dependencies] +revm = { workspace = true, features = ["serde"] } database = { workspace = true, features = ["serde"] } [features] diff --git a/crates/inspector/src/customprinter.rs b/crates/inspector/src/customprinter.rs index b4b0700dc8..d87096a42b 100644 --- a/crates/inspector/src/customprinter.rs +++ b/crates/inspector/src/customprinter.rs @@ -151,6 +151,7 @@ mod test { tx.transact_to = TxKind::Call(callee); tx.data = Bytes::new(); tx.value = U256::ZERO; + tx.gas_limit = 100_000; }) .with_spec_id(SpecId::BERLIN) .append_handler_register(inspector_handle_register) diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 5197e1ff07..8fd1c22323 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -177,7 +177,8 @@ impl TracerEip3155 { state_root: B256::ZERO.to_string(), output: result.output.to_string(), gas_used: hex_number( - context.inner.env().tx.gas_limit() - self.gas_inspector.gas_remaining(), + context.inner.env().tx.common_fields().gas_limit() + - self.gas_inspector.gas_remaining(), ), pass: result.is_ok(), time: None, diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index fe3dd7cfd7..e2d99ad3fc 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -27,6 +27,7 @@ bytecode.workspace = true primitives.workspace = true specification.workspace = true wiring.workspace = true +transaction.workspace = true # mics derive-where = { version = "1.2.7", default-features = false } diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 74376ee4f2..b9af574a3b 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -1,7 +1,8 @@ use super::constants::*; use crate::{num_words, AccountLoad, Eip7702CodeLoad, SStoreResult, SelfDestructResult, StateLoad}; use primitives::U256; -use specification::{eip2930::AccessListItem, eip7702, hardfork::SpecId}; +use specification::{eip7702, hardfork::SpecId}; +use transaction::AccessListTrait; /// `const` Option `?`. macro_rules! tri { @@ -357,11 +358,11 @@ pub const fn memory_gas(num_words: u64) -> u64 { /// Initial gas that is deducted for transaction to be included. /// Initial gas contains initial stipend gas, gas for access list and input data. -pub fn validate_initial_tx_gas( +pub fn validate_initial_tx_gas( spec_id: SpecId, input: &[u8], is_create: bool, - access_list: &[AccessListItem], + access_list: Option<&AccessListT>, authorization_list_num: u64, ) -> u64 { let mut initial_gas = 0; @@ -379,10 +380,10 @@ pub fn validate_initial_tx_gas( }; // get number of access list account and storages. - if spec_id.is_enabled_in(SpecId::BERLIN) { - let accessed_slots: usize = access_list.iter().map(|item| item.storage_keys.len()).sum(); - initial_gas += access_list.len() as u64 * ACCESS_LIST_ADDRESS; - initial_gas += accessed_slots as u64 * ACCESS_LIST_STORAGE_KEY; + if let Some(access_list) = access_list { + let (account_num, storage_num) = access_list.num_account_storages(); + initial_gas += account_num as u64 * ACCESS_LIST_ADDRESS; + initial_gas += storage_num as u64 * ACCESS_LIST_STORAGE_KEY; } // base stipend @@ -403,7 +404,7 @@ pub fn validate_initial_tx_gas( initial_gas += initcode_cost(input.len() as u64) } - // EIP-7702 + // EIP-7702 if spec_id.is_enabled_in(SpecId::PRAGUE) { initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST; } diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index 89fe5132ed..ae411d35de 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -4,15 +4,16 @@ pub mod macros; pub mod arithmetic; pub mod bitwise; +pub mod block_info; pub mod contract; pub mod control; pub mod data; pub mod host; -pub mod host_env; pub mod i256; pub mod memory; pub mod stack; pub mod system; +pub mod tx_info; pub mod utility; use crate::Host; @@ -64,7 +65,7 @@ pub const fn instruction_table() -> [crate::table: table[ADDRESS as usize] = system::address; table[BALANCE as usize] = host::balance::; - table[ORIGIN as usize] = host_env::origin; + table[ORIGIN as usize] = tx_info::origin; table[CALLER as usize] = system::caller; table[CALLVALUE as usize] = system::callvalue; table[CALLDATALOAD as usize] = system::calldataload; @@ -73,23 +74,23 @@ pub const fn instruction_table() -> [crate::table: table[CODESIZE as usize] = system::codesize; table[CODECOPY as usize] = system::codecopy; - table[GASPRICE as usize] = host_env::gasprice; + table[GASPRICE as usize] = tx_info::gasprice; table[EXTCODESIZE as usize] = host::extcodesize::; table[EXTCODECOPY as usize] = host::extcodecopy::; table[RETURNDATASIZE as usize] = system::returndatasize::; table[RETURNDATACOPY as usize] = system::returndatacopy::; table[EXTCODEHASH as usize] = host::extcodehash::; table[BLOCKHASH as usize] = host::blockhash::; - table[COINBASE as usize] = host_env::coinbase; - table[TIMESTAMP as usize] = host_env::timestamp; - table[NUMBER as usize] = host_env::block_number; - table[DIFFICULTY as usize] = host_env::difficulty::; - table[GASLIMIT as usize] = host_env::gaslimit; - table[CHAINID as usize] = host_env::chainid::; + table[COINBASE as usize] = block_info::coinbase; + table[TIMESTAMP as usize] = block_info::timestamp; + table[NUMBER as usize] = block_info::block_number; + table[DIFFICULTY as usize] = block_info::difficulty::; + table[GASLIMIT as usize] = block_info::gaslimit; + table[CHAINID as usize] = block_info::chainid::; table[SELFBALANCE as usize] = host::selfbalance::; - table[BASEFEE as usize] = host_env::basefee::; - table[BLOBHASH as usize] = host_env::blob_hash::; - table[BLOBBASEFEE as usize] = host_env::blob_basefee::; + table[BASEFEE as usize] = block_info::basefee::; + table[BLOBHASH as usize] = tx_info::blob_hash::; + table[BLOBBASEFEE as usize] = block_info::blob_basefee::; table[POP as usize] = stack::pop; table[MLOAD as usize] = memory::mload; diff --git a/crates/interpreter/src/instructions/host_env.rs b/crates/interpreter/src/instructions/block_info.rs similarity index 65% rename from crates/interpreter/src/instructions/host_env.rs rename to crates/interpreter/src/instructions/block_info.rs index 17f9879ab8..9cb656fbc5 100644 --- a/crates/interpreter/src/instructions/host_env.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -1,7 +1,7 @@ use crate::{gas, Host, Interpreter}; use primitives::U256; use specification::hardfork::{Spec, SpecId::*}; -use wiring::{Block, Transaction}; +use wiring::Block; /// EIP-1344: ChainID opcode pub fn chainid(interpreter: &mut Interpreter, host: &mut H) { @@ -39,11 +39,6 @@ pub fn gaslimit(interpreter: &mut Interpreter, host: &mut H) { push!(interpreter, *host.env().block.gas_limit()); } -pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, host.env().effective_gas_price()); -} - /// EIP-3198: BASEFEE opcode pub fn basefee(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, LONDON); @@ -51,35 +46,12 @@ pub fn basefee(interpreter: &mut Interpreter, host push!(interpreter, *host.env().block.basefee()); } -pub fn origin(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().tx.caller().into_word()); -} - -// EIP-4844: Shard Blob Transactions -pub fn blob_hash(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, index); - let i = as_usize_saturated!(index); - *index = match host.env().tx.blob_hashes().get(i) { - Some(hash) => U256::from_be_bytes(hash.0), - None => U256::ZERO, - }; -} - /// EIP-7516: BLOBBASEFEE opcode pub fn blob_basefee(interpreter: &mut Interpreter, host: &mut H) { check!(interpreter, CANCUN); gas!(interpreter, gas::BASE); push!( interpreter, - U256::from( - host.env() - .block - .get_blob_gasprice() - .copied() - .unwrap_or_default() - ) + U256::from(host.env().block.blob_gasprice().unwrap_or_default()) ); } diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 06be29e18d..17c3f64631 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -5,6 +5,7 @@ pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges, resize_me use crate::{ gas::{self, cost_per_word, EOF_CREATE_GAS, KECCAK256WORD, MIN_CALLEE_GAS}, interpreter::Interpreter, + interpreter_action::NewFrameAction, CallInputs, CallScheme, CallValue, CreateInputs, EOFCreateInputs, Host, InstructionResult, InterpreterAction, InterpreterResult, MAX_INITCODE_SIZE, }; @@ -69,16 +70,16 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) gas!(interpreter, gas_limit); // Send container for execution container is preverified. interpreter.instruction_result = InstructionResult::CallOrCreate; - interpreter.next_action = InterpreterAction::EOFCreate { - inputs: Box::new(EOFCreateInputs::new_opcode( + interpreter.next_action = InterpreterAction::NewFrame(NewFrameAction::EOFCreate(Box::new( + EOFCreateInputs::new_opcode( interpreter.contract.target_address, created_address, value, eof, gas_limit, input, - )), - }; + ), + ))); interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) }; } @@ -239,8 +240,8 @@ pub fn extcall(interpreter: &mut Interpreter, host }; // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { input, gas_limit, target_address, @@ -251,8 +252,7 @@ pub fn extcall(interpreter: &mut Interpreter, host is_static: interpreter.is_static, is_eof: true, return_memory_offset: 0..0, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -274,8 +274,8 @@ pub fn extdelegatecall(interpreter: &mut Interpret }; // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { input, gas_limit, target_address: interpreter.contract.target_address, @@ -286,8 +286,7 @@ pub fn extdelegatecall(interpreter: &mut Interpret is_static: interpreter.is_static, is_eof: true, return_memory_offset: 0..0, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -309,8 +308,8 @@ pub fn extstaticcall(interpreter: &mut Interpreter, host: &mut }; // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { input, gas_limit, target_address, @@ -321,8 +320,7 @@ pub fn extstaticcall(interpreter: &mut Interpreter, host: &mut is_static: true, is_eof: true, return_memory_offset: 0..0, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -384,15 +382,14 @@ pub fn create( gas!(interpreter, gas_limit); // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Create { - inputs: Box::new(CreateInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Create(Box::new(CreateInputs { caller: interpreter.contract.target_address, scheme, value, init_code: code, gas_limit, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -431,8 +428,8 @@ pub fn call(interpreter: &mut Interpreter, host: & } // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { input, gas_limit, target_address: to, @@ -443,8 +440,7 @@ pub fn call(interpreter: &mut Interpreter, host: & is_static: interpreter.is_static, is_eof: false, return_memory_offset, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -479,8 +475,8 @@ pub fn call_code(interpreter: &mut Interpreter, ho } // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { input, gas_limit, target_address: interpreter.contract.target_address, @@ -491,8 +487,7 @@ pub fn call_code(interpreter: &mut Interpreter, ho is_static: interpreter.is_static, is_eof: false, return_memory_offset, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -520,8 +515,8 @@ pub fn delegate_call(interpreter: &mut Interpreter gas!(interpreter, gas_limit); // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { input, gas_limit, target_address: interpreter.contract.target_address, @@ -532,8 +527,7 @@ pub fn delegate_call(interpreter: &mut Interpreter is_static: interpreter.is_static, is_eof: false, return_memory_offset, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } @@ -560,8 +554,8 @@ pub fn static_call(interpreter: &mut Interpreter, gas!(interpreter, gas_limit); // Call host to interact with target contract - interpreter.next_action = InterpreterAction::Call { - inputs: Box::new(CallInputs { + interpreter.next_action = + InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { input, gas_limit, target_address: to, @@ -572,7 +566,6 @@ pub fn static_call(interpreter: &mut Interpreter, is_static: true, is_eof: false, return_memory_offset, - }), - }; + }))); interpreter.instruction_result = InstructionResult::CallOrCreate; } diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs new file mode 100644 index 0000000000..52f6fe359e --- /dev/null +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -0,0 +1,39 @@ +use crate::{gas, Host, Interpreter}; +use primitives::U256; +use specification::hardfork::Spec; +use transaction::Eip4844Tx; +use wiring::{Block, Transaction, TransactionType}; + +pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { + gas!(interpreter, gas::BASE); + let env = host.env(); + let basefee = *env.block.basefee(); + push!(interpreter, env.tx.effective_gas_price(basefee)); +} + +pub fn origin(interpreter: &mut Interpreter, host: &mut H) { + gas!(interpreter, gas::BASE); + push_b256!( + interpreter, + host.env().tx.common_fields().caller().into_word() + ); +} + +// EIP-4844: Shard Blob Transactions +pub fn blob_hash(interpreter: &mut Interpreter, host: &mut H) { + check!(interpreter, CANCUN); + gas!(interpreter, gas::VERYLOW); + pop_top!(interpreter, index); + let i = as_usize_saturated!(index); + let tx = &host.env().tx; + *index = if tx.tx_type().into() == TransactionType::Eip4844 { + tx.eip4844() + .blob_versioned_hashes() + .get(i) + .cloned() + .map(|b| U256::from_be_bytes(*b)) + .unwrap_or(U256::ZERO) + } else { + U256::ZERO + }; +} diff --git a/crates/interpreter/src/interpreter/contract.rs b/crates/interpreter/src/interpreter/contract.rs index 04bf4576e8..0863701cf8 100644 --- a/crates/interpreter/src/interpreter/contract.rs +++ b/crates/interpreter/src/interpreter/contract.rs @@ -57,22 +57,20 @@ impl Contract { bytecode: Bytecode, hash: Option, ) -> Self { - let contract_address = match env.tx.kind() { - TxKind::Call(caller) => caller, - TxKind::Create => Address::ZERO, - }; let bytecode_address = match env.tx.kind() { TxKind::Call(caller) => Some(caller), TxKind::Create => None, }; + let target_address = bytecode_address.unwrap_or_default(); + Self::new( - env.tx.data().clone(), + env.tx.common_fields().input().clone(), bytecode, hash, - contract_address, + target_address, bytecode_address, - *env.tx.caller(), - *env.tx.value(), + env.tx.common_fields().caller(), + env.tx.common_fields().value(), ) } diff --git a/crates/interpreter/src/interpreter_action.rs b/crates/interpreter/src/interpreter_action.rs index 2bcde57ffa..5b606a5e71 100644 --- a/crates/interpreter/src/interpreter_action.rs +++ b/crates/interpreter/src/interpreter_action.rs @@ -13,16 +13,23 @@ pub use eof_create_inputs::{EOFCreateInputs, EOFCreateKind}; use crate::InterpreterResult; use std::boxed::Box; -#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum InterpreterAction { +pub enum NewFrameAction { /// CALL, CALLCODE, DELEGATECALL, STATICCALL - /// or EOF EXT instruction called. - Call { inputs: Box }, + /// or EOF EXT*CALL instruction called. + Call(Box), /// CREATE or CREATE2 instruction called. - Create { inputs: Box }, + Create(Box), /// EOF CREATE instruction called. - EOFCreate { inputs: Box }, + EOFCreate(Box), +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum InterpreterAction { + /// New frame + NewFrame(NewFrameAction), /// Interpreter finished execution. Return { result: InterpreterResult }, /// No action @@ -33,12 +40,15 @@ pub enum InterpreterAction { impl InterpreterAction { /// Returns true if action is call. pub fn is_call(&self) -> bool { - matches!(self, InterpreterAction::Call { .. }) + matches!(self, InterpreterAction::NewFrame(NewFrameAction::Call(..))) } /// Returns true if action is create. pub fn is_create(&self) -> bool { - matches!(self, InterpreterAction::Create { .. }) + matches!( + self, + InterpreterAction::NewFrame(NewFrameAction::Create(..)) + ) } /// Returns true if action is return. diff --git a/crates/interpreter/src/interpreter_action/call_inputs.rs b/crates/interpreter/src/interpreter_action/call_inputs.rs index 3adaa05e57..3976e80be3 100644 --- a/crates/interpreter/src/interpreter_action/call_inputs.rs +++ b/crates/interpreter/src/interpreter_action/call_inputs.rs @@ -1,7 +1,5 @@ use core::ops::Range; -use primitives::{Address, Bytes, TxKind, U256}; -use std::boxed::Box; -use wiring::Transaction; +use primitives::{Address, Bytes, U256}; /// Inputs for a call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -44,34 +42,6 @@ pub struct CallInputs { } impl CallInputs { - /// Creates new call inputs. - /// - /// Returns `None` if the transaction is not a call. - pub fn new(tx_env: &impl Transaction, gas_limit: u64) -> Option { - let TxKind::Call(target_address) = tx_env.kind() else { - return None; - }; - Some(CallInputs { - input: tx_env.data().clone(), - gas_limit, - target_address, - bytecode_address: target_address, - caller: *tx_env.caller(), - value: CallValue::Transfer(*tx_env.value()), - scheme: CallScheme::Call, - is_static: false, - is_eof: false, - return_memory_offset: 0..0, - }) - } - - /// Creates new boxed call inputs. - /// - /// Returns `None` if the transaction is not a call. - pub fn new_boxed(tx_env: &impl Transaction, gas_limit: u64) -> Option> { - Self::new(tx_env, gas_limit).map(Box::new) - } - /// Returns `true` if the call will transfer a non-zero value. #[inline] pub fn transfers_value(&self) -> bool { diff --git a/crates/interpreter/src/interpreter_action/create_inputs.rs b/crates/interpreter/src/interpreter_action/create_inputs.rs index 5c333cca07..adab30c430 100644 --- a/crates/interpreter/src/interpreter_action/create_inputs.rs +++ b/crates/interpreter/src/interpreter_action/create_inputs.rs @@ -1,6 +1,5 @@ -use primitives::{Address, Bytes, TxKind, U256}; -use std::boxed::Box; -use wiring::{default::CreateScheme, Transaction}; +use primitives::{Address, Bytes, U256}; +use wiring::default::CreateScheme; /// Inputs for a create call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -19,26 +18,6 @@ pub struct CreateInputs { } impl CreateInputs { - /// Creates new create inputs. - pub fn new(tx_env: &impl Transaction, gas_limit: u64) -> Option { - let TxKind::Create = tx_env.kind() else { - return None; - }; - - Some(CreateInputs { - caller: *tx_env.caller(), - scheme: CreateScheme::Create, - value: *tx_env.value(), - init_code: tx_env.data().clone(), - gas_limit, - }) - } - - /// Returns boxed create inputs. - pub fn new_boxed(tx_env: &impl Transaction, gas_limit: u64) -> Option> { - Self::new(tx_env, gas_limit).map(Box::new) - } - /// Returns the address that this create call will create. pub fn created_address(&self, nonce: u64) -> Address { match self.scheme { diff --git a/crates/interpreter/src/interpreter_action/eof_create_inputs.rs b/crates/interpreter/src/interpreter_action/eof_create_inputs.rs index e0bfe51243..54ed29e150 100644 --- a/crates/interpreter/src/interpreter_action/eof_create_inputs.rs +++ b/crates/interpreter/src/interpreter_action/eof_create_inputs.rs @@ -1,6 +1,5 @@ use bytecode::Eof; use primitives::{Address, Bytes, U256}; -use wiring::{EvmWiring, Transaction}; /// EOF create can be called from two places: /// * EOFCREATE opcode @@ -74,18 +73,6 @@ impl EOFCreateInputs { } } - /// Creates new EOFCreateInputs from transaction. - pub fn new_tx(tx: &EvmWiringT::Transaction, gas_limit: u64) -> Self { - EOFCreateInputs::new( - *tx.caller(), - *tx.value(), - gas_limit, - EOFCreateKind::Tx { - initdata: tx.data().clone(), - }, - ) - } - /// Returns a new instance of EOFCreateInput. pub fn new_opcode( caller: Address, diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index e2821e06f5..c124d80d50 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -39,8 +39,8 @@ pub use interpreter::{ }; pub use interpreter_action::{ CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, EOFCreateInputs, - EOFCreateKind, InterpreterAction, + EOFCreateKind, InterpreterAction, NewFrameAction, }; -pub use primitives::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; +pub use specification::constants::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; pub use table::Instruction; pub use wiring::default::CreateScheme; diff --git a/crates/optimism/src/env.rs b/crates/optimism/src/env.rs deleted file mode 100644 index 520436baf4..0000000000 --- a/crates/optimism/src/env.rs +++ /dev/null @@ -1,119 +0,0 @@ -use revm::{ - primitives::{Address, Bytes, TxKind, B256, U256}, - specification::{eip2930::AccessListItem, eip7702::AuthorizationList}, - wiring::{ - default::TxEnv as EthTxEnv, - transaction::{Transaction, TransactionValidation}, - }, -}; - -use super::{OptimismInvalidTransaction, OptimismTransaction}; - -/// The Optimism transaction environment. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TxEnv { - #[cfg_attr(feature = "serde", serde(flatten))] - pub base: EthTxEnv, - - /// The source hash is used to make sure that deposit transactions do - /// not have identical hashes. - /// - /// L1 originated deposit transaction source hashes are computed using - /// the hash of the l1 block hash and the l1 log index. - /// L1 attributes deposit source hashes are computed with the l1 block - /// hash and the sequence number = l2 block number - l2 epoch start - /// block number. - /// - /// These two deposit transaction sources specify a domain in the outer - /// hash so there are no collisions. - pub source_hash: Option, - /// The amount to increase the balance of the `from` account as part of - /// a deposit transaction. This is unconditional and is applied to the - /// `from` account even if the deposit transaction fails since - /// the deposit is pre-paid on L1. - pub mint: Option, - /// Whether or not the transaction is a system transaction. - pub is_system_transaction: Option, - /// An enveloped EIP-2718 typed transaction. This is used - /// to compute the L1 tx cost using the L1 block info, as - /// opposed to requiring downstream apps to compute the cost - /// externally. - pub enveloped_tx: Option, -} - -impl Transaction for TxEnv { - fn caller(&self) -> &Address { - self.base.caller() - } - - fn gas_limit(&self) -> u64 { - self.base.gas_limit() - } - - fn gas_price(&self) -> &U256 { - self.base.gas_price() - } - - fn kind(&self) -> TxKind { - self.base.kind() - } - - fn value(&self) -> &U256 { - self.base.value() - } - - fn data(&self) -> &Bytes { - self.base.data() - } - - fn nonce(&self) -> u64 { - self.base.nonce() - } - - fn chain_id(&self) -> Option { - self.base.chain_id() - } - - fn access_list(&self) -> &[AccessListItem] { - self.base.access_list() - } - - fn max_priority_fee_per_gas(&self) -> Option<&U256> { - self.base.max_priority_fee_per_gas() - } - - fn blob_hashes(&self) -> &[B256] { - self.base.blob_hashes() - } - - fn max_fee_per_blob_gas(&self) -> Option<&U256> { - self.base.max_fee_per_blob_gas() - } - - fn authorization_list(&self) -> Option<&AuthorizationList> { - self.base.authorization_list() - } -} - -impl OptimismTransaction for TxEnv { - fn source_hash(&self) -> Option<&B256> { - self.source_hash.as_ref() - } - - fn mint(&self) -> Option<&u128> { - self.mint.as_ref() - } - - fn is_system_transaction(&self) -> Option { - self.is_system_transaction - } - - fn enveloped_tx(&self) -> Option { - self.enveloped_tx.clone() - } -} - -impl TransactionValidation for TxEnv { - type ValidationError = OptimismInvalidTransaction; -} diff --git a/crates/optimism/src/fast_lz.rs b/crates/optimism/src/fast_lz.rs index 5c75a8820e..847170531e 100644 --- a/crates/optimism/src/fast_lz.rs +++ b/crates/optimism/src/fast_lz.rs @@ -105,6 +105,9 @@ fn u24(input: &[u8], idx: u32) -> u32 { #[cfg(test)] mod tests { + use crate::wiring::OptimismEvmWiring; + use crate::OpTransaction; + use super::*; use alloy_sol_types::sol; use alloy_sol_types::SolCall; @@ -112,7 +115,6 @@ mod tests { use revm::{ bytecode::Bytecode, primitives::{address, bytes, Bytes, TxKind, U256}, - wiring::EthereumWiring, Evm, }; use std::vec::Vec; @@ -164,13 +166,18 @@ mod tests { let native_val = flz_compress_len(&input); - let mut evm = Evm::>::builder() + let mut evm = Evm::>::builder() .with_db(BenchmarkDB::new_bytecode(contract_bytecode.clone())) .with_default_ext_ctx() .modify_tx_env(|tx| { + let OpTransaction::Base { tx, enveloped_tx } = tx else { + panic!("Default is base tx"); + }; tx.caller = address!("1000000000000000000000000000000000000000"); tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); tx.data = FastLz::fastLzCall::new((input,)).abi_encode().into(); + tx.gas_limit = 300_000; + *enveloped_tx = Some(Bytes::default()); }) .build(); diff --git a/crates/optimism/src/handler_register.rs b/crates/optimism/src/handler_register.rs index 294b605986..95928b5282 100644 --- a/crates/optimism/src/handler_register.rs +++ b/crates/optimism/src/handler_register.rs @@ -1,21 +1,26 @@ //! Handler related to Optimism chain -use super::{ - optimism_spec_to_generic, OptimismContext, OptimismHaltReason, OptimismInvalidTransaction, - OptimismSpec, OptimismSpecId, OptimismTransaction, OptimismWiring, +use crate::{ + optimism_spec_to_generic, + transaction::{ + deposit::DepositTransaction, error::OpTransactionError, OpTransactionType, OpTxTrait, + }, + wiring::{OptimismContextTrait, OptimismWiring}, + OptimismHaltReason, OptimismSpec, OptimismSpecId, }; use crate::{BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT}; use core::ops::Mul; use revm::{ database_interface::Database, handler::{ - mainnet::{self, deduct_caller_inner}, + mainnet::{self, deduct_caller_inner, validate_block_env, validate_tx_env}, register::EvmHandler, }, interpreter::{return_ok, return_revert, Gas}, precompile::{secp256r1, PrecompileSpecId}, primitives::{HashMap, U256}, state::Account, + transaction::CommonTxFields, wiring::{ default::EnvWiring, result::{ @@ -60,22 +65,28 @@ pub fn validate_env( env: &EnvWiring, ) -> EVMResultGeneric<(), EvmWiringT> { // Do not perform any extra validation for deposit transactions, they are pre-verified on L1. - if env.tx.source_hash().is_some() { + let tx_type = env.tx.tx_type(); + if tx_type == OpTransactionType::Deposit { + let tx = env.tx.deposit(); + // Do not allow for a system transaction to be processed if Regolith is enabled. + // TODO check if this is correct. + if tx.is_system_transaction() && SPEC::optimism_enabled(OptimismSpecId::REGOLITH) { + return Err(OpTransactionError::DepositSystemTxPostRegolith.into()); + } return Ok(()); } // Important: validate block before tx. - env.validate_block_env::()?; + //validate_block_env::()?; + // Important: validate block before tx as some field are used in transaction validation. + validate_block_env::(&env.block).map_err(EVMError::Header)?; - // Do not allow for a system transaction to be processed if Regolith is enabled. - if env.tx.is_system_transaction().unwrap_or(false) - && SPEC::optimism_enabled(OptimismSpecId::REGOLITH) - { - return Err(OptimismInvalidTransaction::DepositSystemTxPostRegolith.into()); - } + // env.validate_tx::() + // .map_err(OptimismInvalidTransaction::Base)?; - env.validate_tx::() - .map_err(OptimismInvalidTransaction::Base)?; + // validate transaction. + validate_tx_env::(&env.tx, &env.block, &env.cfg) + .map_err(OpTransactionError::Base)?; Ok(()) } @@ -84,7 +95,7 @@ pub fn validate_env( pub fn validate_tx_against_state( context: &mut Context, ) -> EVMResultGeneric<(), EvmWiringT> { - if context.evm.inner.env.tx.source_hash().is_some() { + if context.evm.env.tx.tx_type() == OpTransactionType::Deposit { return Ok(()); } mainnet::validate_tx_against_state::(context) @@ -97,9 +108,8 @@ pub fn last_frame_return( frame_result: &mut FrameResult, ) -> EVMResultGeneric<(), EvmWiringT> { let env = context.evm.inner.env(); - let is_deposit = env.tx.source_hash().is_some(); - let tx_system = env.tx.is_system_transaction(); - let tx_gas_limit = env.tx.gas_limit(); + let is_deposit = env.tx.tx_type() == OpTransactionType::Deposit; + let tx_gas_limit = env.tx.common_fields().gas_limit(); let is_regolith = SPEC::optimism_enabled(OptimismSpecId::REGOLITH); let instruction_result = frame_result.interpreter_result().result; @@ -129,10 +139,13 @@ pub fn last_frame_return( // Regolith, gas is reported as normal. gas.erase_cost(remaining); gas.record_refund(refunded); - } else if is_deposit && tx_system.unwrap_or(false) { - // System transactions were a special type of deposit transaction in - // the Bedrock hardfork that did not incur any gas costs. - gas.erase_cost(tx_gas_limit); + } else if is_deposit { + let tx = env.tx.deposit(); + if tx.is_system_transaction() { + // System transactions were a special type of deposit transaction in + // the Bedrock hardfork that did not incur any gas costs. + gas.erase_cost(tx_gas_limit); + } } } return_revert!() => { @@ -167,7 +180,7 @@ pub fn refund( gas.record_refund(eip7702_refund); let env = context.evm.inner.env(); - let is_deposit = env.tx.source_hash().is_some(); + let is_deposit = env.tx.tx_type() == OpTransactionType::Deposit; let is_regolith = SPEC::optimism_enabled(OptimismSpecId::REGOLITH); // Prior to Regolith, deposit transactions did not receive gas refunds. @@ -207,7 +220,7 @@ pub fn load_accounts( ) -> EVMResultGeneric<(), EvmWiringT> { // the L1-cost fee is only computed for Optimism non-deposit transactions. - if context.evm.env.tx.source_hash().is_none() { + if context.evm.env.tx.tx_type() != OpTransactionType::Deposit { let l1_block_info = super::L1BlockInfo::try_fetch(&mut context.evm.inner.db, SPEC::OPTIMISM_SPEC_ID) .map_err(EVMError::Database)?; @@ -224,22 +237,25 @@ pub fn load_accounts( pub fn deduct_caller( context: &mut Context, ) -> EVMResultGeneric<(), EvmWiringT> { + let caller = context.evm.inner.env.tx.common_fields().caller(); // load caller's account. let mut caller_account = context .evm .inner .journaled_state - .load_account( - *context.evm.inner.env.tx.caller(), - &mut context.evm.inner.db, - ) + .load_account(caller, &mut context.evm.inner.db) .map_err(EVMError::Database)?; + let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; + // If the transaction is a deposit with a `mint` value, add the mint value // in wei to the caller's balance. This should be persisted to the database // prior to the rest of execution. - if let Some(mint) = context.evm.inner.env.tx.mint() { - caller_account.info.balance += U256::from(*mint); + if is_deposit { + let tx = context.evm.inner.env.tx.deposit(); + if let Some(mint) = tx.mint() { + caller_account.info.balance += U256::from(mint); + } } // We deduct caller max balance after minting and before deducing the @@ -248,13 +264,15 @@ pub fn deduct_caller( // If the transaction is not a deposit transaction, subtract the L1 data fee from the // caller's balance directly after minting the requested amount of ETH. - if context.evm.inner.env.tx.source_hash().is_none() { + if !is_deposit { // get envelope - let Some(enveloped_tx) = &context.evm.inner.env.tx.enveloped_tx() else { - return Err(EVMError::Custom( - "[OPTIMISM] Failed to load enveloped transaction.".into(), - )); - }; + let enveloped_tx = context + .evm + .inner + .env + .tx + .enveloped_tx() + .expect("all not deposit tx have enveloped tx"); let tx_l1_cost = context .evm @@ -283,7 +301,7 @@ pub fn reward_beneficiary( context: &mut Context, gas: &Gas, ) -> EVMResultGeneric<(), EvmWiringT> { - let is_deposit = context.evm.inner.env.tx.source_hash().is_some(); + let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; // transfer fee to coinbase/beneficiary. if !is_deposit { @@ -348,10 +366,10 @@ pub fn output( // Post-regolith, if the transaction is a deposit transaction and it halts, // we bubble up to the global return handler. The mint value will be persisted // and the caller nonce will be incremented there. - let is_deposit = context.evm.inner.env.tx.source_hash().is_some(); + let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; if is_deposit && SPEC::optimism_enabled(OptimismSpecId::REGOLITH) { return Err(EVMError::Transaction( - OptimismInvalidTransaction::HaltedDepositPostRegolith, + OpTransactionError::HaltedDepositPostRegolith, )); } } @@ -364,17 +382,16 @@ pub fn end( context: &mut Context, evm_output: EVMResult, ) -> EVMResult { + let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; evm_output.or_else(|err| { - if matches!(err, EVMError::Transaction(_)) - && context.evm.inner.env().tx.source_hash().is_some() - { + if matches!(err, EVMError::Transaction(_)) && is_deposit { + let tx = context.evm.inner.env.tx.deposit(); // If the transaction is a deposit transaction and it failed // for any reason, the caller nonce must be bumped, and the // gas reported must be altered depending on the Hardfork. This is // also returned as a special Halt variant so that consumers can more // easily distinguish between a failed deposit and a failed // normal transaction. - let caller = *context.evm.inner.env().tx.caller(); // Increment sender nonce and account balance for the mint amount. Deposits // always persist the mint amount, even if the transaction fails. @@ -382,32 +399,30 @@ pub fn end( let mut acc = Account::from( context .evm + .inner .db - .basic(caller) + .basic(tx.caller()) .unwrap_or_default() .unwrap_or_default(), ); acc.info.nonce = acc.info.nonce.saturating_add(1); - acc.info.balance = acc.info.balance.saturating_add(U256::from( - context.evm.inner.env().tx.mint().cloned().unwrap_or(0), - )); + acc.info.balance = acc + .info + .balance + .saturating_add(U256::from(tx.mint().unwrap_or_default())); acc.mark_touch(); acc }; - let state = HashMap::from_iter([(caller, account)]); + let state = HashMap::from_iter([(tx.caller(), account)]); // The gas used of a failed deposit post-regolith is the gas // limit of the transaction. pre-regolith, it is the gas limit // of the transaction for non system transactions and 0 for system // transactions. - let is_system_tx = context - .evm - .env() - .tx - .is_system_transaction() - .unwrap_or(false); - let gas_used = if SPEC::optimism_enabled(OptimismSpecId::REGOLITH) || !is_system_tx { - context.evm.inner.env().tx.gas_limit() + let gas_used = if SPEC::optimism_enabled(OptimismSpecId::REGOLITH) + || !tx.is_system_transaction() + { + tx.gas_limit() } else { 0 }; @@ -428,13 +443,17 @@ pub fn end( #[cfg(test)] mod tests { use super::*; - use crate::{BedrockSpec, L1BlockInfo, LatestSpec, OptimismEvmWiring, RegolithSpec}; + use crate::{ + transaction::deposit::TxDeposit, wiring::OptimismEvmWiring, BedrockSpec, L1BlockInfo, + LatestSpec, OpTransaction, RegolithSpec, + }; use database::InMemoryDB; use revm::{ database_interface::EmptyDB, interpreter::{CallOutcome, InstructionResult, InterpreterResult}, primitives::{bytes, Address, Bytes, B256}, state::AccountInfo, + wiring::default::{block::BlockEnv, Env, TxEnv}, }; use std::boxed::Box; @@ -468,8 +487,14 @@ mod tests { #[test] fn test_revert_gas() { let mut env = EnvWiring::::default(); - env.tx.base.gas_limit = 100; - env.tx.source_hash = None; + let tx = TxEnv { + gas_limit: 100, + ..Default::default() + }; + env.tx = OpTransaction::Base { + tx, + enveloped_tx: None, + }; let gas = call_last_frame_return::(env, InstructionResult::Revert, Gas::new(90)); @@ -481,8 +506,15 @@ mod tests { #[test] fn test_consume_gas() { let mut env = EnvWiring::::default(); - env.tx.base.gas_limit = 100; - env.tx.source_hash = Some(B256::ZERO); + //env.tx.base.gas_limit = 100; + //env.tx.source_hash = Some(B256::ZERO); + + let deposit = TxDeposit { + gas_limit: 100, + source_hash: B256::ZERO, + ..Default::default() + }; + env.tx = OpTransaction::Deposit(deposit); let gas = call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); @@ -494,8 +526,14 @@ mod tests { #[test] fn test_consume_gas_with_refund() { let mut env = EnvWiring::::default(); - env.tx.base.gas_limit = 100; - env.tx.source_hash = Some(B256::ZERO); + //env.tx.base.gas_limit = 100; + //env.tx.source_hash = Some(B256::ZERO); + let deposit = TxDeposit { + gas_limit: 100, + source_hash: B256::ZERO, + ..Default::default() + }; + env.tx = OpTransaction::Deposit(deposit); let mut ret_gas = Gas::new(90); ret_gas.record_refund(20); @@ -515,8 +553,15 @@ mod tests { #[test] fn test_consume_gas_sys_deposit_tx() { let mut env = EnvWiring::::default(); - env.tx.base.gas_limit = 100; - env.tx.source_hash = Some(B256::ZERO); + //env.tx.base.gas_limit = 100; + //env.tx.source_hash = Some(B256::ZERO); + + let deposit = TxDeposit { + gas_limit: 100, + source_hash: B256::ZERO, + ..Default::default() + }; + env.tx = OpTransaction::Deposit(deposit); let gas = call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 0); @@ -543,10 +588,18 @@ mod tests { l1_base_fee_scalar: U256::from(1_000), ..Default::default() }); - // Enveloped needs to be some but it will deduce zero fee. - context.evm.inner.env.tx.enveloped_tx = Some(bytes!("")); - // added mint value is 10. - context.evm.inner.env.tx.mint = Some(10); + // // Enveloped needs to be some but it will deduce zero fee. + // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("")); + // // added mint value is 10. + // context.evm.inner.env.tx.mint = Some(10); + + let deposit = TxDeposit { + gas_limit: 100, + mint: Some(10), + source_hash: B256::ZERO, + ..Default::default() + }; + context.evm.inner.env.tx = OpTransaction::Deposit(deposit); deduct_caller::(&mut context).unwrap(); @@ -578,13 +631,20 @@ mod tests { l1_base_fee_scalar: U256::from(1_000), ..Default::default() }); - // l1block cost is 1048 fee. - context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); - // added mint value is 10. - context.evm.inner.env.tx.mint = Some(10); - // Putting source_hash to some makes it a deposit transaction. - // so enveloped_tx gas cost is ignored. - context.evm.inner.env.tx.source_hash = Some(B256::ZERO); + // // l1block cost is 1048 fee. + // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); + // // added mint value is 10. + // context.evm.inner.env.tx.mint = Some(10); + // // Putting source_hash to some makes it a deposit transaction. + // // so enveloped_tx gas cost is ignored. + // context.evm.inner.env.tx.source_hash = Some(B256::ZERO); + + let deposit = TxDeposit { + mint: Some(10), + source_hash: B256::ZERO, + ..Default::default() + }; + context.evm.inner.env.tx = OpTransaction::Deposit(deposit); deduct_caller::(&mut context).unwrap(); @@ -617,7 +677,10 @@ mod tests { ..Default::default() }); // l1block cost is 1048 fee. - context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); + context.evm.inner.env.tx = OpTransaction::Base { + tx: TxEnv::default(), + enveloped_tx: Some(bytes!("FACADE")), + }; deduct_caller::(&mut context).unwrap(); // Check the account balance is updated. @@ -649,7 +712,10 @@ mod tests { ..Default::default() }); // l1block cost is 1048 fee. - context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); + context.evm.inner.env.tx = OpTransaction::Base { + tx: TxEnv::default(), + enveloped_tx: Some(bytes!("FACADE")), + }; assert_eq!( deduct_caller::(&mut context), @@ -666,12 +732,20 @@ mod tests { #[test] fn test_validate_sys_tx() { // mark the tx as a system transaction. - let mut env = EnvWiring::::default(); - env.tx.is_system_transaction = Some(true); + // Set source hash. + let tx = TxDeposit { + is_system_transaction: true, + ..Default::default() + }; + let env = Env::> { + tx: OpTransaction::Deposit(tx), + ..Default::default() + }; + assert_eq!( validate_env::(&env), Err(EVMError::Transaction( - OptimismInvalidTransaction::DepositSystemTxPostRegolith + OpTransactionError::DepositSystemTxPostRegolith )) ); @@ -682,16 +756,28 @@ mod tests { #[test] fn test_validate_deposit_tx() { // Set source hash. - let mut env = EnvWiring::::default(); - env.tx.source_hash = Some(B256::ZERO); + let tx = TxDeposit { + source_hash: B256::ZERO, + ..Default::default() + }; + let env = Env::> { + tx: OpTransaction::Deposit(tx), + ..Default::default() + }; assert!(validate_env::(&env).is_ok()); } #[test] fn test_validate_tx_against_state_deposit_tx() { // Set source hash. - let mut env = EnvWiring::::default(); - env.tx.source_hash = Some(B256::ZERO); + let tx = TxDeposit { + source_hash: B256::ZERO, + ..Default::default() + }; + let env = Env::> { + tx: OpTransaction::Deposit(tx), + ..Default::default() + }; // Nonce and balance checks should be skipped for deposit transactions. assert!(validate_env::(&env).is_ok()); diff --git a/crates/optimism/src/lib.rs b/crates/optimism/src/lib.rs index 3b602b6297..faf0d1edf3 100644 --- a/crates/optimism/src/lib.rs +++ b/crates/optimism/src/lib.rs @@ -5,13 +5,14 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; -mod bn128; -mod env; -mod fast_lz; -mod handler_register; -mod l1block; -mod result; -mod spec; +pub mod bn128; +pub mod fast_lz; +pub mod handler_register; +pub mod l1block; +pub mod result; +pub mod spec; +pub mod transaction; +pub mod wiring; pub use handler_register::{ deduct_caller, end, last_frame_return, load_accounts, load_precompiles, @@ -19,68 +20,6 @@ pub use handler_register::{ validate_tx_against_state, }; pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; -pub use result::{OptimismHaltReason, OptimismInvalidTransaction}; -use revm::{ - primitives::{Bytes, B256}, - wiring::TransactionValidation, -}; +pub use result::OptimismHaltReason; pub use spec::*; - -pub trait OptimismContext { - /// A reference to the cached L1 block info. - fn l1_block_info(&self) -> Option<&L1BlockInfo>; - - /// A mutable reference to the cached L1 block info. - fn l1_block_info_mut(&mut self) -> &mut Option; -} - -/// Trait for an Optimism transaction. -pub trait OptimismTransaction { - /// The source hash is used to make sure that deposit transactions do - /// not have identical hashes. - /// - /// L1 originated deposit transaction source hashes are computed using - /// the hash of the l1 block hash and the l1 log index. - /// L1 attributes deposit source hashes are computed with the l1 block - /// hash and the sequence number = l2 block number - l2 epoch start - /// block number. - /// - /// These two deposit transaction sources specify a domain in the outer - /// hash so there are no collisions. - fn source_hash(&self) -> Option<&B256>; - /// The amount to increase the balance of the `from` account as part of - /// a deposit transaction. This is unconditional and is applied to the - /// `from` account even if the deposit transaction fails since - /// the deposit is pre-paid on L1. - fn mint(&self) -> Option<&u128>; - /// Whether or not the transaction is a system transaction. - fn is_system_transaction(&self) -> Option; - /// An enveloped EIP-2718 typed transaction. This is used - /// to compute the L1 tx cost using the L1 block info, as - /// opposed to requiring downstream apps to compute the cost - /// externally. - fn enveloped_tx(&self) -> Option; -} - -/// Trait for an Optimism chain spec. -pub trait OptimismWiring: - revm::EvmWiring< - ChainContext: OptimismContext, - Hardfork = OptimismSpecId, - HaltReason = OptimismHaltReason, - Transaction: OptimismTransaction - + TransactionValidation, -> -{ -} - -impl OptimismWiring for EvmWiringT where - EvmWiringT: revm::EvmWiring< - ChainContext: OptimismContext, - Hardfork = OptimismSpecId, - HaltReason = OptimismHaltReason, - Transaction: OptimismTransaction - + TransactionValidation, - > -{ -} +pub use transaction::{error::OpTransactionError, OpTransaction, OpTransactionType}; diff --git a/crates/optimism/src/result.rs b/crates/optimism/src/result.rs index 80f724943b..7b3103f47c 100644 --- a/crates/optimism/src/result.rs +++ b/crates/optimism/src/result.rs @@ -1,76 +1,4 @@ -use core::fmt::Display; -use revm::wiring::result::{EVMError, HaltReason, InvalidTransaction}; - -/// Optimism transaction validation error. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum OptimismInvalidTransaction { - Base(InvalidTransaction), - /// System transactions are not supported post-regolith hardfork. - /// - /// Before the Regolith hardfork, there was a special field in the `Deposit` transaction - /// type that differentiated between `system` and `user` deposit transactions. This field - /// was deprecated in the Regolith hardfork, and this error is thrown if a `Deposit` transaction - /// is found with this field set to `true` after the hardfork activation. - /// - /// In addition, this error is internal, and bubbles up into a [OptimismHaltReason::FailedDeposit] error - /// in the `revm` handler for the consumer to easily handle. This is due to a state transition - /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction - /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and - /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors - /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this - /// case for failed deposit transactions. - DepositSystemTxPostRegolith, - /// Deposit transaction haults bubble up to the global main return handler, wiping state and - /// only increasing the nonce + persisting the mint value. - /// - /// This is a catch-all error for any deposit transaction that is results in a [HaltReason] error - /// post-regolith hardfork. This allows for a consumer to easily handle special cases where - /// a deposit transaction fails during validation, but must still be included in the block. - /// - /// In addition, this error is internal, and bubbles up into a [OptimismHaltReason::FailedDeposit] error - /// in the `revm` handler for the consumer to easily handle. This is due to a state transition - /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction - /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and - /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors - /// are cause for non-inclusion, so a special [HaltReason] variant was introduced to handle this - /// case for failed deposit transactions. - HaltedDepositPostRegolith, -} - -impl Display for OptimismInvalidTransaction { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Base(error) => error.fmt(f), - Self::DepositSystemTxPostRegolith => { - write!( - f, - "deposit system transactions post regolith hardfork are not supported" - ) - } - Self::HaltedDepositPostRegolith => { - write!( - f, - "deposit transaction halted post-regolith; error will be bubbled up to main return handler" - ) - } - } - } -} - -impl core::error::Error for OptimismInvalidTransaction {} - -impl From for OptimismInvalidTransaction { - fn from(value: InvalidTransaction) -> Self { - Self::Base(value) - } -} - -impl From for EVMError { - fn from(value: OptimismInvalidTransaction) -> Self { - Self::Transaction(value) - } -} +use revm::wiring::result::HaltReason; #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/optimism/src/spec.rs b/crates/optimism/src/spec.rs index a8be1a6690..6e05af241b 100644 --- a/crates/optimism/src/spec.rs +++ b/crates/optimism/src/spec.rs @@ -1,61 +1,8 @@ -use crate::{ - env::TxEnv, optimism_handle_register, L1BlockInfo, OptimismContext, OptimismHaltReason, -}; -use core::marker::PhantomData; use revm::{ - database_interface::Database, - handler::register::HandleRegisters, precompile::PrecompileSpecId, specification::hardfork::{Spec, SpecId}, - wiring::default::block::BlockEnv, - wiring::EvmWiring, - EvmHandler, }; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct OptimismEvmWiring { - _phantom: PhantomData<(DB, EXT)>, -} - -impl EvmWiring for OptimismEvmWiring { - type Block = BlockEnv; - type Database = DB; - type ChainContext = Context; - type ExternalContext = EXT; - type Hardfork = OptimismSpecId; - type HaltReason = OptimismHaltReason; - type Transaction = TxEnv; -} - -impl revm::EvmWiring for OptimismEvmWiring { - fn handler<'evm>(hardfork: Self::Hardfork) -> EvmHandler<'evm, Self> - where - DB: Database, - { - let mut handler = EvmHandler::mainnet_with_spec(hardfork); - - handler.append_handler_register(HandleRegisters::Plain(optimism_handle_register::)); - - handler - } -} - -/// Context for the Optimism chain. -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct Context { - l1_block_info: Option, -} - -impl OptimismContext for Context { - fn l1_block_info(&self) -> Option<&L1BlockInfo> { - self.l1_block_info.as_ref() - } - - fn l1_block_info_mut(&mut self) -> &mut Option { - &mut self.l1_block_info - } -} - /// Specification IDs for the optimism blockchain. #[repr(u8)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, enumn::N)] diff --git a/crates/optimism/src/transaction.rs b/crates/optimism/src/transaction.rs new file mode 100644 index 0000000000..6261ce4568 --- /dev/null +++ b/crates/optimism/src/transaction.rs @@ -0,0 +1,5 @@ +pub mod abstraction; +pub mod deposit; +pub mod error; + +pub use abstraction::{OpTransaction, OpTransactionType, OpTxTrait}; diff --git a/crates/optimism/src/transaction/abstraction.rs b/crates/optimism/src/transaction/abstraction.rs new file mode 100644 index 0000000000..b41997db92 --- /dev/null +++ b/crates/optimism/src/transaction/abstraction.rs @@ -0,0 +1,162 @@ +use super::deposit::{DepositTransaction, TxDeposit}; +use crate::OpTransactionError; +use revm::{ + primitives::Bytes, + transaction::{CommonTxFields, Transaction, TransactionType}, + wiring::default::TxEnv, +}; + +pub trait OpTxTrait: Transaction { + type DepositTx: DepositTransaction; + + fn deposit(&self) -> &Self::DepositTx; + + fn enveloped_tx(&self) -> Option<&Bytes>; +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum OpTransactionType { + /// Base transaction type supported on Ethereum mainnet. + Base(TransactionType), + /// Optimism-specific deposit transaction type. + Deposit, +} + +impl From for TransactionType { + fn from(tx_type: OpTransactionType) -> Self { + match tx_type { + OpTransactionType::Base(tx_type) => tx_type, + OpTransactionType::Deposit => TransactionType::Custom, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum OpTransaction { + Base { + tx: T, + /// An enveloped EIP-2718 typed transaction. This is used + /// to compute the L1 tx cost using the L1 block info, as + /// opposed to requiring downstream apps to compute the cost + /// externally. + enveloped_tx: Option, + }, + Deposit(TxDeposit), +} + +impl Default for OpTransaction { + fn default() -> Self { + Self::Base { + tx: TxEnv::default(), + enveloped_tx: None, + } + } +} + +impl Transaction for OpTransaction { + // TODO + type TransactionError = OpTransactionError; + type TransactionType = OpTransactionType; + + type AccessList = T::AccessList; + + type Legacy = T::Legacy; + + type Eip2930 = T::Eip2930; + + type Eip1559 = T::Eip1559; + + type Eip4844 = T::Eip4844; + + type Eip7702 = T::Eip7702; + + fn tx_type(&self) -> Self::TransactionType { + match self { + Self::Base { tx, .. } => OpTransactionType::Base(tx.tx_type().into()), + Self::Deposit(_) => OpTransactionType::Deposit, + } + } + + fn common_fields(&self) -> &dyn CommonTxFields { + match self { + Self::Base { tx, .. } => tx.common_fields(), + Self::Deposit(deposit) => deposit, + } + } + + fn kind(&self) -> revm::primitives::TxKind { + match self { + Self::Base { tx, .. } => tx.kind(), + Self::Deposit(deposit) => deposit.to, + } + } + + fn effective_gas_price(&self, base_fee: revm::primitives::U256) -> revm::primitives::U256 { + match self { + Self::Base { tx, .. } => tx.effective_gas_price(base_fee), + Self::Deposit(_) => base_fee, + } + } + + fn max_fee(&self) -> u128 { + match self { + Self::Base { tx, .. } => tx.max_fee(), + Self::Deposit(_) => 0, + } + } + + fn legacy(&self) -> &Self::Legacy { + let Self::Base { tx, .. } = self else { + panic!("Not a legacy transaction") + }; + tx.legacy() + } + + fn eip2930(&self) -> &Self::Eip2930 { + let Self::Base { tx, .. } = self else { + panic!("Not eip2930 transaction") + }; + tx.eip2930() + } + + fn eip1559(&self) -> &Self::Eip1559 { + let Self::Base { tx, .. } = self else { + panic!("Not a eip1559 transaction") + }; + tx.eip1559() + } + + fn eip4844(&self) -> &Self::Eip4844 { + let Self::Base { tx, .. } = self else { + panic!("Not a eip4844 transaction") + }; + tx.eip4844() + } + + fn eip7702(&self) -> &Self::Eip7702 { + let Self::Base { tx, .. } = self else { + panic!("Not a eip7702 transaction") + }; + tx.eip7702() + } +} + +impl OpTxTrait for OpTransaction { + type DepositTx = TxDeposit; + + fn deposit(&self) -> &Self::DepositTx { + match self { + Self::Base { .. } => panic!("Not a deposit transaction"), + Self::Deposit(deposit) => deposit, + } + } + + fn enveloped_tx(&self) -> Option<&Bytes> { + match self { + Self::Base { enveloped_tx, .. } => enveloped_tx.as_ref(), + Self::Deposit(_) => None, + } + } +} diff --git a/crates/optimism/src/transaction/deposit.rs b/crates/optimism/src/transaction/deposit.rs new file mode 100644 index 0000000000..41c71ce395 --- /dev/null +++ b/crates/optimism/src/transaction/deposit.rs @@ -0,0 +1,77 @@ +use revm::{ + primitives::{Address, Bytes, TxKind, B256, U256}, + transaction::CommonTxFields, +}; + +pub trait DepositTransaction: CommonTxFields { + fn source_hash(&self) -> B256; + + fn to(&self) -> TxKind; + + fn mint(&self) -> Option; + + fn is_system_transaction(&self) -> bool; +} + +#[derive(Clone, Default, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TxDeposit { + /// Hash that uniquely identifies the source of the deposit. + pub source_hash: B256, + /// The address of the sender account. + pub from: Address, + /// The address of the recipient account, or the null (zero-length) address if the deposited + /// transaction is a contract creation. + pub to: TxKind, + /// The ETH value to mint on L2. + pub mint: Option, + /// The ETH value to send to the recipient account. + pub value: U256, + /// The gas limit for the L2 transaction. + pub gas_limit: u64, + /// Field indicating if this transaction is exempt from the L2 gas limit. + pub is_system_transaction: bool, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). + pub input: Bytes, +} + +impl CommonTxFields for TxDeposit { + fn caller(&self) -> Address { + self.from + } + + fn gas_limit(&self) -> u64 { + self.gas_limit + } + + fn value(&self) -> U256 { + self.value + } + + fn input(&self) -> &Bytes { + &self.input + } + + fn nonce(&self) -> u64 { + panic!("There is no nonce in a deposit transaction"); + } +} + +impl DepositTransaction for TxDeposit { + fn source_hash(&self) -> B256 { + self.source_hash + } + + fn to(&self) -> TxKind { + self.to + } + + fn mint(&self) -> Option { + self.mint + } + + fn is_system_transaction(&self) -> bool { + self.is_system_transaction + } +} diff --git a/crates/optimism/src/transaction/error.rs b/crates/optimism/src/transaction/error.rs new file mode 100644 index 0000000000..6cd8144bb4 --- /dev/null +++ b/crates/optimism/src/transaction/error.rs @@ -0,0 +1,78 @@ +use core::fmt::Display; +use revm::{ + transaction::TransactionError, + wiring::result::{EVMError, InvalidTransaction}, +}; + +/// Optimism transaction validation error. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum OpTransactionError { + Base(InvalidTransaction), + /// System transactions are not supported post-regolith hardfork. + /// + /// Before the Regolith hardfork, there was a special field in the `Deposit` transaction + /// type that differentiated between `system` and `user` deposit transactions. This field + /// was deprecated in the Regolith hardfork, and this error is thrown if a `Deposit` transaction + /// is found with this field set to `true` after the hardfork activation. + /// + /// In addition, this error is internal, and bubbles up into a [crate::OptimismHaltReason::FailedDeposit] error + /// in the `revm` handler for the consumer to easily handle. This is due to a state transition + /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction + /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and + /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors + /// are cause for non-inclusion, so a special [crate::OptimismHaltReason] variant was introduced to handle this + /// case for failed deposit transactions. + DepositSystemTxPostRegolith, + /// Deposit transaction haults bubble up to the global main return handler, wiping state and + /// only increasing the nonce + persisting the mint value. + /// + /// This is a catch-all error for any deposit transaction that is results in a [crate::OptimismHaltReason] error + /// post-regolith hardfork. This allows for a consumer to easily handle special cases where + /// a deposit transaction fails during validation, but must still be included in the block. + /// + /// In addition, this error is internal, and bubbles up into a [crate::OptimismHaltReason::FailedDeposit] error + /// in the `revm` handler for the consumer to easily handle. This is due to a state transition + /// rule on OP Stack chains where, if for any reason a deposit transaction fails, the transaction + /// must still be included in the block, the sender nonce is bumped, the `mint` value persists, and + /// special gas accounting rules are applied. Normally on L1, [EVMError::Transaction] errors + /// are cause for non-inclusion, so a special [crate::OptimismHaltReason] variant was introduced to handle this + /// case for failed deposit transactions. + HaltedDepositPostRegolith, +} + +impl TransactionError for OpTransactionError {} + +impl Display for OpTransactionError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Base(error) => error.fmt(f), + Self::DepositSystemTxPostRegolith => { + write!( + f, + "deposit system transactions post regolith hardfork are not supported" + ) + } + Self::HaltedDepositPostRegolith => { + write!( + f, + "deposit transaction halted post-regolith; error will be bubbled up to main return handler" + ) + } + } + } +} + +impl core::error::Error for OpTransactionError {} + +impl From for OpTransactionError { + fn from(value: InvalidTransaction) -> Self { + Self::Base(value) + } +} + +impl From for EVMError { + fn from(value: OpTransactionError) -> Self { + Self::Transaction(value) + } +} diff --git a/crates/optimism/src/wiring.rs b/crates/optimism/src/wiring.rs new file mode 100644 index 0000000000..da19e42dd6 --- /dev/null +++ b/crates/optimism/src/wiring.rs @@ -0,0 +1,92 @@ +use crate::{ + optimism_handle_register, + transaction::{OpTransaction, OpTransactionType, OpTxTrait}, + L1BlockInfo, OpTransactionError, OptimismHaltReason, OptimismSpecId, +}; +use core::marker::PhantomData; +use revm::{ + database_interface::Database, + handler::register::HandleRegisters, + wiring::default::{block::BlockEnv, TxEnv}, + wiring::EvmWiring, + EvmHandler, +}; + +pub trait OptimismContextTrait { + /// A reference to the cached L1 block info. + fn l1_block_info(&self) -> Option<&L1BlockInfo>; + + /// A mutable reference to the cached L1 block info. + fn l1_block_info_mut(&mut self) -> &mut Option; +} + +/// Trait for an Optimism chain spec. +pub trait OptimismWiring: + revm::EvmWiring< + ChainContext: OptimismContextTrait, + Hardfork = OptimismSpecId, + HaltReason = OptimismHaltReason, + Transaction: OpTxTrait< + TransactionType = OpTransactionType, + TransactionError = OpTransactionError, + >, +> +{ +} + +impl OptimismWiring for EvmWiringT where + EvmWiringT: revm::EvmWiring< + ChainContext: OptimismContextTrait, + Hardfork = OptimismSpecId, + HaltReason = OptimismHaltReason, + Transaction: OpTxTrait< + TransactionType = OpTransactionType, + TransactionError = OpTransactionError, + >, + > +{ +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct OptimismEvmWiring { + _phantom: PhantomData<(DB, EXT)>, +} + +impl EvmWiring for OptimismEvmWiring { + type Block = BlockEnv; + type Database = DB; + type ChainContext = Context; + type ExternalContext = EXT; + type Hardfork = OptimismSpecId; + type HaltReason = OptimismHaltReason; + type Transaction = OpTransaction; +} + +impl revm::EvmWiring for OptimismEvmWiring { + fn handler<'evm>(hardfork: Self::Hardfork) -> EvmHandler<'evm, Self> + where + DB: Database, + { + let mut handler = EvmHandler::mainnet_with_spec(hardfork); + + handler.append_handler_register(HandleRegisters::Plain(optimism_handle_register::)); + + handler + } +} + +/// Context for the Optimism chain. +#[derive(Clone, Default, Debug, PartialEq, Eq)] +pub struct Context { + l1_block_info: Option, +} + +impl OptimismContextTrait for Context { + fn l1_block_info(&self) -> Option<&L1BlockInfo> { + self.l1_block_info.as_ref() + } + + fn l1_block_info_mut(&mut self) -> &mut Option { + &mut self.l1_block_info + } +} diff --git a/crates/precompile/benches/bench.rs b/crates/precompile/benches/bench.rs index c480862c2d..07c603c2c0 100644 --- a/crates/precompile/benches/bench.rs +++ b/crates/precompile/benches/bench.rs @@ -1,5 +1,5 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use primitives::{hex, keccak256, U256, VERSIONED_HASH_VERSION_KZG}; +use primitives::{hex, keccak256, U256}; use revm_precompile::{ bn128::{ add::ISTANBUL_ADD_GAS_COST, @@ -12,6 +12,7 @@ use revm_precompile::{ }; use secp256k1::{Message, SecretKey, SECP256K1}; use sha2::{Digest, Sha256}; +use specification::eip4844::VERSIONED_HASH_VERSION_KZG; use wiring::default::CfgEnv; /// Benchmarks different cryptography-related precompiles. diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 1e771754a1..b20a8f9783 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -30,8 +30,9 @@ alloy-primitives = { version = "0.8.5", default-features = false, features = [ [features] default = ["std"] std = ["alloy-primitives/std"] -hashbrown = ["alloy-primitives/map-hashbrown"] serde = ["alloy-primitives/serde"] + +hashbrown = ["alloy-primitives/map-hashbrown"] arbitrary = ["std", "alloy-primitives/arbitrary"] asm-keccak = ["alloy-primitives/asm-keccak"] rand = ["alloy-primitives/rand"] diff --git a/crates/primitives/src/constants.rs b/crates/primitives/src/constants.rs index e2af7499de..05170261aa 100644 --- a/crates/primitives/src/constants.rs +++ b/crates/primitives/src/constants.rs @@ -1,10 +1,5 @@ use alloy_primitives::{address, Address}; -/// EIP-170: Contract code size limit -/// -/// By default the limit is `0x6000` (~25kb) -pub const MAX_CODE_SIZE: usize = 0x6000; - /// Number of block hashes that EVM can access in the past (pre-Prague). pub const BLOCK_HASH_HISTORY: u64 = 256; @@ -26,37 +21,6 @@ pub const BLOCKHASH_SERVE_WINDOW: usize = 8192; /// This is named `HISTORY_STORAGE_ADDRESS` in the EIP. pub const BLOCKHASH_STORAGE_ADDRESS: Address = address!("25a219378dad9b3503c8268c9ca836a52427a4fb"); -/// EIP-3860: Limit and meter initcode -/// -/// Limit of maximum initcode size is `2 * MAX_CODE_SIZE`. -pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; - /// The address of precompile 3, which is handled specially in a few places. pub const PRECOMPILE3: Address = Address::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]); - -// === EIP-4844 constants === - -/// Gas consumption of a single data blob (== blob byte size). -pub const GAS_PER_BLOB: u64 = 1 << 17; - -/// Target number of the blob per block. -pub const TARGET_BLOB_NUMBER_PER_BLOCK: u64 = 3; - -/// Max number of blobs per block -pub const MAX_BLOB_NUMBER_PER_BLOCK: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK; - -/// Maximum consumable blob gas for data blobs per block. -pub const MAX_BLOB_GAS_PER_BLOCK: u64 = MAX_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; - -/// Target consumable blob gas for data blobs per block (for 1559-like pricing). -pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = TARGET_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; - -/// Minimum gas price for data blobs. -pub const MIN_BLOB_GASPRICE: u64 = 1; - -/// Controls the maximum rate of change for blob gas price. -pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477; - -/// First version of the blob. -pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 6dc901f6c4..7c832a4e32 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -32,6 +32,7 @@ state.workspace = true specification.workspace = true bytecode.workspace = true database = { workspace = true, optional = true } +transaction.workspace = true # misc derive-where = { version = "1.2.7", default-features = false } @@ -59,13 +60,14 @@ alloy-provider = "0.4.2" [features] default = ["std", "c-kzg", "secp256k1", "portable", "blst"] -std = [ - "serde?/std", - "interpreter/std", - "precompile/std", -] +std = ["serde?/std", "interpreter/std", "precompile/std"] hashbrown = ["interpreter/hashbrown", "precompile/hashbrown"] -serde = ["dep:serde", "interpreter/serde", "database-interface/serde"] +serde = [ + "dep:serde", + "interpreter/serde", + "database-interface/serde", + "primitives/serde", +] arbitrary = ["primitives/arbitrary"] asm-keccak = ["primitives/asm-keccak"] portable = ["wiring/portable"] @@ -93,28 +95,3 @@ c-kzg = ["precompile/c-kzg"] # `kzg-rs` is not audited but useful for `no_std` environment, use it with causing and default to `c-kzg` if possible. kzg-rs = ["precompile/kzg-rs"] blst = ["precompile/blst"] - -# [[example]] -# name = "fork_ref_transact" -# path = "../../examples/fork_ref_transact.rs" -# required-features = ["alloydb"] - -# [[example]] -# name = "generate_block_traces" -# path = "../../examples/generate_block_traces.rs" -# required-features = ["std", "serde-json", "alloydb"] - -# [[example]] -# name = "db_by_ref" -# path = "../../examples/db_by_ref.rs" -# required-features = ["std", "serde-json"] - -# [[example]] -# name = "deploy" -# path = "../../examples/deploy.rs" -# required-features = [] - -# [[bench]] -# name = "bench" -# path = "benches/bench.rs" -# harness = false diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 9630ae345e..721f40f03f 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -2,10 +2,10 @@ use crate::{handler::register, Context, Evm, EvmContext, EvmWiring, Handler}; use core::marker::PhantomData; use database_interface::EmptyDB; use std::boxed::Box; +use transaction::Transaction; use wiring::{ default::{CfgEnv, EnvWiring}, result::InvalidTransaction, - transaction::TransactionValidation, EthereumWiring, }; @@ -130,8 +130,7 @@ impl<'a, EvmWiringT: EvmWiring> EvmBuilder<'a, SetGenericStage, EvmWiringT> { impl<'a, EvmWiringT> EvmBuilder<'a, SetGenericStage, EvmWiringT> where - EvmWiringT: - EvmWiring>>, + EvmWiringT: EvmWiring>>, { /// Creates the default [EvmWiring]::[crate::Database] that will be used by [`Evm`]. pub fn with_default_db(mut self) -> EvmBuilder<'a, SetGenericStage, EvmWiringT> @@ -441,8 +440,7 @@ where impl<'a, BuilderStage, EvmWiringT: EvmWiring> EvmBuilder<'a, BuilderStage, EvmWiringT> where - EvmWiringT: - EvmWiring>>, + EvmWiringT: EvmWiring>>, { /// Sets specification Id , that will mark the version of EVM. /// It represent the hard fork of ethereum. @@ -495,12 +493,14 @@ mod test { .with_default_db() .with_default_ext_ctx() .modify_db(|db| { - db.insert_account_info(to_addr, AccountInfo::new(U256::ZERO, 0, code_hash, code)) + db.insert_account_info( + to_addr, + AccountInfo::new(U256::from(1_000_000), 0, code_hash, code), + ) }) .modify_tx_env(|tx| { - let transact_to = &mut tx.transact_to; - - *transact_to = TxKind::Call(to_addr) + tx.transact_to = TxKind::Call(to_addr); + tx.gas_limit = 100_000; }) // we need to use handle register box to capture the custom context in the handle // register diff --git a/crates/revm/src/context/evm_context.rs b/crates/revm/src/context/evm_context.rs index cac7ad4e2a..ce60aa07e7 100644 --- a/crates/revm/src/context/evm_context.rs +++ b/crates/revm/src/context/evm_context.rs @@ -387,9 +387,10 @@ where } // Use nonce from tx to calculate address. - let nonce = self.env.tx.nonce(); + let tx = self.env.tx.common_fields(); + let create_address = tx.caller().create(tx.nonce()); - (input, eof, Some(self.env.tx.caller().create(nonce))) + (input, eof, Some(create_address)) } }; diff --git a/crates/revm/src/context/inner_evm_context.rs b/crates/revm/src/context/inner_evm_context.rs index 259d608255..ae778e1947 100644 --- a/crates/revm/src/context/inner_evm_context.rs +++ b/crates/revm/src/context/inner_evm_context.rs @@ -1,22 +1,19 @@ -use derive_where::derive_where; - use crate::{journaled_state::JournaledState, JournalCheckpoint}; use bytecode::{Bytecode, Eof, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; use database_interface::Database; +use derive_where::derive_where; use interpreter::{ gas, return_ok, AccountLoad, Eip7702CodeLoad, InstructionResult, InterpreterResult, SStoreResult, SelfDestructResult, StateLoad, }; use primitives::{Address, Bytes, HashSet, B256, U256}; -use specification::{ - eip2930::AccessListItem, - hardfork::{ - Spec, - SpecId::{self, *}, - }, +use specification::hardfork::{ + Spec, + SpecId::{self, *}, }; use state::Account; use std::{boxed::Box, sync::Arc}; +use transaction::AccessListTrait; use wiring::{ default::{AnalysisKind, CfgEnv, EnvWiring}, EvmWiring, Transaction, @@ -96,14 +93,14 @@ impl InnerEvmContext { /// Loading of accounts/storages is needed to make them warm. #[inline] pub fn load_access_list(&mut self) -> Result<(), ::Error> { - for AccessListItem { - address, - storage_keys, - } in self.env.tx.access_list() - { + let Some(access_list) = self.env.tx.access_list() else { + return Ok(()); + }; + + for access_list in access_list.iter() { self.journaled_state.initial_account_load( - *address, - storage_keys.iter().map(|i| U256::from_be_bytes(i.0)), + access_list.0, + access_list.1.map(|i| U256::from_be_bytes(i.0)), &mut self.db, )?; } diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 1401134065..0970e6d2b2 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -4,14 +4,9 @@ use crate::{ Context, ContextWithEvmWiring, EvmContext, EvmWiring, Frame, FrameOrResult, FrameResult, InnerEvmContext, }; -use bytecode::EOF_MAGIC_BYTES; use core::fmt::{self, Debug}; use database_interface::{Database, DatabaseCommit}; -use interpreter::{ - CallInputs, CreateInputs, EOFCreateInputs, Host, InterpreterAction, SharedMemory, -}; -use primitives::TxKind; -use specification::hardfork::SpecId; +use interpreter::{Host, InterpreterAction, NewFrameAction, SharedMemory}; use std::{boxed::Box, vec::Vec}; use wiring::{ default::{CfgEnv, EnvWiring}, @@ -130,9 +125,13 @@ impl<'a, EvmWiringT: EvmWiring> Evm<'a, EvmWiringT> { let exec = &mut self.handler.execution; let frame_or_result = match next_action { - InterpreterAction::Call { inputs } => exec.call(&mut self.context, inputs)?, - InterpreterAction::Create { inputs } => exec.create(&mut self.context, inputs)?, - InterpreterAction::EOFCreate { inputs } => { + InterpreterAction::NewFrame(NewFrameAction::Call(inputs)) => { + exec.call(&mut self.context, inputs)? + } + InterpreterAction::NewFrame(NewFrameAction::Create(inputs)) => { + exec.create(&mut self.context, inputs)? + } + InterpreterAction::NewFrame(NewFrameAction::EOFCreate(inputs)) => { exec.eofcreate(&mut self.context, inputs)? } InterpreterAction::Return { result } => { @@ -350,7 +349,6 @@ impl Evm<'_, EvmWiringT> { /// Transact pre-verified transaction. fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { - let spec_id = self.spec_id(); let ctx = &mut self.context; let pre_exec = self.handler.pre_execution(); @@ -364,41 +362,25 @@ impl Evm<'_, EvmWiringT> { // deduce caller balance with its limit. pre_exec.deduct_caller(ctx)?; - let gas_limit = ctx.evm.env.tx.gas_limit() - initial_gas_spend; + let gas_limit = ctx.evm.env.tx.common_fields().gas_limit() - initial_gas_spend; // apply EIP-7702 auth list. let eip7702_gas_refund = pre_exec.apply_eip7702_auth_list(ctx)? as i64; + // start execution let exec = self.handler.execution(); - // call inner handling of call/create - let first_frame_or_result = match ctx.evm.env.tx.kind() { - TxKind::Call(_) => exec.call( - ctx, - CallInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), - )?, - TxKind::Create => { - // if first byte of data is magic 0xEF00, then it is EOFCreate. - if Into::::into(spec_id).is_enabled_in(SpecId::PRAGUE_EOF) - && ctx.env().tx.data().starts_with(&EOF_MAGIC_BYTES) - { - exec.eofcreate( - ctx, - Box::new(EOFCreateInputs::new_tx::( - &ctx.evm.env.tx, - gas_limit, - )), - )? - } else { - // Safe to unwrap because we are sure that it is create tx. - exec.create( - ctx, - CreateInputs::new_boxed(&ctx.evm.env.tx, gas_limit).unwrap(), - )? - } - } + + // create first frame action + let first_frame_action = exec.first_frame_creation(ctx, gas_limit)?; + + // call handler to create first frame. + let first_frame_or_result = match first_frame_action { + NewFrameAction::Call(inputs) => exec.call(ctx, inputs)?, + NewFrameAction::Create(inputs) => exec.create(ctx, inputs)?, + NewFrameAction::EOFCreate(inputs) => exec.eofcreate(ctx, inputs)?, }; - // Starts the main running loop. + // Starts the main running loop or return the result. let mut result = match first_frame_or_result { FrameOrResult::Frame(first_frame) => self.run_the_loop(first_frame)?, FrameOrResult::Result(result) => result, @@ -432,8 +414,12 @@ mod tests { Bytecode, }; use database::BenchmarkDB; - use primitives::{address, U256}; - use specification::eip7702::{Authorization, RecoveredAuthorization, Signature}; + use primitives::{address, TxKind, U256}; + use specification::{ + eip7702::{Authorization, RecoveredAuthorization, Signature}, + hardfork::SpecId, + }; + use transaction::TransactionType; use wiring::EthereumWiring; #[test] @@ -449,18 +435,18 @@ mod tests { .with_db(BenchmarkDB::new_bytecode(bytecode)) .with_default_ext_ctx() .modify_tx_env(|tx| { - tx.authorization_list = Some( - vec![RecoveredAuthorization::new_unchecked( - Authorization { - chain_id: U256::from(1), - address: delegate, - nonce: 0, - } - .into_signed(Signature::test_signature()), - Some(auth), - )] - .into(), - ); + tx.tx_type = TransactionType::Eip7702; + tx.gas_limit = 100_000; + tx.authorization_list = vec![RecoveredAuthorization::new_unchecked( + Authorization { + chain_id: U256::from(1), + address: delegate, + nonce: 0, + } + .into_signed(Signature::test_signature()), + Some(auth), + )] + .into(); tx.caller = caller; tx.transact_to = TxKind::Call(auth); }) diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index ed810c0ff7..4a012d8085 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -16,7 +16,7 @@ use specification::spec_to_generic; use std::vec::Vec; use wiring::{ result::{EVMResultGeneric, InvalidTransaction}, - transaction::TransactionValidation, + Transaction, }; use self::register::{HandleRegister, HandleRegisterBox}; @@ -43,8 +43,7 @@ pub struct Handler<'a, EvmWiringT: EvmWiring, H: Host + 'a> { impl<'a, EvmWiringT> EvmHandler<'a, EvmWiringT> where - EvmWiringT: - EvmWiring>>, + EvmWiringT: EvmWiring>>, { /// Creates a base/vanilla Ethereum handler with the provided spec id. pub fn mainnet_with_spec(spec_id: EvmWiringT::Hardfork) -> Self { diff --git a/crates/revm/src/handler/handle_types/execution.rs b/crates/revm/src/handler/handle_types/execution.rs index c6c512462c..f898f5a932 100644 --- a/crates/revm/src/handler/handle_types/execution.rs +++ b/crates/revm/src/handler/handle_types/execution.rs @@ -4,12 +4,16 @@ use crate::{ }; use interpreter::{ table::InstructionTables, CallInputs, CallOutcome, CreateInputs, CreateOutcome, - EOFCreateInputs, InterpreterAction, InterpreterResult, SharedMemory, + EOFCreateInputs, InterpreterAction, InterpreterResult, NewFrameAction, SharedMemory, }; use specification::hardfork::Spec; use std::{boxed::Box, sync::Arc}; use wiring::result::EVMResultGeneric; +/// Handles creation of first frame +pub type FirstFrameCreation<'a, EvmWiringT> = + Arc, u64) -> EVMResultGeneric + 'a>; + /// Handles first frame return handle. pub type LastFrameReturnHandle<'a, EvmWiringT> = Arc< dyn Fn(&mut Context, &mut FrameResult) -> EVMResultGeneric<(), EvmWiringT> + 'a, @@ -105,6 +109,9 @@ pub type InsertEOFCreateOutcomeHandle<'a, EvmWiringT> = Arc< /// Handles related to stack frames. pub struct ExecutionHandler<'a, EvmWiringT: EvmWiring> { + /// Handler that created first frame action. It uses transaction + /// to determine if it is a call or create or EOF create. + pub first_frame_creation: FirstFrameCreation<'a, EvmWiringT>, /// Handles last frame return, modified gas for refund and /// sets tx gas limit. pub last_frame_return: LastFrameReturnHandle<'a, EvmWiringT>, @@ -134,6 +141,7 @@ impl<'a, EvmWiringT: EvmWiring + 'a> ExecutionHandler<'a, EvmWiringT> { /// Creates mainnet ExecutionHandler. pub fn new() -> Self { Self { + first_frame_creation: Arc::new(mainnet::first_frame_creation::), last_frame_return: Arc::new(mainnet::last_frame_return::), execute_frame: Arc::new(mainnet::execute_frame::), call: Arc::new(mainnet::call::), @@ -162,6 +170,16 @@ impl<'a, EvmWiringT: EvmWiring> ExecutionHandler<'a, EvmWiringT> { (self.execute_frame)(frame, shared_memory, instruction_tables, context) } + /// Handle call return, depending on instruction result gas will be reimbursed or not. + #[inline] + pub fn first_frame_creation( + &self, + context: &mut Context, + gas_limit: u64, + ) -> EVMResultGeneric { + (self.first_frame_creation)(context, gas_limit) + } + /// Handle call return, depending on instruction result gas will be reimbursed or not. #[inline] pub fn last_frame_return( diff --git a/crates/revm/src/handler/handle_types/validation.rs b/crates/revm/src/handler/handle_types/validation.rs index 90239cede0..5dd90fbb72 100644 --- a/crates/revm/src/handler/handle_types/validation.rs +++ b/crates/revm/src/handler/handle_types/validation.rs @@ -1,10 +1,10 @@ use crate::{handler::mainnet, Context, EvmWiring}; use specification::hardfork::Spec; use std::sync::Arc; +use transaction::Transaction; use wiring::{ default::EnvWiring, result::{EVMResultGeneric, InvalidTransaction}, - transaction::TransactionValidation, }; /// Handle that validates env. @@ -32,7 +32,7 @@ pub struct ValidationHandler<'a, EvmWiringT: EvmWiring> { impl<'a, EvmWiringT: EvmWiring + 'a> ValidationHandler<'a, EvmWiringT> where - ::ValidationError: From, + ::TransactionError: From, { /// Create new ValidationHandles pub fn new() -> Self { diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs index 05d0b0f3f1..4c27175c59 100644 --- a/crates/revm/src/handler/mainnet.rs +++ b/crates/revm/src/handler/mainnet.rs @@ -9,10 +9,15 @@ mod validation; pub use execution::{ call, call_return, create, create_return, eofcreate, eofcreate_return, execute_frame, - insert_call_outcome, insert_create_outcome, insert_eofcreate_outcome, last_frame_return, + first_frame_creation, insert_call_outcome, insert_create_outcome, insert_eofcreate_outcome, + last_frame_return, }; pub use post_execution::{clear, end, output, refund, reimburse_caller, reward_beneficiary}; pub use pre_execution::{ apply_eip7702_auth_list, deduct_caller, deduct_caller_inner, load_accounts, load_precompiles, }; -pub use validation::{validate_env, validate_initial_tx_gas, validate_tx_against_state}; +pub use validation::{ + validate_block_env, validate_eip4844_tx, validate_env, validate_initial_tx_gas, + validate_priority_fee_tx, validate_tx_against_account, validate_tx_against_state, + validate_tx_env, +}; diff --git a/crates/revm/src/handler/mainnet/execution.rs b/crates/revm/src/handler/mainnet/execution.rs index 19e98f072d..062fcd50c4 100644 --- a/crates/revm/src/handler/mainnet/execution.rs +++ b/crates/revm/src/handler/mainnet/execution.rs @@ -2,13 +2,15 @@ use crate::{ frame::EOFCreateFrame, CallFrame, Context, CreateFrame, EvmWiring, Frame, FrameOrResult, FrameResult, }; +use bytecode::EOF_MAGIC_BYTES; use core::mem; use interpreter::{ - return_ok, return_revert, table::InstructionTables, CallInputs, CallOutcome, CreateInputs, - CreateOutcome, EOFCreateInputs, Gas, InterpreterAction, InterpreterResult, SharedMemory, - EMPTY_SHARED_MEMORY, + return_ok, return_revert, table::InstructionTables, CallInputs, CallOutcome, CallScheme, + CallValue, CreateInputs, CreateOutcome, CreateScheme, EOFCreateInputs, EOFCreateKind, Gas, + InterpreterAction, InterpreterResult, NewFrameAction, SharedMemory, EMPTY_SHARED_MEMORY, }; -use specification::hardfork::Spec; +use primitives::TxKind; +use specification::hardfork::{Spec, SpecId}; use std::boxed::Box; use wiring::{ result::{EVMError, EVMResultGeneric}, @@ -35,6 +37,53 @@ pub fn execute_frame( Ok(next_action) } +/// First frame creation. +pub fn first_frame_creation( + context: &mut Context, + gas_limit: u64, +) -> EVMResultGeneric { + // Make new frame action. + let tx = &context.evm.env.tx; + + let input = tx.common_fields().input().clone(); + + let new_frame = match tx.kind() { + TxKind::Call(target_address) => NewFrameAction::Call(Box::new(CallInputs { + input, + gas_limit, + target_address, + bytecode_address: target_address, + caller: tx.common_fields().caller(), + value: CallValue::Transfer(tx.common_fields().value()), + scheme: CallScheme::Call, + is_static: false, + is_eof: false, + return_memory_offset: 0..0, + })), + TxKind::Create => { + // if first byte of data is magic 0xEF00, then it is EOFCreate. + if SPEC::enabled(SpecId::PRAGUE_EOF) && input.starts_with(&EOF_MAGIC_BYTES) { + NewFrameAction::EOFCreate(Box::new(EOFCreateInputs::new( + tx.common_fields().caller(), + tx.common_fields().value(), + gas_limit, + EOFCreateKind::Tx { initdata: input }, + ))) + } else { + NewFrameAction::Create(Box::new(CreateInputs { + caller: tx.common_fields().caller(), + scheme: CreateScheme::Create, + value: tx.common_fields().value(), + init_code: input, + gas_limit, + })) + } + } + }; + + Ok(new_frame) +} + /// Handle output of the transaction #[inline] pub fn last_frame_return( @@ -47,7 +96,7 @@ pub fn last_frame_return( let refunded = gas.refunded(); // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - *gas = Gas::new_spent(context.evm.env.tx.gas_limit()); + *gas = Gas::new_spent(context.evm.env.tx.common_fields().gas_limit()); match instruction_result { return_ok!() => { diff --git a/crates/revm/src/handler/mainnet/post_execution.rs b/crates/revm/src/handler/mainnet/post_execution.rs index 5cfd7d5d32..784415e25f 100644 --- a/crates/revm/src/handler/mainnet/post_execution.rs +++ b/crates/revm/src/handler/mainnet/post_execution.rs @@ -76,7 +76,7 @@ pub fn reimburse_caller( context: &mut Context, gas: &Gas, ) -> EVMResultGeneric<(), EvmWiringT> { - let caller = *context.evm.env.tx.caller(); + let caller = context.evm.env.tx.common_fields().caller(); let effective_gas_price = context.evm.env.effective_gas_price(); // return balance of not spend gas. diff --git a/crates/revm/src/handler/mainnet/pre_execution.rs b/crates/revm/src/handler/mainnet/pre_execution.rs index 28c6252e4f..2741c5073e 100644 --- a/crates/revm/src/handler/mainnet/pre_execution.rs +++ b/crates/revm/src/handler/mainnet/pre_execution.rs @@ -11,10 +11,11 @@ use specification::{ hardfork::{Spec, SpecId}, }; use state::Account; +use transaction::{eip7702::Authorization, Eip7702Tx}; use wiring::{ default::EnvWiring, result::{EVMError, EVMResultGeneric}, - Block, Transaction, + Block, Transaction, TransactionType, }; /// Main precompile load @@ -65,11 +66,11 @@ pub fn deduct_caller_inner( ) { // Subtract gas costs from the caller's account. // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = U256::from(env.tx.gas_limit()).saturating_mul(env.effective_gas_price()); + let mut gas_cost = + U256::from(env.tx.common_fields().gas_limit()).saturating_mul(env.effective_gas_price()); // EIP-4844 - if SPEC::enabled(SpecId::CANCUN) { - let data_fee = env.calc_data_fee().expect("already checked"); + if let Some(data_fee) = env.calc_data_fee() { gas_cost = gas_cost.saturating_add(data_fee); } @@ -91,15 +92,13 @@ pub fn deduct_caller_inner( pub fn deduct_caller( context: &mut Context, ) -> EVMResultGeneric<(), EvmWiringT> { + let caller = context.evm.inner.env.tx.common_fields().caller(); // load caller's account. let caller_account = context .evm .inner .journaled_state - .load_account( - *context.evm.inner.env.tx.caller(), - &mut context.evm.inner.db, - ) + .load_account(caller, &mut context.evm.inner.db) .map_err(EVMError::Database)?; // deduct gas cost from caller's account. @@ -115,9 +114,7 @@ pub fn deduct_caller( .journal .last_mut() .unwrap() - .push(JournalEntry::NonceChange { - address: *context.evm.inner.env.tx.caller(), - }); + .push(JournalEntry::NonceChange { address: caller }); } Ok(()) } @@ -133,12 +130,15 @@ pub fn apply_eip7702_auth_list( } // return if there is no auth list. - let Some(authorization_list) = context.evm.inner.env.tx.authorization_list() else { + let tx = &context.evm.inner.env.tx; + if tx.tx_type().into() != TransactionType::Eip7702 { return Ok(0); - }; + } + + //let authorization_list = tx.eip7702().authorization_list(); let mut refunded_accounts = 0; - for authorization in authorization_list.recovered_iter() { + for authorization in tx.eip7702().authorization_list_iter() { // 1. recover authority and authorized addresses. // authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s] let Some(authority) = authorization.authority() else { @@ -180,7 +180,7 @@ pub fn apply_eip7702_auth_list( } // 7. Set the code of authority to be 0xef0100 || address. This is a delegation designation. - let bytecode = Bytecode::new_eip7702(authorization.address); + let bytecode = Bytecode::new_eip7702(authorization.address()); authority_acc.info.code_hash = bytecode.hash_slow(); authority_acc.info.code = Some(bytecode); diff --git a/crates/revm/src/handler/mainnet/validation.rs b/crates/revm/src/handler/mainnet/validation.rs index e50cdb29bb..233a1cb5f0 100644 --- a/crates/revm/src/handler/mainnet/validation.rs +++ b/crates/revm/src/handler/mainnet/validation.rs @@ -1,23 +1,327 @@ +use core::cmp::{self, Ordering}; + use crate::{Context, EvmWiring}; use interpreter::gas; -use specification::hardfork::Spec; +use primitives::{B256, U256}; +use specification::{ + constants::MAX_INITCODE_SIZE, + eip4844, + hardfork::{Spec, SpecId}, +}; +use state::Account; +use std::boxed::Box; +use transaction::{ + eip7702::Authorization, Eip1559CommonTxFields, Eip2930Tx, Eip4844Tx, Eip7702Tx, LegacyTx, + Transaction, +}; use wiring::{ - default::EnvWiring, - result::{EVMError, EVMResultGeneric, InvalidTransaction}, - transaction::{Transaction, TransactionValidation}, + default::{CfgEnv, EnvWiring}, + result::{EVMError, EVMResultGeneric, InvalidHeader, InvalidTransaction}, + Block, TransactionType, }; -/// Validate environment for the mainnet. +/// Validate environment (block and transaction) for the mainnet. pub fn validate_env( env: &EnvWiring, ) -> EVMResultGeneric<(), EvmWiringT> where - ::ValidationError: From, + ::TransactionError: From, { - // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::() - .map_err(|error| EVMError::Transaction(error.into()))?; + // Important: validate block before tx as some field are used in transaction validation. + validate_block_env::(&env.block).map_err(EVMError::Header)?; + + // validate transaction. + validate_tx_env::(&env.tx, &env.block, &env.cfg) + .map_err(|e| EVMError::Transaction(e.into()))?; + Ok(()) +} + +/// Validate the block environment. +#[inline] +pub fn validate_block_env( + block: &EvmWiringT::Block, +) -> Result<(), InvalidHeader> { + // `prevrandao` is required for the merge + if SPEC::enabled(SpecId::MERGE) && block.prevrandao().is_none() { + return Err(InvalidHeader::PrevrandaoNotSet); + } + // `excess_blob_gas` is required for Cancun + if SPEC::enabled(SpecId::CANCUN) && block.blob_excess_gas_and_price().is_none() { + return Err(InvalidHeader::ExcessBlobGasNotSet); + } + Ok(()) +} + +/// Validate transaction that has EIP-1559 priority fee +pub fn validate_priority_fee_tx( + max_fee: u128, + max_priority_fee: u128, + base_fee: Option, +) -> Result<(), InvalidTransaction> { + if max_priority_fee > max_fee { + // or gas_max_fee for eip1559 + return Err(InvalidTransaction::PriorityFeeGreaterThanMaxFee); + } + + // check minimal cost against basefee + if let Some(base_fee) = base_fee { + let effective_gas_price = cmp::min( + U256::from(max_fee), + base_fee.saturating_add(U256::from(max_priority_fee)), + ); + if effective_gas_price < base_fee { + return Err(InvalidTransaction::GasPriceLessThanBasefee); + } + } + + Ok(()) +} + +/// Validate EIP-4844 transaction. +pub fn validate_eip4844_tx( + blobs: &[B256], + max_blob_fee: u128, + block_blob_gas_price: u128, +) -> Result<(), InvalidTransaction> { + // ensure that the user was willing to at least pay the current blob gasprice + if block_blob_gas_price > max_blob_fee { + return Err(InvalidTransaction::BlobGasPriceGreaterThanMax); + } + + // there must be at least one blob + if blobs.is_empty() { + return Err(InvalidTransaction::EmptyBlobs); + } + + // all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG + for blob in blobs { + if blob[0] != eip4844::VERSIONED_HASH_VERSION_KZG { + return Err(InvalidTransaction::BlobVersionNotSupported); + } + } + + // ensure the total blob gas spent is at most equal to the limit + // assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK + if blobs.len() > eip4844::MAX_BLOB_NUMBER_PER_BLOCK as usize { + return Err(InvalidTransaction::TooManyBlobs { + have: blobs.len(), + max: eip4844::MAX_BLOB_NUMBER_PER_BLOCK as usize, + }); + } + Ok(()) +} + +/// Validate transaction against block and configuration for mainnet. +pub fn validate_tx_env( + tx: &EvmWiringT::Transaction, + block: &EvmWiringT::Block, + cfg: &CfgEnv, +) -> Result<(), InvalidTransaction> { + // Check if the transaction's chain id is correct + let common_field = tx.common_fields(); + let tx_type = tx.tx_type().into(); + + let base_fee = if cfg.is_base_fee_check_disabled() { + None + } else { + Some(*block.basefee()) + }; + + match tx_type { + TransactionType::Legacy => { + let tx = tx.legacy(); + // check chain_id only if it is present in the legacy transaction. + // EIP-155: Simple replay attack protection + if let Some(chain_id) = tx.chain_id() { + if chain_id != cfg.chain_id { + return Err(InvalidTransaction::InvalidChainId); + } + } + // gas price must be at least the basefee. + if let Some(base_fee) = base_fee { + if U256::from(tx.gas_price()) < base_fee { + return Err(InvalidTransaction::GasPriceLessThanBasefee); + } + } + } + TransactionType::Eip2930 => { + // enabled in BERLIN hardfork + if !SPEC::enabled(SpecId::BERLIN) { + return Err(InvalidTransaction::Eip2930NotSupported); + } + let tx = tx.eip2930(); + + if cfg.chain_id != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId); + } + + // gas price must be at least the basefee. + if let Some(base_fee) = base_fee { + if U256::from(tx.gas_price()) < base_fee { + return Err(InvalidTransaction::GasPriceLessThanBasefee); + } + } + } + TransactionType::Eip1559 => { + if !SPEC::enabled(SpecId::LONDON) { + return Err(InvalidTransaction::Eip1559NotSupported); + } + let tx = tx.eip1559(); + + if cfg.chain_id != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId); + } + + validate_priority_fee_tx( + tx.max_fee_per_gas(), + tx.max_priority_fee_per_gas(), + base_fee, + )?; + } + TransactionType::Eip4844 => { + if !SPEC::enabled(SpecId::CANCUN) { + return Err(InvalidTransaction::Eip4844NotSupported); + } + let tx = tx.eip4844(); + + if cfg.chain_id != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId); + } + + validate_priority_fee_tx( + tx.max_fee_per_gas(), + tx.max_priority_fee_per_gas(), + base_fee, + )?; + + validate_eip4844_tx( + tx.blob_versioned_hashes(), + tx.max_fee_per_blob_gas(), + block.blob_gasprice().unwrap_or_default(), + )?; + } + TransactionType::Eip7702 => { + // check if EIP-7702 transaction is enabled. + if !SPEC::enabled(SpecId::PRAGUE) { + return Err(InvalidTransaction::Eip7702NotSupported); + } + let tx = tx.eip7702(); + + if cfg.chain_id != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId); + } + + validate_priority_fee_tx( + tx.max_fee_per_gas(), + tx.max_priority_fee_per_gas(), + base_fee, + )?; + + let auth_list_len = tx.authorization_list_len(); + // The transaction is considered invalid if the length of authorization_list is zero. + if auth_list_len == 0 { + return Err(InvalidTransaction::EmptyAuthorizationList); + } + + // TODO temporary here as newest EIP have removed this check. + for auth in tx.authorization_list_iter() { + if auth.is_invalid() { + return Err(InvalidTransaction::Eip7702NotSupported); + } + } + } + TransactionType::Custom => { + // custom transaction type check is not done here. + } + }; + + // Check if gas_limit is more than block_gas_limit + if !cfg.is_block_gas_limit_disabled() + && U256::from(common_field.gas_limit()) > *block.gas_limit() + { + return Err(InvalidTransaction::CallerGasLimitMoreThanBlock); + } + + // EIP-3860: Limit and meter initcode + if SPEC::enabled(SpecId::SHANGHAI) && tx.kind().is_create() { + let max_initcode_size = cfg + .limit_contract_code_size + .map(|limit| limit.saturating_mul(2)) + .unwrap_or(MAX_INITCODE_SIZE); + if tx.common_fields().input().len() > max_initcode_size { + return Err(InvalidTransaction::CreateInitCodeSizeLimit); + } + } + + Ok(()) +} + +/// Validate account against the transaction. +pub fn validate_tx_against_account( + account: &mut Account, + tx: &EvmWiringT::Transaction, + cfg: &CfgEnv, +) -> Result<(), InvalidTransaction> +where + ::TransactionError: From, +{ + let tx_type = tx.tx_type().into(); + // EIP-3607: Reject transactions from senders with deployed code + // This EIP is introduced after london but there was no collision in past + // so we can leave it enabled always + if !cfg.is_eip3607_disabled() { + let bytecode = &account.info.code.as_ref().unwrap(); + // allow EOAs whose code is a valid delegation designation, + // i.e. 0xef0100 || address, to continue to originate transactions. + if !bytecode.is_empty() && !bytecode.is_eip7702() { + return Err(InvalidTransaction::RejectCallerWithCode); + } + } + + // Check that the transaction's nonce is correct + if !cfg.is_nonce_check_disabled() { + let tx = tx.common_fields().nonce(); + let state = account.info.nonce; + match tx.cmp(&state) { + Ordering::Greater => { + return Err(InvalidTransaction::NonceTooHigh { tx, state }); + } + Ordering::Less => { + return Err(InvalidTransaction::NonceTooLow { tx, state }); + } + _ => {} + } + } + + // gas_limit * max_fee + value + let mut balance_check = U256::from(tx.common_fields().gas_limit()) + .checked_mul(U256::from(tx.max_fee())) + .and_then(|gas_cost| gas_cost.checked_add(tx.common_fields().value())) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + + if tx_type == TransactionType::Eip4844 { + let tx = tx.eip4844(); + // if the tx is not a blob tx, this will be None, so we add zero + let data_fee = tx.calc_max_data_fee(); + balance_check = balance_check + .checked_add(U256::from(data_fee)) + .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; + } + + // Check if account has enough balance for `gas_limit * max_fee`` and value transfer. + // Transfer will be done inside `*_inner` functions. + if balance_check > account.info.balance { + if cfg.is_balance_check_disabled() { + // Add transaction cost to balance to ensure execution doesn't fail. + account.info.balance = account.info.balance.saturating_add(balance_check); + } else { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(balance_check), + balance: Box::new(account.info.balance), + }); + } + } + Ok(()) } @@ -26,23 +330,24 @@ pub fn validate_tx_against_state( context: &mut Context, ) -> EVMResultGeneric<(), EvmWiringT> where - ::ValidationError: From, + ::TransactionError: From, { + let tx_caller = context.evm.env.tx.common_fields().caller(); // load acc - let tx_caller = *context.evm.env.tx.caller(); - let caller_account = context - .evm - .inner + + let inner = &mut context.evm.inner; + + let caller_account = inner .journaled_state - .load_code(tx_caller, &mut context.evm.inner.db) + .load_code(tx_caller, &mut inner.db) .map_err(EVMError::Database)?; - context - .evm - .inner - .env - .validate_tx_against_state::(caller_account.data) - .map_err(|e| EVMError::Transaction(e.into()))?; + validate_tx_against_account::( + caller_account.data, + &inner.env.tx, + &inner.env.cfg, + ) + .map_err(|e| EVMError::Transaction(e.into()))?; Ok(()) } @@ -52,17 +357,20 @@ pub fn validate_initial_tx_gas( env: &EnvWiring, ) -> EVMResultGeneric where - ::ValidationError: From, + ::TransactionError: From, { - let input = &env.tx.data(); + let tx_type = env.tx.tx_type().into(); + + let authorization_list_num = if tx_type == TransactionType::Eip7702 { + env.tx.eip7702().authorization_list_len() as u64 + } else { + 0 + }; + + let common_fields = env.tx.common_fields(); let is_create = env.tx.kind().is_create(); + let input = common_fields.input(); let access_list = env.tx.access_list(); - let authorization_list_num = env - .tx - .authorization_list() - .as_ref() - .map(|l| l.len() as u64) - .unwrap_or_default(); let initial_gas_spend = gas::validate_initial_tx_gas( SPEC::SPEC_ID, @@ -73,7 +381,7 @@ where ); // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit() { + if initial_gas_spend > common_fields.gas_limit() { return Err(EVMError::Transaction( InvalidTransaction::CallGasCostMoreThanGasLimit.into(), )); diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index da02554dc3..c1663e252d 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -14,6 +14,7 @@ pub use precompile; pub use primitives; pub use specification; pub use state; +pub use transaction; pub use wiring; // Define modules. diff --git a/crates/specification/src/constants.rs b/crates/specification/src/constants.rs index 7f545727c3..4f489fb11b 100644 --- a/crates/specification/src/constants.rs +++ b/crates/specification/src/constants.rs @@ -1,2 +1,12 @@ /// EVM interpreter stack limit. pub const STACK_LIMIT: usize = 1024; + +/// EIP-170: Contract code size limit +/// +/// By default the limit is `0x6000` (~25kb) +pub const MAX_CODE_SIZE: usize = 0x6000; + +/// EIP-3860: Limit and meter initcode +/// +/// Limit of maximum initcode size is `2 * MAX_CODE_SIZE`. +pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; diff --git a/crates/specification/src/eip4844.rs b/crates/specification/src/eip4844.rs new file mode 100644 index 0000000000..0c18be4002 --- /dev/null +++ b/crates/specification/src/eip4844.rs @@ -0,0 +1,25 @@ +/// === EIP-4844 constants === + +/// Gas consumption of a single data blob (== blob byte size). +pub const GAS_PER_BLOB: u64 = 1 << 17; + +/// Target number of the blob per block. +pub const TARGET_BLOB_NUMBER_PER_BLOCK: u64 = 3; + +/// Max number of blobs per block +pub const MAX_BLOB_NUMBER_PER_BLOCK: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK; + +/// Maximum consumable blob gas for data blobs per block. +pub const MAX_BLOB_GAS_PER_BLOCK: u64 = MAX_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; + +/// Target consumable blob gas for data blobs per block (for 1559-like pricing). +pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = TARGET_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; + +/// Minimum gas price for data blobs. +pub const MIN_BLOB_GASPRICE: u64 = 1; + +/// Controls the maximum rate of change for blob gas price. +pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477; + +/// First version of the blob. +pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; diff --git a/crates/specification/src/eip7702/authorization_list.rs b/crates/specification/src/eip7702/authorization_list.rs index 546fea6dcc..0caa02ab24 100644 --- a/crates/specification/src/eip7702/authorization_list.rs +++ b/crates/specification/src/eip7702/authorization_list.rs @@ -1,5 +1,5 @@ use super::RecoveredAuthorization; -use crate::{eip2::SECP256K1N_HALF, eip7702::SignedAuthorization}; +use crate::eip7702::SignedAuthorization; pub use alloy_primitives::{Parity, Signature}; use core::fmt; use std::{boxed::Box, vec::Vec}; @@ -12,6 +12,12 @@ pub enum AuthorizationList { Recovered(Vec), } +impl Default for AuthorizationList { + fn default() -> Self { + Self::Signed(Vec::new()) + } +} + impl From> for AuthorizationList { fn from(signed: Vec) -> Self { Self::Signed(signed) @@ -33,40 +39,6 @@ impl AuthorizationList { } } - /// Returns true if the authorization list is valid. - pub fn is_valid(&self, _chain_id: u64) -> Result<(), InvalidAuthorization> { - let validate = |auth: &SignedAuthorization| -> Result<(), InvalidAuthorization> { - // TODO Eip7702. Check chain_id - // Pending: https://github.com/ethereum/EIPs/pull/8833/files - // let auth_chain_id: u64 = auth.chain_id().try_into().unwrap_or(u64::MAX); - // if auth_chain_id != 0 && auth_chain_id != chain_id { - // return Err(InvalidAuthorization::InvalidChainId); - // } - - // Check y_parity, Parity::Parity means that it was 0 or 1. - if !matches!(auth.signature().v(), Parity::Parity(_)) { - return Err(InvalidAuthorization::InvalidYParity); - } - - // Check s-value - if auth.signature().s() > SECP256K1N_HALF { - return Err(InvalidAuthorization::Eip2InvalidSValue); - } - - Ok(()) - }; - - match self { - Self::Signed(signed) => signed.iter().try_for_each(validate)?, - Self::Recovered(recovered) => recovered - .iter() - .map(|recovered| recovered.inner()) - .try_for_each(validate)?, - }; - - Ok(()) - } - /// Return empty authorization list. pub fn empty() -> Self { Self::Recovered(Vec::new()) diff --git a/crates/specification/src/lib.rs b/crates/specification/src/lib.rs index 68bf7cd61b..e7e0ce4d0f 100644 --- a/crates/specification/src/lib.rs +++ b/crates/specification/src/lib.rs @@ -9,5 +9,6 @@ pub mod constants; pub mod eip170; pub mod eip2; pub mod eip2930; +pub mod eip4844; pub mod eip7702; pub mod hardfork; diff --git a/crates/state/Cargo.toml b/crates/state/Cargo.toml index e7d62ad841..3028ef38d5 100644 --- a/crates/state/Cargo.toml +++ b/crates/state/Cargo.toml @@ -38,6 +38,8 @@ serde = { version = "1.0", default-features = false, features = [ [features] +default = ["std"] +std = ["serde?/std", "primitives/std"] serde = [ "dep:serde", "primitives/serde", @@ -45,5 +47,3 @@ serde = [ "bytecode/serde", "specification/serde", ] -default = ["std"] -std = ["serde?/std", "primitives/std"] diff --git a/crates/statetest-types/src/transaction.rs b/crates/statetest-types/src/transaction.rs index f68e041a7a..873f366fd2 100644 --- a/crates/statetest-types/src/transaction.rs +++ b/crates/statetest-types/src/transaction.rs @@ -1,6 +1,7 @@ use revm::{ primitives::{Address, Bytes, B256, U256}, specification::eip2930::AccessList, + transaction::TransactionType, }; use serde::{Deserialize, Serialize}; @@ -32,6 +33,47 @@ pub struct TransactionParts { pub max_fee_per_blob_gas: Option, } +impl TransactionParts { + /// Returns the transaction type. + /// + /// As this information is derived from the fields it is not stored in the struct. + /// + /// Returns `None` if the transaction is invalid: + /// * It has both blob gas and no destination. + /// * It has authorization list and no destination. + pub fn tx_type(&self, access_list_index: usize) -> Option { + let mut tx_type = TransactionType::Legacy; + + // if it has access list it is EIP-2930 tx + if let Some(access_list) = self.access_lists.get(access_list_index) { + if access_list.is_some() { + tx_type = TransactionType::Eip2930; + } + } + + // If there is max_fee it is EIP-1559 tx + if self.max_fee_per_gas.is_some() { + tx_type = TransactionType::Eip1559; + } + + // if it has max_fee_per_blob_gas it is EIP-4844 tx + if self.max_fee_per_blob_gas.is_some() { + // target need to be present for EIP-4844 tx + self.to?; + tx_type = TransactionType::Eip4844; + } + + // and if it has authorization list it is EIP-7702 tx + if self.authorization_list.is_some() { + // target need to be present for EIP-7702 tx + self.to?; + tx_type = TransactionType::Eip7702; + } + + Some(tx_type) + } +} + /// Transaction part indices. #[derive(Debug, PartialEq, Eq, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] diff --git a/crates/wiring/Cargo.toml b/crates/wiring/Cargo.toml index 668bc10bc6..17cb501f58 100644 --- a/crates/wiring/Cargo.toml +++ b/crates/wiring/Cargo.toml @@ -27,6 +27,7 @@ primitives.workspace = true database-interface.workspace = true specification.workspace = true state.workspace = true +transaction.workspace = true # mics dyn-clone = "1.0" @@ -54,7 +55,13 @@ once_cell = { version = "1.19", default-features = false, optional = true, featu [features] default = ["std", "portable"] std = ["serde?/std"] -serde = ["dep:serde", "primitives/serde", "specification/serde", "state/serde"] +serde = [ + "dep:serde", + "primitives/serde", + "transaction/serde", + "specification/serde", + "state/serde", +] portable = ["c-kzg?/portable"] c-kzg = ["dep:c-kzg", "dep:cfg-if"] diff --git a/crates/wiring/src/block.rs b/crates/wiring/src/block.rs index b8d0d8f6f6..2215635f19 100644 --- a/crates/wiring/src/block.rs +++ b/crates/wiring/src/block.rs @@ -50,19 +50,19 @@ pub trait Block { /// See [EIP-4844] and [`calc_blob_gasprice`]. /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`crate::default::Env::validate_block_env`]. + /// Returns `None` if `Cancun` is not enabled. /// /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - fn get_blob_gasprice(&self) -> Option<&u128> { - self.blob_excess_gas_and_price().map(|a| &a.blob_gasprice) + fn blob_gasprice(&self) -> Option { + self.blob_excess_gas_and_price().map(|a| a.blob_gasprice) } /// Return `blob_excess_gas` header field. See [EIP-4844]. /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`crate::default::Env::validate_block_env`]. + /// Returns `None` if `Cancun` is not enabled. /// /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - fn get_blob_excess_gas(&self) -> Option { + fn blob_excess_gas(&self) -> Option { self.blob_excess_gas_and_price().map(|a| a.excess_blob_gas) } } diff --git a/crates/wiring/src/block/blob.rs b/crates/wiring/src/block/blob.rs index 646b95e9db..fcf86659d5 100644 --- a/crates/wiring/src/block/blob.rs +++ b/crates/wiring/src/block/blob.rs @@ -1,4 +1,6 @@ -use primitives::{BLOB_GASPRICE_UPDATE_FRACTION, MIN_BLOB_GASPRICE, TARGET_BLOB_GAS_PER_BLOCK}; +use specification::eip4844::{ + BLOB_GASPRICE_UPDATE_FRACTION, MIN_BLOB_GASPRICE, TARGET_BLOB_GAS_PER_BLOCK, +}; /// Structure holding block blob excess gas and it calculates blob fee. /// @@ -80,7 +82,7 @@ pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 { #[cfg(test)] mod tests { use super::*; - use primitives::GAS_PER_BLOB; + use specification::eip4844::GAS_PER_BLOB; // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L27 #[test] diff --git a/crates/wiring/src/default.rs b/crates/wiring/src/default.rs index 028b801bc9..0669599380 100644 --- a/crates/wiring/src/default.rs +++ b/crates/wiring/src/default.rs @@ -1,25 +1,16 @@ pub mod block; -pub mod transaction; +pub mod tx; + +use transaction::{Eip4844Tx, TransactionType}; +pub use tx::TxEnv; use crate::block::blob::calc_blob_gasprice; -use crate::result::InvalidHeader; -use crate::transaction::TransactionValidation; -use crate::{result::InvalidTransaction, Block, EvmWiring, Transaction}; -use core::cmp::{min, Ordering}; +use crate::{Block, EvmWiring, Transaction}; use core::fmt::Debug; use core::hash::Hash; -use primitives::{ - Address, Bytes, TxKind, B256, MAX_BLOB_NUMBER_PER_BLOCK, MAX_CODE_SIZE, MAX_INITCODE_SIZE, - U256, VERSIONED_HASH_VERSION_KZG, -}; -use specification::{ - eip2930::AccessListItem, - eip7702::AuthorizationList, - hardfork::{Spec, SpecId}, -}; -use state::Account; +use primitives::{TxKind, U256}; +use specification::constants::MAX_CODE_SIZE; use std::boxed::Box; -use std::vec::Vec; /// Subtype pub type EnvWiring = @@ -44,27 +35,24 @@ impl Env { Box::new(Self { cfg, block, tx }) } - /// Calculates the effective gas price of the transaction. - #[inline] pub fn effective_gas_price(&self) -> U256 { - let gas_price = self.tx.gas_price(); - if let Some(priority_fee) = self.tx.max_priority_fee_per_gas() { - min(*gas_price, self.block.basefee() + priority_fee) - } else { - *gas_price - } + let basefee = *self.block.basefee(); + self.tx.effective_gas_price(basefee) } /// Calculates the [EIP-4844] `data_fee` of the transaction. /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`]. + /// Returns `None` if `Cancun` is not enabled. /// /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 #[inline] pub fn calc_data_fee(&self) -> Option { - self.block.get_blob_gasprice().map(|blob_gas_price| { - U256::from(*blob_gas_price).saturating_mul(U256::from(self.tx.get_total_blob_gas())) - }) + if self.tx.tx_type().into() == TransactionType::Eip4844 { + let blob_gas = U256::from(self.tx.eip4844().total_blob_gas()); + let blob_gas_price = U256::from(self.block.blob_gasprice().unwrap_or_default()); + return Some(blob_gas_price.saturating_mul(blob_gas)); + } + None } /// Calculates the maximum [EIP-4844] `data_fee` of the transaction. @@ -75,217 +63,12 @@ impl Env { /// See EIP-4844: /// pub fn calc_max_data_fee(&self) -> Option { - self.tx.max_fee_per_blob_gas().map(|max_fee_per_blob_gas| { - max_fee_per_blob_gas.saturating_mul(U256::from(self.tx.get_total_blob_gas())) - }) - } - - /// Validate the block environment. - #[inline] - pub fn validate_block_env(&self) -> Result<(), InvalidHeader> { - // `prevrandao` is required for the merge - if SPEC::enabled(SpecId::MERGE) && self.block.prevrandao().is_none() { - return Err(InvalidHeader::PrevrandaoNotSet); - } - // `excess_blob_gas` is required for Cancun - if SPEC::enabled(SpecId::CANCUN) && self.block.blob_excess_gas_and_price().is_none() { - return Err(InvalidHeader::ExcessBlobGasNotSet); - } - Ok(()) - } - - /// Validate transaction data that is set inside ENV and return error if something is wrong. - /// - /// Return initial spend gas (Gas needed to execute transaction). - #[inline] - pub fn validate_tx(&self) -> Result<(), InvalidTransaction> { - // Check if the transaction's chain id is correct - if let Some(tx_chain_id) = self.tx.chain_id() { - if tx_chain_id != self.cfg.chain_id { - return Err(InvalidTransaction::InvalidChainId); - } - } - - // Check if gas_limit is more than block_gas_limit - if !self.cfg.is_block_gas_limit_disabled() - && U256::from(self.tx.gas_limit()) > *self.block.gas_limit() - { - return Err(InvalidTransaction::CallerGasLimitMoreThanBlock); - } - - // Check that access list is empty for transactions before BERLIN - if !SPEC::enabled(SpecId::BERLIN) && !self.tx.access_list().is_empty() { - return Err(InvalidTransaction::AccessListNotSupported); - } - - // BASEFEE tx check - if SPEC::enabled(SpecId::LONDON) { - if let Some(priority_fee) = self.tx.max_priority_fee_per_gas() { - if priority_fee > self.tx.gas_price() { - // or gas_max_fee for eip1559 - return Err(InvalidTransaction::PriorityFeeGreaterThanMaxFee); - } - } - - // check minimal cost against basefee - if !self.cfg.is_base_fee_check_disabled() - && self.effective_gas_price() < *self.block.basefee() - { - return Err(InvalidTransaction::GasPriceLessThanBasefee); - } - } - - // EIP-3860: Limit and meter initcode - if SPEC::enabled(SpecId::SHANGHAI) && self.tx.kind().is_create() { - let max_initcode_size = self - .cfg - .limit_contract_code_size - .map(|limit| limit.saturating_mul(2)) - .unwrap_or(MAX_INITCODE_SIZE); - if self.tx.data().len() > max_initcode_size { - return Err(InvalidTransaction::CreateInitCodeSizeLimit); - } - } - - // - For before CANCUN, check that `blob_hashes` and `max_fee_per_blob_gas` are empty / not set - if !SPEC::enabled(SpecId::CANCUN) - && (self.tx.max_fee_per_blob_gas().is_some() || !self.tx.blob_hashes().is_empty()) - { - return Err(InvalidTransaction::BlobVersionedHashesNotSupported); - } - - // Presence of max_fee_per_blob_gas means that this is blob transaction. - if let Some(max) = self.tx.max_fee_per_blob_gas() { - // ensure that the user was willing to at least pay the current blob gasprice - let price = self.block.get_blob_gasprice().expect("already checked"); - if U256::from(*price) > *max { - return Err(InvalidTransaction::BlobGasPriceGreaterThanMax); - } - - // there must be at least one blob - if self.tx.blob_hashes().is_empty() { - return Err(InvalidTransaction::EmptyBlobs); - } - - // The field `to` deviates slightly from the semantics with the exception - // that it MUST NOT be nil and therefore must always represent - // a 20-byte address. This means that blob transactions cannot - // have the form of a create transaction. - if self.tx.kind().is_create() { - return Err(InvalidTransaction::BlobCreateTransaction); - } - - // all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG - for blob in self.tx.blob_hashes() { - if blob[0] != VERSIONED_HASH_VERSION_KZG { - return Err(InvalidTransaction::BlobVersionNotSupported); - } - } - - // ensure the total blob gas spent is at most equal to the limit - // assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK - let num_blobs = self.tx.blob_hashes().len(); - if num_blobs > MAX_BLOB_NUMBER_PER_BLOCK as usize { - return Err(InvalidTransaction::TooManyBlobs { - have: num_blobs, - max: MAX_BLOB_NUMBER_PER_BLOCK as usize, - }); - } - } else { - // if max_fee_per_blob_gas is not set, then blob_hashes must be empty - if !self.tx.blob_hashes().is_empty() { - return Err(InvalidTransaction::BlobVersionedHashesNotSupported); - } - } - - // check if EIP-7702 transaction is enabled. - if !SPEC::enabled(SpecId::PRAGUE) && self.tx.authorization_list().is_some() { - return Err(InvalidTransaction::AuthorizationListNotSupported); - } - - if let Some(auth_list) = &self.tx.authorization_list() { - // The transaction is considered invalid if the length of authorization_list is zero. - if auth_list.is_empty() { - return Err(InvalidTransaction::EmptyAuthorizationList); - } - - // Check validity of authorization_list - auth_list.is_valid(self.cfg.chain_id)?; - - // Check if other fields are unset. - if self.tx.max_fee_per_blob_gas().is_some() || !self.tx.blob_hashes().is_empty() { - return Err(InvalidTransaction::AuthorizationListInvalidFields); - } - } - - Ok(()) - } - - /// Validate transaction against state. - /// - /// # Panics - /// - /// If account code is not loaded. - #[inline] - pub fn validate_tx_against_state( - &self, - account: &mut Account, - ) -> Result<(), InvalidTransaction> { - // EIP-3607: Reject transactions from senders with deployed code - // This EIP is introduced after london but there was no collision in past - // so we can leave it enabled always - if !self.cfg.is_eip3607_disabled() { - let bytecode = &account.info.code.as_ref().unwrap(); - // allow EOAs whose code is a valid delegation designation, - // i.e. 0xef0100 || address, to continue to originate transactions. - if !bytecode.is_empty() && !bytecode.is_eip7702() { - return Err(InvalidTransaction::RejectCallerWithCode); - } + if self.tx.tx_type().into() == TransactionType::Eip4844 { + let blob_gas = U256::from(self.tx.eip4844().total_blob_gas()); + let max_blob_fee = U256::from(self.tx.eip4844().max_fee_per_blob_gas()); + return Some(max_blob_fee.saturating_mul(blob_gas)); } - - // Check that the transaction's nonce is correct - if !self.cfg.is_nonce_check_disabled() { - let tx = self.tx.nonce(); - let state = account.info.nonce; - match tx.cmp(&state) { - Ordering::Greater => { - return Err(InvalidTransaction::NonceTooHigh { tx, state }); - } - Ordering::Less => { - return Err(InvalidTransaction::NonceTooLow { tx, state }); - } - _ => {} - } - } - - let mut balance_check = U256::from(self.tx.gas_limit()) - .checked_mul(*self.tx.gas_price()) - .and_then(|gas_cost| gas_cost.checked_add(*self.tx.value())) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - - if SPEC::enabled(SpecId::CANCUN) { - // if the tx is not a blob tx, this will be None, so we add zero - let data_fee = self.calc_max_data_fee().unwrap_or_default(); - balance_check = balance_check - .checked_add(U256::from(data_fee)) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - } - - // Check if account has enough balance for gas_limit*gas_price and value transfer. - // Transfer will be done inside `*_inner` functions. - if balance_check > account.info.balance { - if self.cfg.is_balance_check_disabled() { - // Add transaction cost to balance to ensure execution doesn't fail. - account.info.balance = balance_check; - } else { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(balance_check), - balance: Box::new(account.info.balance), - }); - } - } - - Ok(()) + None } } @@ -317,9 +100,7 @@ pub struct CfgEnv { /// If some it will effects EIP-170: Contract code size limit. Useful to increase this because of tests. /// By default it is 0x6000 (~25kb). pub limit_contract_code_size: Option, - /// Skips the nonce validation against the account's nonce: - /// [`crate::default::InvalidTransaction::NonceTooHigh`] and - /// [`crate::default::InvalidTransaction::NonceTooLow`] + /// Skips the nonce validation against the account's nonce. pub disable_nonce_check: bool, /// A hard memory limit in bytes beyond which [crate::result::OutOfGasError::Memory] cannot be resized. /// @@ -445,169 +226,6 @@ impl Default for CfgEnv { } } -/// The transaction environment. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TxEnv { - /// Caller aka Author aka transaction signer. - pub caller: Address, - /// The gas limit of the transaction. - pub gas_limit: u64, - /// The gas price of the transaction. - pub gas_price: U256, - /// The destination of the transaction. - pub transact_to: TxKind, - /// The value sent to `transact_to`. - pub value: U256, - /// The data of the transaction. - pub data: Bytes, - - /// The nonce of the transaction. - pub nonce: u64, - - /// The chain ID of the transaction. If set to `None`, no checks are performed. - /// - /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155]. - /// - /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 - pub chain_id: Option, - - /// A list of addresses and storage keys that the transaction plans to access. - /// - /// Added in [EIP-2930]. - /// - /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 - pub access_list: Vec, - - /// The priority fee per gas. - /// - /// Incorporated as part of the London upgrade via [EIP-1559]. - /// - /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 - pub gas_priority_fee: Option, - - /// The list of blob versioned hashes. Per EIP there should be at least - /// one blob present if [`Self::max_fee_per_blob_gas`] is `Some`. - /// - /// Incorporated as part of the Cancun upgrade via [EIP-4844]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - pub blob_hashes: Vec, - - /// The max fee per blob gas. - /// - /// Incorporated as part of the Cancun upgrade via [EIP-4844]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - pub max_fee_per_blob_gas: Option, - - /// List of authorizations, that contains the signature that authorizes this - /// caller to place the code to signer account. - /// - /// Set EOA account code for one transaction - /// - /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702) - pub authorization_list: Option, -} - -impl Transaction for TxEnv { - #[inline] - fn caller(&self) -> &Address { - &self.caller - } - - #[inline] - fn gas_limit(&self) -> u64 { - self.gas_limit - } - - #[inline] - fn gas_price(&self) -> &U256 { - &self.gas_price - } - - #[inline] - fn kind(&self) -> TxKind { - self.transact_to - } - - #[inline] - fn value(&self) -> &U256 { - &self.value - } - - #[inline] - fn data(&self) -> &Bytes { - &self.data - } - - #[inline] - fn nonce(&self) -> u64 { - self.nonce - } - - #[inline] - fn chain_id(&self) -> Option { - self.chain_id - } - - #[inline] - fn access_list(&self) -> &[AccessListItem] { - &self.access_list - } - - #[inline] - fn max_priority_fee_per_gas(&self) -> Option<&U256> { - self.gas_priority_fee.as_ref() - } - - #[inline] - fn blob_hashes(&self) -> &[B256] { - &self.blob_hashes - } - - #[inline] - fn max_fee_per_blob_gas(&self) -> Option<&U256> { - self.max_fee_per_blob_gas.as_ref() - } - - #[inline] - fn authorization_list(&self) -> Option<&AuthorizationList> { - self.authorization_list.as_ref() - } -} - -impl TransactionValidation for TxEnv { - type ValidationError = InvalidTransaction; -} - -pub enum TxType { - Legacy, - Eip1559, - BlobTx, - EofCreate, -} - -impl Default for TxEnv { - fn default() -> Self { - Self { - caller: Address::ZERO, - gas_limit: u64::MAX, - gas_price: U256::ZERO, - gas_priority_fee: None, - transact_to: TxKind::Call(Address::ZERO), // will do nothing - value: U256::ZERO, - data: Bytes::new(), - chain_id: None, - nonce: 0, - access_list: Vec::new(), - blob_hashes: Vec::new(), - max_fee_per_blob_gas: None, - authorization_list: None, - } - } -} - /// Structure holding block blob excess gas and it calculates blob fee. /// /// Incorporated as part of the Cancun upgrade via [EIP-4844]. @@ -662,31 +280,32 @@ pub enum AnalysisKind { #[cfg(test)] mod tests { - use super::*; - use crate::default::block::BlockEnv; - use specification::hardfork::{FrontierSpec, LatestSpec}; - - #[test] - fn test_validate_tx_chain_id() { - let mut env = Env::::default(); - env.tx.chain_id = Some(1); - env.cfg.chain_id = 2; - assert_eq!( - env.validate_tx::(), - Err(InvalidTransaction::InvalidChainId) - ); - } - - #[test] - fn test_validate_tx_access_list() { - let mut env = Env::::default(); - env.tx.access_list = vec![AccessListItem { - address: Address::ZERO, - storage_keys: vec![], - }]; - assert_eq!( - env.validate_tx::(), - Err(InvalidTransaction::AccessListNotSupported) - ); - } + // use super::*; + // use crate::default::block::BlockEnv; + // use specification::hardfork::{FrontierSpec, LatestSpec}; + + // #[test] + // fn test_validate_tx_chain_id() { + // let mut env = Env::::default(); + // env.tx.chain_id = Some(1); + // env.cfg.chain_id = 2; + // assert_eq!( + // env.validate_tx::(), + // Err(InvalidTransaction::InvalidChainId) + // ); + // } + + // #[test] + // fn test_validate_tx_access_list() { + // let mut env = Env::::default(); + // env.tx.access_list = vec![AccessListItem { + // address: Address::ZERO, + // storage_keys: vec![], + // }] + // .into(); + // assert_eq!( + // env.validate_tx::(), + // Err(InvalidTransaction::AccessListNotSupported) + // ); + // } } diff --git a/crates/wiring/src/default/transaction.rs b/crates/wiring/src/default/transaction.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/crates/wiring/src/default/transaction.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/wiring/src/default/tx.rs b/crates/wiring/src/default/tx.rs new file mode 100644 index 0000000000..586fbda383 --- /dev/null +++ b/crates/wiring/src/default/tx.rs @@ -0,0 +1,254 @@ +use crate::{result::InvalidTransaction, Transaction}; +use core::fmt::Debug; +use primitives::{Address, Bytes, TxKind, B256, U256}; +use specification::eip2930::AccessList; +use specification::eip7702::AuthorizationList; +use std::vec::Vec; +use transaction::{ + eip7702::Authorization, CommonTxFields, Eip1559CommonTxFields, Eip1559Tx, Eip2930Tx, Eip4844Tx, + Eip7702Tx, LegacyTx, TransactionType, +}; + +/// The transaction environment. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TxEnv { + pub tx_type: TransactionType, + /// Caller aka Author aka transaction signer. + pub caller: Address, + /// The gas limit of the transaction. + pub gas_limit: u64, + /// The gas price of the transaction. + pub gas_price: U256, + /// The destination of the transaction. + pub transact_to: TxKind, + /// The value sent to `transact_to`. + pub value: U256, + /// The data of the transaction. + pub data: Bytes, + + /// The nonce of the transaction. + pub nonce: u64, + + /// The chain ID of the transaction. If set to `None`, no checks are performed. + /// + /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155]. + /// + /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 + pub chain_id: Option, + + /// A list of addresses and storage keys that the transaction plans to access. + /// + /// Added in [EIP-2930]. + /// + /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 + pub access_list: AccessList, + + /// The priority fee per gas. + /// + /// Incorporated as part of the London upgrade via [EIP-1559]. + /// + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + pub gas_priority_fee: Option, + + /// The list of blob versioned hashes. Per EIP there should be at least + /// one blob present if [`Self::max_fee_per_blob_gas`] is `Some`. + /// + /// Incorporated as part of the Cancun upgrade via [EIP-4844]. + /// + /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 + pub blob_hashes: Vec, + + /// The max fee per blob gas. + /// + /// Incorporated as part of the Cancun upgrade via [EIP-4844]. + /// + /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 + pub max_fee_per_blob_gas: Option, + + /// List of authorizations, that contains the signature that authorizes this + /// caller to place the code to signer account. + /// + /// Set EOA account code for one transaction + /// + /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702) + pub authorization_list: AuthorizationList, +} + +impl Default for TxEnv { + fn default() -> Self { + Self { + tx_type: TransactionType::Legacy, + caller: Address::default(), + gas_limit: u64::MAX, + gas_price: U256::ZERO, + transact_to: TxKind::Call(Address::default()), + value: U256::ZERO, + data: Bytes::default(), + nonce: 0, + chain_id: Some(1), // Mainnet chain ID is 1 + access_list: AccessList::default(), + gas_priority_fee: Some(U256::ZERO), + blob_hashes: Vec::new(), + max_fee_per_blob_gas: Some(U256::ZERO), + authorization_list: AuthorizationList::default(), + } + } +} + +impl CommonTxFields for TxEnv { + fn caller(&self) -> Address { + self.caller + } + + fn gas_limit(&self) -> u64 { + self.gas_limit + } + + fn value(&self) -> U256 { + self.value + } + + fn input(&self) -> &Bytes { + &self.data + } + + fn nonce(&self) -> u64 { + self.nonce + } +} + +impl Eip1559CommonTxFields for TxEnv { + type AccessList = AccessList; + + fn chain_id(&self) -> u64 { + self.chain_id.unwrap_or_default() + } + + fn max_fee_per_gas(&self) -> u128 { + self.gas_price.to() + } + + fn max_priority_fee_per_gas(&self) -> u128 { + self.gas_priority_fee.unwrap_or_default().to() + } + + fn access_list(&self) -> &Self::AccessList { + &self.access_list + } +} + +impl LegacyTx for TxEnv { + fn kind(&self) -> TxKind { + self.transact_to + } + + fn chain_id(&self) -> Option { + self.chain_id + } + + fn gas_price(&self) -> u128 { + self.gas_price.try_into().unwrap_or(u128::MAX) + } +} + +impl Eip2930Tx for TxEnv { + type AccessList = AccessList; + + fn access_list(&self) -> &Self::AccessList { + &self.access_list + } + + fn chain_id(&self) -> u64 { + self.chain_id.unwrap_or_default() + } + + fn gas_price(&self) -> u128 { + self.gas_price.to() + } + + fn kind(&self) -> TxKind { + self.transact_to + } +} + +impl Eip1559Tx for TxEnv { + fn kind(&self) -> TxKind { + self.transact_to + } +} + +impl Eip4844Tx for TxEnv { + fn destination(&self) -> Address { + match self.transact_to { + TxKind::Call(addr) => addr, + TxKind::Create => panic!("Create transaction are not allowed in Eip4844"), + } + } + + fn blob_versioned_hashes(&self) -> &[B256] { + &self.blob_hashes + } + + fn max_fee_per_blob_gas(&self) -> u128 { + self.max_fee_per_blob_gas.unwrap_or_default().to() + } +} + +impl Eip7702Tx for TxEnv { + fn destination(&self) -> Address { + match self.transact_to { + TxKind::Call(addr) => addr, + TxKind::Create => panic!("Create transaction are not allowed in Eip7702"), + } + } + + fn authorization_list_len(&self) -> usize { + self.authorization_list.len() + } + + fn authorization_list_iter(&self) -> impl Iterator { + self.authorization_list.recovered_iter() + } +} + +impl Transaction for TxEnv { + type TransactionError = InvalidTransaction; + type TransactionType = TransactionType; + + type AccessList = ::AccessList; + + type Legacy = Self; + + type Eip1559 = Self; + + type Eip2930 = Self; + + type Eip4844 = Self; + + type Eip7702 = Self; + + fn tx_type(&self) -> Self::TransactionType { + self.tx_type + } + + fn legacy(&self) -> &Self::Legacy { + self + } + + fn eip2930(&self) -> &Self::Eip2930 { + self + } + + fn eip1559(&self) -> &Self::Eip1559 { + self + } + + fn eip4844(&self) -> &Self::Eip4844 { + self + } + + fn eip7702(&self) -> &Self::Eip7702 { + self + } +} diff --git a/crates/wiring/src/evm_wiring.rs b/crates/wiring/src/evm_wiring.rs index 78b78259dc..6ef73d4ae9 100644 --- a/crates/wiring/src/evm_wiring.rs +++ b/crates/wiring/src/evm_wiring.rs @@ -1,11 +1,8 @@ -use crate::{ - result::HaltReason, - transaction::{Transaction, TransactionValidation}, - Block, -}; +use crate::{result::HaltReason, Block}; use core::{fmt::Debug, hash::Hash}; use database_interface::{Database, EmptyDB}; use specification::hardfork::SpecId; +use transaction::Transaction; /// The type that enumerates the chain's hardforks. pub trait HardforkTrait: Clone + Copy + Default + PartialEq + Eq + Into {} @@ -36,7 +33,7 @@ pub trait EvmWiring: Sized { type Block: Block; /// The type that contains all transaction information. - type Transaction: Transaction + TransactionValidation; + type Transaction: Transaction; /// The type that enumerates the chain's hardforks. type Hardfork: HardforkTrait; diff --git a/crates/wiring/src/lib.rs b/crates/wiring/src/lib.rs index 1808d524c6..b6956334dc 100644 --- a/crates/wiring/src/lib.rs +++ b/crates/wiring/src/lib.rs @@ -10,11 +10,10 @@ pub mod default; pub mod evm_wiring; pub mod precompile; pub mod result; -pub mod transaction; pub use block::Block; pub use evm_wiring::{DefaultEthereumWiring, EthereumWiring, EvmWiring, HaltReasonTrait}; -pub use transaction::{Transaction, TransactionValidation}; +pub use transaction::{Transaction, TransactionType}; // KZG diff --git a/crates/wiring/src/result.rs b/crates/wiring/src/result.rs index 1fd53f6c7c..74eb2e2ed8 100644 --- a/crates/wiring/src/result.rs +++ b/crates/wiring/src/result.rs @@ -1,10 +1,11 @@ -use crate::{evm_wiring::HaltReasonTrait, transaction::TransactionValidation, EvmWiring}; +use crate::{evm_wiring::HaltReasonTrait, EvmWiring}; use core::fmt::{self, Debug}; use database_interface::Database; use primitives::{Address, Bytes, Log, U256}; use specification::eip7702::InvalidAuthorization; use state::EvmState; use std::{boxed::Box, string::String, vec::Vec}; +use transaction::{Transaction, TransactionError}; /// Result of EVM execution. pub type EVMResult = @@ -16,7 +17,7 @@ pub type EVMResultGeneric = core::result::Result = EVMError< <::Database as Database>::Error, - <::Transaction as TransactionValidation>::ValidationError, + <::Transaction as Transaction>::TransactionError, >; #[derive(Debug, Clone, PartialEq, Eq)] @@ -147,15 +148,15 @@ impl Output { pub type EVMErrorWiring = EVMError< <::Database as Database>::Error, - <::Transaction as TransactionValidation>::ValidationError, + <::Transaction as Transaction>::TransactionError, >; /// Main EVM error. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum EVMError { +pub enum EVMError { /// Transaction validation error. - Transaction(TransactionValidationErrorT), + Transaction(TransactionError), /// Header validation error. Header(InvalidHeader), /// Database error. @@ -287,7 +288,7 @@ pub enum InvalidTransaction { /// Blob transaction can't be a create transaction. /// `to` must be present BlobCreateTransaction, - /// Transaction has more then [`primitives::MAX_BLOB_NUMBER_PER_BLOCK`] blobs + /// Transaction has more then [`specification::eip4844::MAX_BLOB_NUMBER_PER_BLOCK`] blobs TooManyBlobs { max: usize, have: usize, @@ -304,8 +305,18 @@ pub enum InvalidTransaction { EmptyAuthorizationList, /// Invalid EIP-7702 Authorization List InvalidAuthorizationList(InvalidAuthorization), + /// EIP-2930 is not supported. + Eip2930NotSupported, + /// EIP-1559 is not supported. + Eip1559NotSupported, + /// EIP-4844 is not supported. + Eip4844NotSupported, + /// EIP-7702 is not supported. + Eip7702NotSupported, } +impl TransactionError for InvalidTransaction {} + impl From for InvalidTransaction { fn from(value: InvalidAuthorization) -> Self { Self::InvalidAuthorizationList(value) @@ -373,6 +384,10 @@ impl fmt::Display for InvalidTransaction { write!(f, "authorization list tx has invalid fields") } Self::EmptyAuthorizationList => write!(f, "empty authorization list"), + Self::Eip2930NotSupported => write!(f, "Eip2930 is not supported"), + Self::Eip1559NotSupported => write!(f, "Eip1559 is not supported"), + Self::Eip4844NotSupported => write!(f, "Eip4844 is not supported"), + Self::Eip7702NotSupported => write!(f, "Eip7702 is not supported"), Self::InvalidAuthorizationList(i) => fmt::Display::fmt(i, f), } } diff --git a/crates/wiring/src/transaction.rs b/crates/wiring/src/transaction.rs deleted file mode 100644 index 4a99d59166..0000000000 --- a/crates/wiring/src/transaction.rs +++ /dev/null @@ -1,72 +0,0 @@ -use core::fmt::Debug; -use primitives::{Address, Bytes, TxKind, B256, GAS_PER_BLOB, U256}; -use specification::{eip2930, eip7702}; -//{AccessListItem, Address, AuthorizationList, Bytes, TxKind, B256, GAS_PER_BLOB, U256}; - -/// Trait for retrieving transaction information required for execution. -pub trait Transaction { - /// Caller aka Author aka transaction signer. - fn caller(&self) -> &Address; - /// The maximum amount of gas the transaction can use. - fn gas_limit(&self) -> u64; - /// The gas price the sender is willing to pay. - fn gas_price(&self) -> &U256; - /// Returns what kind of transaction this is. - fn kind(&self) -> TxKind; - /// The value sent to the receiver of `TxKind::Call`. - fn value(&self) -> &U256; - /// Returns the input data of the transaction. - fn data(&self) -> &Bytes; - /// The nonce of the transaction. - fn nonce(&self) -> u64; - /// The chain ID of the transaction. If set to `None`, no checks are performed. - /// - /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155]. - /// - /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 - fn chain_id(&self) -> Option; - /// A list of addresses and storage keys that the transaction plans to access. - /// - /// Added in [EIP-2930]. - /// - /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 - fn access_list(&self) -> &[eip2930::AccessListItem]; - /// The maximum priority fee per gas the sender is willing to pay. - /// - /// Incorporated as part of the London upgrade via [EIP-1559]. - /// - /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 - fn max_priority_fee_per_gas(&self) -> Option<&U256>; - /// The list of blob versioned hashes. Per EIP there should be at least - /// one blob present if [`Transaction::max_fee_per_blob_gas`] is `Some`. - /// - /// Incorporated as part of the Cancun upgrade via [EIP-4844]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - fn blob_hashes(&self) -> &[B256]; - /// The maximum fee per blob gas the sender is willing to pay. - /// - /// Incorporated as part of the Cancun upgrade via [EIP-4844]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - fn max_fee_per_blob_gas(&self) -> Option<&U256>; - /// List of authorizations, that contains the signature that authorizes this - /// caller to place the code to signer account. - /// - /// Set EOA account code for one transaction - /// - /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702) - fn authorization_list(&self) -> Option<&eip7702::AuthorizationList>; - - /// See [EIP-4844], [`crate::default::Env::calc_data_fee`], and [`crate::default::Env::calc_max_data_fee`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - fn get_total_blob_gas(&self) -> u64 { - GAS_PER_BLOB * self.blob_hashes().len() as u64 - } -} - -pub trait TransactionValidation { - /// An error that occurs when validating a transaction. - type ValidationError: Debug + core::error::Error; -} diff --git a/crates/wiring/transaction/CHANGELOG.md b/crates/wiring/transaction/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/wiring/transaction/Cargo.toml b/crates/wiring/transaction/Cargo.toml new file mode 100644 index 0000000000..5dfd154e00 --- /dev/null +++ b/crates/wiring/transaction/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "revm-transaction" +description = "Revm crate" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unreachable_pub = "warn" +unused_must_use = "deny" +rust_2018_idioms = "deny" + +[lints.rustdoc] +all = "warn" + +[dependencies] +# revm +specification.workspace = true +primitives.workspace = true + +# Optional +serde = { version = "1.0", default-features = false, features = [ + "derive", + "rc", +], optional = true } + +# mics +auto_impl = "1.2" + +[features] +default = ["std"] +std = ["serde?/std"] +serde = ["dep:serde"] +serde-json = ["serde"] diff --git a/crates/wiring/transaction/LICENSE b/crates/wiring/transaction/LICENSE new file mode 100644 index 0000000000..ad98ff22cc --- /dev/null +++ b/crates/wiring/transaction/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-2024 draganrakita + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/wiring/transaction/src/access_list.rs b/crates/wiring/transaction/src/access_list.rs new file mode 100644 index 0000000000..5a83884c02 --- /dev/null +++ b/crates/wiring/transaction/src/access_list.rs @@ -0,0 +1,35 @@ +use primitives::{Address, B256}; + +/// Access list type is introduced in EIP-2930, and every +/// transaction after it contains access list. +/// +/// Note +/// +/// Iterator over access list returns account address and storage slot keys that +/// are warm loaded before transaction execution. +/// +/// Number of account and storage slots is used to calculate initial tx gas cost. +pub trait AccessListTrait { + /// Iterate over access list. + fn iter(&self) -> impl Iterator)>; + + /// Returns number of account and storage slots. + fn num_account_storages(&self) -> (usize, usize) { + let storage_num = self.iter().map(|i| i.1.count()).sum(); + let account_num = self.iter().count(); + + (account_num, storage_num) + } +} + +// TODO move to default context +use specification::eip2930::AccessList; + +impl AccessListTrait for AccessList { + fn iter(&self) -> impl Iterator)> { + self.0.iter().map(|item| { + let slots = item.storage_keys.iter().copied(); + (item.address, slots) + }) + } +} diff --git a/crates/wiring/transaction/src/common.rs b/crates/wiring/transaction/src/common.rs new file mode 100644 index 0000000000..06fb0a2fe8 --- /dev/null +++ b/crates/wiring/transaction/src/common.rs @@ -0,0 +1,20 @@ +use primitives::{Address, Bytes, U256}; + +/// Trait that contains all common field that are shared by all transactions. +/// This trait is base for Legacy, EIp2930 and Eip1559 transactions. +pub trait CommonTxFields { + /// Caller aka Author aka transaction signer. + fn caller(&self) -> Address; + + /// The maximum amount of gas the transaction can use. + fn gas_limit(&self) -> u64; + + /// The value sent to the receiver of `TxKind::Call`. + fn value(&self) -> U256; + + /// Returns the input data of the transaction. + fn input(&self) -> &Bytes; + + /// The nonce of the transaction. + fn nonce(&self) -> u64; +} diff --git a/crates/wiring/transaction/src/eip1559.rs b/crates/wiring/transaction/src/eip1559.rs new file mode 100644 index 0000000000..b797c520cc --- /dev/null +++ b/crates/wiring/transaction/src/eip1559.rs @@ -0,0 +1,24 @@ +use crate::{AccessListTrait, CommonTxFields}; +use primitives::TxKind; + +pub trait Eip1559Tx: Eip1559CommonTxFields { + fn kind(&self) -> TxKind; +} + +/// This trait is base for Eip1559, EIp4844 and Eip7702 transactions. +pub trait Eip1559CommonTxFields: CommonTxFields { + /// Access list type. + type AccessList: AccessListTrait; + + /// Chain id became mandatory in all transaction after EIP-2930. + fn chain_id(&self) -> u64; + + /// Maximum fee per gas. + fn max_fee_per_gas(&self) -> u128; + + /// Maximum priority fee per gas. + fn max_priority_fee_per_gas(&self) -> u128; + + /// EIP-1559 access list. + fn access_list(&self) -> &Self::AccessList; +} diff --git a/crates/wiring/transaction/src/eip2930.rs b/crates/wiring/transaction/src/eip2930.rs new file mode 100644 index 0000000000..1513eefdfc --- /dev/null +++ b/crates/wiring/transaction/src/eip2930.rs @@ -0,0 +1,19 @@ +use crate::{AccessListTrait, CommonTxFields}; +use primitives::TxKind; + +/// EIP-2930: Optional access lists +pub trait Eip2930Tx: CommonTxFields { + type AccessList: AccessListTrait; + + /// The chain ID of the chain the transaction is intended for. + fn chain_id(&self) -> u64; + + /// The gas price of the transaction. + fn gas_price(&self) -> u128; + + /// The kind of transaction. + fn kind(&self) -> TxKind; + + /// The access list of the transaction. + fn access_list(&self) -> &Self::AccessList; +} diff --git a/crates/wiring/transaction/src/eip4844.rs b/crates/wiring/transaction/src/eip4844.rs new file mode 100644 index 0000000000..73b5037d93 --- /dev/null +++ b/crates/wiring/transaction/src/eip4844.rs @@ -0,0 +1,33 @@ +use crate::eip1559::Eip1559CommonTxFields; +use primitives::{Address, B256, U256}; +use specification::eip4844::GAS_PER_BLOB; + +pub trait Eip4844Tx: Eip1559CommonTxFields { + /// Call destination + fn destination(&self) -> Address; + + /// Returns vector of fixed size hash(32 bytes) + fn blob_versioned_hashes(&self) -> &[B256]; + + /// Max fee per data gas + fn max_fee_per_blob_gas(&self) -> u128; + + /// Total gas for all blobs. Max number of blocks is already checked + /// so we dont need to check for overflow. + fn total_blob_gas(&self) -> u64 { + GAS_PER_BLOB * self.blob_versioned_hashes().len() as u64 + } + + /// Calculates the maximum [EIP-4844] `data_fee` of the transaction. + /// + /// This is used for ensuring that the user has at least enough funds to pay the + /// `max_fee_per_blob_gas * total_blob_gas`, on top of regular gas costs. + /// + /// See EIP-4844: + /// + fn calc_max_data_fee(&self) -> U256 { + let blob_gas = U256::from(self.total_blob_gas()); + let max_blob_fee = U256::from(self.max_fee_per_blob_gas()); + max_blob_fee.saturating_mul(blob_gas) + } +} diff --git a/crates/wiring/transaction/src/eip7702.rs b/crates/wiring/transaction/src/eip7702.rs new file mode 100644 index 0000000000..8e67bd909a --- /dev/null +++ b/crates/wiring/transaction/src/eip7702.rs @@ -0,0 +1,108 @@ +use crate::Eip1559Tx; +use auto_impl::auto_impl; +use primitives::{Address, U256}; + +/// EIP-7702 transaction, TODO set Trait for AuthorizationList. +pub trait Eip7702Tx: Eip1559Tx { + /// Destination address of the call. + fn destination(&self) -> Address; + + /// Returns length of the authorization list. + /// + /// # Note + /// + /// Transaction is considered invalid if list is empty. + fn authorization_list_len(&self) -> usize; + + /// List of authorizations, that contains the signature that authorizes this + /// caller to place the code to signer account. + /// + /// Set EOA account code for one transaction + /// + /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702) + fn authorization_list_iter(&self) -> impl Iterator; +} + +/// Authorization trait. +#[auto_impl(&, Arc)] +pub trait Authorization { + /// Authority address. + /// + /// # Note + /// + /// Authority signature can be invalid, so this method returns None if the authority + /// could not be recovered. + /// + /// Valid signature Parity should be 0 or 1 and + /// signature s-value should be less than SECP256K1N_HALF. + fn authority(&self) -> Option
; + + /// Returns authorization the chain id. + fn chain_id(&self) -> U256; + + /// Returns the nonce. + /// + /// # Note + /// + /// If nonce is not same as the nonce of the signer account, + /// the authorization is skipped. + fn nonce(&self) -> u64; + + /// Returns the address that this account is delegated to. + fn address(&self) -> Address; + + /// Returns true if the authorization is valid. + /// + /// Temporary method needed for older EIP spec and will removed in future + /// when test get updated. + fn is_invalid(&self) -> bool; +} + +// TODO move to default context +use specification::eip7702::RecoveredAuthorization; + +impl Authorization for RecoveredAuthorization { + /// Authority address. Obtained by recovering of the signature. + fn authority(&self) -> Option
{ + self.authority() + } + + /// Returns authorization the chain id. + fn chain_id(&self) -> U256 { + self.inner().chain_id() + } + + /// Returns the nonce. + /// + /// # Note + /// + /// If nonce is not same as the nonce of the signer account, + /// authorization is skipped and considered invalidated. + fn nonce(&self) -> u64 { + self.inner().nonce() + } + + /// Returns the address that this account should delegate to. + fn address(&self) -> Address { + *self.inner().address() + } + + /// Returns true if the authorization is valid. + /// + /// Temporary method needed for older EIP spec and will removed in future + fn is_invalid(&self) -> bool { + use specification::{eip2::SECP256K1N_HALF, eip7702::Parity}; + + // Check y_parity, Parity::Parity means that it was 0 or 1. + if !matches!(self.inner().signature().v(), Parity::Parity(_)) { + return true; + } + + // Check s-value + if self.inner().signature().s() > SECP256K1N_HALF { + return true; + } + + false + } +} diff --git a/crates/wiring/transaction/src/legacy.rs b/crates/wiring/transaction/src/legacy.rs new file mode 100644 index 0000000000..3506141b1c --- /dev/null +++ b/crates/wiring/transaction/src/legacy.rs @@ -0,0 +1,15 @@ +use crate::CommonTxFields; +use primitives::TxKind; + +/// Legacy transaction trait before introduction of EIP-2929 +pub trait LegacyTx: CommonTxFields { + /// Transaction kind. + fn kind(&self) -> TxKind; + + /// Chain Id is optional for legacy transactions + /// As it was introduced in EIP-155. + fn chain_id(&self) -> Option; + + /// Gas price for the transaction. + fn gas_price(&self) -> u128; +} diff --git a/crates/wiring/transaction/src/lib.rs b/crates/wiring/transaction/src/lib.rs new file mode 100644 index 0000000000..f39217df77 --- /dev/null +++ b/crates/wiring/transaction/src/lib.rs @@ -0,0 +1,23 @@ +//! Optimism-specific constants, types, and helpers. +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(feature = "std"), no_std)] + +mod access_list; +mod common; +pub mod eip1559; +pub mod eip2930; +pub mod eip4844; +pub mod eip7702; +pub mod legacy; +pub mod transaction; +pub mod transaction_type; + +pub use access_list::AccessListTrait; +pub use common::CommonTxFields; +pub use eip1559::{Eip1559CommonTxFields, Eip1559Tx}; +pub use eip2930::Eip2930Tx; +pub use eip4844::Eip4844Tx; +pub use eip7702::Eip7702Tx; +pub use legacy::LegacyTx; +pub use transaction::{Transaction, TransactionError}; +pub use transaction_type::TransactionType; diff --git a/crates/wiring/transaction/src/transaction.rs b/crates/wiring/transaction/src/transaction.rs new file mode 100644 index 0000000000..8f241b6e7d --- /dev/null +++ b/crates/wiring/transaction/src/transaction.rs @@ -0,0 +1,135 @@ +use crate::{ + eip1559::Eip1559CommonTxFields, AccessListTrait, CommonTxFields, Eip1559Tx, Eip2930Tx, + Eip4844Tx, Eip7702Tx, LegacyTx, TransactionType, +}; +use core::cmp::min; +use core::fmt::Debug; +use primitives::{TxKind, U256}; + +/// Transaction validity error types. +pub trait TransactionError: Debug + core::error::Error {} + +/// Main Transaction trait that abstracts and specifies all transaction currently supported by Ethereum. +/// +/// Access to any associated type is gaited behind `tx_type` function. +/// +/// It can be extended to support new transaction types and only transaction types can be +/// deprecated by not returning tx_type. +pub trait Transaction { + /// An error that occurs when validating a transaction. + type TransactionError: TransactionError; + /// Transaction type. + type TransactionType: Into; + /// Access list type. + type AccessList: AccessListTrait; + + type Legacy: LegacyTx; + type Eip2930: Eip2930Tx; + type Eip1559: Eip1559Tx; + type Eip4844: Eip4844Tx; + type Eip7702: Eip7702Tx; + + /// Transaction type. Depending on this field other functions should be called. + /// If transaction is Legacy, then `legacy()` should be called. + fn tx_type(&self) -> Self::TransactionType; + + /// Legacy transaction. + fn legacy(&self) -> &Self::Legacy { + unimplemented!("legacy tx not supported") + } + + /// EIP-2930 transaction. + fn eip2930(&self) -> &Self::Eip2930 { + unimplemented!("Eip2930 tx not supported") + } + + /// EIP-1559 transaction. + fn eip1559(&self) -> &Self::Eip1559 { + unimplemented!("Eip1559 tx not supported") + } + + /// EIP-4844 transaction. + fn eip4844(&self) -> &Self::Eip4844 { + unimplemented!("Eip4844 tx not supported") + } + + /// EIP-7702 transaction. + fn eip7702(&self) -> &Self::Eip7702 { + unimplemented!("Eip7702 tx not supported") + } + + /// Common fields for all transactions. + fn common_fields(&self) -> &dyn CommonTxFields { + match self.tx_type().into() { + TransactionType::Legacy => self.legacy(), + TransactionType::Eip2930 => self.eip2930(), + TransactionType::Eip1559 => self.eip1559(), + TransactionType::Eip4844 => self.eip4844(), + TransactionType::Eip7702 => self.eip7702(), + TransactionType::Custom => unimplemented!("Custom tx not supported"), + } + } + + /// Maximum fee that can be paid for the transaction. + fn max_fee(&self) -> u128 { + match self.tx_type().into() { + TransactionType::Legacy => self.legacy().gas_price(), + TransactionType::Eip2930 => self.eip2930().gas_price(), + TransactionType::Eip1559 => self.eip1559().max_fee_per_gas(), + TransactionType::Eip4844 => self.eip4844().max_fee_per_gas(), + TransactionType::Eip7702 => self.eip7702().max_fee_per_gas(), + TransactionType::Custom => unimplemented!("Custom tx not supported"), + } + } + + /// Effective gas price is gas price field for Legacy and Eip2930 transaction + /// While for transactions after Eip1559 it is minimum of max_fee and base+max_priority_fee. + fn effective_gas_price(&self, base_fee: U256) -> U256 { + let tx_type = self.tx_type().into(); + let (max_fee, max_priority_fee) = match tx_type { + TransactionType::Legacy => return U256::from(self.legacy().gas_price()), + TransactionType::Eip2930 => return U256::from(self.eip2930().gas_price()), + TransactionType::Eip1559 => ( + self.eip1559().max_fee_per_gas(), + self.eip1559().max_priority_fee_per_gas(), + ), + TransactionType::Eip4844 => ( + self.eip4844().max_fee_per_gas(), + self.eip4844().max_priority_fee_per_gas(), + ), + TransactionType::Eip7702 => ( + self.eip7702().max_fee_per_gas(), + self.eip7702().max_priority_fee_per_gas(), + ), + TransactionType::Custom => unimplemented!("Custom tx not supported"), + }; + + min(U256::from(max_fee), base_fee + U256::from(max_priority_fee)) + } + + /// Transaction kind. + fn kind(&self) -> TxKind { + let tx_type = self.tx_type().into(); + match tx_type { + TransactionType::Legacy => self.legacy().kind(), + TransactionType::Eip2930 => self.eip2930().kind(), + TransactionType::Eip1559 => self.eip1559().kind(), + TransactionType::Eip4844 => TxKind::Call(self.eip4844().destination()), + TransactionType::Eip7702 => TxKind::Call(self.eip7702().destination()), + TransactionType::Custom => unimplemented!("Custom tx not supported"), + } + } + + /// Returns access list. + fn access_list(&self) -> Option<&Self::AccessList> { + let tx_type = self.tx_type().into(); + match tx_type { + TransactionType::Legacy => None, + TransactionType::Eip2930 => Some(self.eip2930().access_list()), + TransactionType::Eip1559 => Some(self.eip1559().access_list()), + TransactionType::Eip4844 => Some(self.eip4844().access_list()), + TransactionType::Eip7702 => Some(self.eip7702().access_list()), + TransactionType::Custom => unimplemented!("Custom tx not supported"), + } + } +} diff --git a/crates/wiring/transaction/src/transaction_type.rs b/crates/wiring/transaction/src/transaction_type.rs new file mode 100644 index 0000000000..ef1d270d43 --- /dev/null +++ b/crates/wiring/transaction/src/transaction_type.rs @@ -0,0 +1,18 @@ +/// Transaction types of all Ethereum transaction. + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum TransactionType { + /// Legacy transaction type. + Legacy, + /// EIP-2930 Access List transaction type. + Eip2930, + /// EIP-1559 Fee market change transaction type. + Eip1559, + /// EIP-4844 Blob transaction type. + Eip4844, + /// EIP-7702 Set EOA account code transaction type. + Eip7702, + /// Custom type means that transaction trait was extend and have custom types. + Custom, +} diff --git a/examples/block_traces/src/main.rs b/examples/block_traces/src/main.rs index df6846e63b..a02ccfbc34 100644 --- a/examples/block_traces/src/main.rs +++ b/examples/block_traces/src/main.rs @@ -8,8 +8,7 @@ use indicatif::ProgressBar; use inspector::{inspector_handle_register, inspectors::TracerEip3155}; use revm::{ database_interface::WrapDatabaseAsync, - primitives::{TxKind, B256, U256}, - specification::eip2930::AccessListItem, + primitives::{TxKind, U256}, wiring::EthereumWiring, Evm, }; @@ -123,22 +122,7 @@ async fn main() -> anyhow::Result<()> { etx.chain_id = Some(chain_id); etx.nonce = tx.nonce; if let Some(access_list) = tx.access_list { - etx.access_list = access_list - .0 - .into_iter() - .map(|item| { - let storage_keys: Vec = item - .storage_keys - .into_iter() - .map(|h256| B256::new(h256.0)) - .collect(); - - AccessListItem { - address: item.address, - storage_keys, - } - }) - .collect(); + etx.access_list = access_list; } else { etx.access_list = Default::default(); }