From 8a6a2b5a81e72c301604c1acaa1aee1400924b09 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 11 Sep 2024 03:20:24 +0400 Subject: [PATCH 01/13] wip --- Cargo.lock | 290 ++++++++++----------- Cargo.toml | 28 ++ crates/rpc/rpc-eth-api/src/core.rs | 8 +- crates/rpc/rpc-eth-api/src/helpers/call.rs | 176 +++++++++++-- crates/rpc/rpc-eth-types/src/error.rs | 5 +- crates/rpc/rpc-eth-types/src/revm_utils.rs | 13 +- crates/rpc/rpc/src/debug.rs | 2 +- 7 files changed, 345 insertions(+), 177 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7059fbf00747..54fc26273d78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,13 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -118,8 +112,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28ddd17ffb7e4d66ef3a84e7b179072a9320cdc4b26c7f6f44cbf1081631b36" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-eips", "alloy-primitives", @@ -132,9 +125,9 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03f58cfae4d41b624effe1f11624ee40499449174b20a6d6505fd72ef0d547d" +checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -178,8 +171,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f6c5c0a383f14519531cf58d8440e74f10b938e289f803af870be6f79223110" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -197,8 +189,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db0ddc76399bb1a4010f630767f027cafe65ab406cfee8e6040128cd65e8325" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "alloy-serde", @@ -207,9 +198,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ecae8b5315daecd0075084eb47f4374b3037777346ca52fc8d9c327693f02" +checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -220,8 +211,7 @@ dependencies = [ [[package]] name = "alloy-json-rpc" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7111af869909275cffc5c84d16b6c892d6d512773e40cbe83187d0b9c5235e91" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -234,8 +224,7 @@ dependencies = [ [[package]] name = "alloy-network" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342028392a2d5050b7b93dd32a0715d3b3b9ce30072ecb69a35dd4895c005495" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -255,8 +244,7 @@ dependencies = [ [[package]] name = "alloy-network-primitives" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e66d78c049dcadd065a926a9f2d9a9b2b10981a7889449e694fac7bccd2c6f" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-eips", "alloy-primitives", @@ -267,8 +255,7 @@ dependencies = [ [[package]] name = "alloy-node-bindings" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41eb6fd2c70b076b0953d05ff1d2814fc370629301676a578519f8769cc2bc82" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -283,9 +270,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb865df835f851b367ae439d6c82b117ded971628c8888b24fed411a290e38a" +checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" dependencies = [ "alloy-rlp", "arbitrary", @@ -310,8 +297,7 @@ dependencies = [ [[package]] name = "alloy-provider" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f14ccc2a3c575cb17b1b4af8c772cf9b5b93b7ce7047d6640e53954abb558d" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-chains", "alloy-consensus", @@ -348,8 +334,7 @@ dependencies = [ [[package]] name = "alloy-pubsub" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b9f5e85120aab30b8da23354592f7bd2b208d33d3204ffa1d44ac2e3dd5691" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -389,8 +374,7 @@ dependencies = [ [[package]] name = "alloy-rpc-client" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc79aeca84abb122a2fffbc1c91fdf958dca5c95be3875977bc99672bde0027" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -413,8 +397,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22045187a5ebf5b2af3f8b6831b66735b6556c5750ec5790aeeb45935260c1c2" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", @@ -425,8 +408,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-admin" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ea9f282cd1ad5c30d5ed26c4c2ddf4e48403b7322941546d6ec86545312b07" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -437,8 +419,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578d9ccad4e8510d32cc2810d05e01a232ccd79a4a6df60d257466897b43b013" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "alloy-serde", @@ -448,8 +429,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3005edeaea06e916f4d723e7e275bef44decfb1947438679edbb5b64dd8789" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-eips", "alloy-primitives", @@ -462,8 +442,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754e6d7fcb0973611c23a7588cb6adf6887a3a95c079dc733d2edeb5c7e6c62" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "serde", @@ -472,8 +451,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c031a91e94a39f928244bc837c953817be5b8cc61759e1a9123b3abd17560dd" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -490,8 +468,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238f494727ff861a803bd73b1274ef788a615bf8f8c4bfada4e6df42afa275d2" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -513,8 +490,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-mev" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb6b5f521b1dc2a1517b46a85b43310bb7bacbf0a2cbea6a574d591c41db31c" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-eips", "alloy-primitives", @@ -526,8 +502,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca08b0ccc0861055ceb83a1db009c4c8a7f52a259e7cda7ca6ca36ec2b5ce8" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -540,8 +515,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "192ad94fe34c12be8ac4413ea00b1170202faa6fdebaa756b6a33555bf86d902" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -552,8 +526,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b95b6f024a558593dd3b8628af03f7df2ca50e4c56839293ad0a7546e471db0" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "arbitrary", @@ -564,8 +537,7 @@ dependencies = [ [[package]] name = "alloy-signer" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da64740ff0518606c514eb0e03dd0a1daa8ff94d6d491a626fd8e50efd6c4f18" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-primitives", "async-trait", @@ -578,8 +550,7 @@ dependencies = [ [[package]] name = "alloy-signer-local" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e250010dce0e3caf6a6033e809718e5921391d937d1cbbcffe52653b37cc63" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-consensus", "alloy-network", @@ -595,9 +566,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2dc5201ca0018afb7a3e0cd8bd15f7ca6aca924333b5f3bb87463b41d0c4ef2" +checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -609,13 +580,13 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155f63dc6945885aa4532601800201fddfaa3b20901fda8e8c2570327242fe0e" +checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" dependencies = [ "alloy-sol-macro-input", "const-hex", - "heck 0.5.0", + "heck", "indexmap 2.5.0", "proc-macro-error2", "proc-macro2", @@ -627,13 +598,13 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847700aa9cb59d3c7b290b2d05976cd8d76b64d73bb63116a9533132d995586b" +checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" dependencies = [ "const-hex", "dunce", - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.77", @@ -642,9 +613,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6b5d462d4520bd9ed70d8364c6280aeff13baa46ea26be1ddd33538dbbe6ac" +checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" dependencies = [ "serde", "winnow", @@ -652,9 +623,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83665e5607725a7a1aab3cb0dea708f4a05e70776954ec7f0a9461439175c957" +checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -666,8 +637,7 @@ dependencies = [ [[package]] name = "alloy-transport" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7a669caa427abe8802184c8776f5103302f9337bb30a5b36bdebc332946c14" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -685,8 +655,7 @@ dependencies = [ [[package]] name = "alloy-transport-http" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4433ffa97aab6ae643de81c7bde9a2f043496f27368a607405a5c78a610caf74" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -700,8 +669,7 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa02db8751f9c0c37caf8c38ad3eb7aa1cfb09cfea3451a13aacaf06846c7a5" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -719,8 +687,7 @@ dependencies = [ [[package]] name = "alloy-transport-ws" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0a5c4a0929479bcb85a2df628c01173618a71c807b2f499939a236dbde5d008" +source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -1133,17 +1100,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -1733,7 +1700,7 @@ version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn 2.0.77", @@ -2608,11 +2575,11 @@ dependencies = [ [[package]] name = "enum-as-inner" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "syn 2.0.77", @@ -3054,7 +3021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -3273,9 +3240,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "glob" @@ -3417,12 +3384,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -3643,9 +3604,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", @@ -4044,9 +4005,9 @@ checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "iri-string" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f5f6c2df22c009ac44f6f1499308e7a3ac7ba42cd2378475cc691510e1eef1b" +checksum = "5f80d71a680f471ab23df28c5cb3e6a5598f96ab5570a2e320b73a44c46db440" dependencies = [ "memchr", "serde", @@ -4241,7 +4202,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b07a2daf52077ab1b197aea69a5c990c060143835bf04c77070e98903791715" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro-crate", "proc-macro2", "quote", @@ -4351,9 +4312,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422fbc7ff2f2f5bdffeb07718e5a5324dca72b0c9293d50df4026652385e3314" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -4422,7 +4383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -4748,15 +4709,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -5119,9 +5071,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a0004dfb630414d0dbed58b61a943740ae829def89f261cbd0d91d920c21bba" +checksum = "3ef361231f72bea90365e441bd97d9c8fd3875603f18bbcad0e04874c6158e53" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5136,9 +5088,9 @@ dependencies = [ [[package]] name = "op-alloy-network" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a0d85993996f82ffc14f3a83deaa390fb4b27623ba83f70e4163f68a7cedbd8" +checksum = "411e1fe4c2d6ea0001523d35cf37d3d4d79d9f86497c2c059fb2f7a0f6b5c015" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5149,11 +5101,28 @@ dependencies = [ "op-alloy-rpc-types", ] +[[package]] +name = "op-alloy-protocol" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8340ab59cffa611baeb048ba7e304cf3a99fd759dc516705a66c986503d87f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "hashbrown 0.14.5", + "op-alloy-consensus", + "serde", + "superchain-primitives", +] + [[package]] name = "op-alloy-rpc-types" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219dfbe7a20588f172ae9cfe2ec7e2804f71816f24355dc07fd9a1453c62f07f" +checksum = "e5a65da5f7591acba3d2304b25145ca9942ea90481213269aed9cb28add8e3ff" dependencies = [ "alloy-eips", "alloy-network", @@ -5167,13 +5136,14 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7105d398120c9ee788b86049506c513de7dfc4d48b3cefcffc88d7d4f4ec29a" +checksum = "16990a17db7109eed0692ee16443582bd363fb9cf2768af2bf92c08f56ae4f3e" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", "alloy-serde", + "op-alloy-protocol", "serde", ] @@ -5480,9 +5450,9 @@ dependencies = [ [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -5493,15 +5463,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -8963,9 +8933,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d37cf496100c6ff1fb7de04a0e05318b7f36b36aec54054bdeeb3346fb2abeb" +checksum = "02b2198350ddee1744cc99812fb39175daf6960af26c1dcfb26ec853c1937d9e" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -9212,9 +9182,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "log", "once_cell", @@ -9296,9 +9266,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.102.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -9346,9 +9316,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.16" +version = "2.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb7ac86243095b70a7920639507b71d51a63390d1ba26c4f60a552fbb914a37" +checksum = "0c947adb109a8afce5fc9c7bf951f87f146e9147b3a6a58413105628fb1d1e66" dependencies = [ "sdd", ] @@ -9387,9 +9357,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f" +checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc" [[package]] name = "sec1" @@ -9532,6 +9502,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "serde_spanned" version = "0.6.7" @@ -9655,9 +9636,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d79b758b7cb2085612b11a235055e485605a5103faccdd633f35bd7aee69dd" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", "cfg-if", @@ -9906,7 +9887,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "rustversion", @@ -9932,6 +9913,23 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "superchain-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d0cd81318c6ad65932af03168da2095befebef0a851f17391fc773dbf49dbc" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-serde", + "alloy-sol-types", + "anyhow", + "serde", + "serde_repr", +] + [[package]] name = "symbolic-common" version = "12.11.0" @@ -9979,9 +9977,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e1355d44af21638c8e05d45097db6cb5ec2aa3e970c51cb2901605cf3344fa" +checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" dependencies = [ "paste", "proc-macro2", @@ -10767,9 +10765,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -11075,7 +11073,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e971fecc703a..d7851b551d73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -583,3 +583,31 @@ test-fuzz = "5" tikv-jemalloc-ctl = "0.6" tikv-jemallocator = "0.6" tracy-client = "0.17.3" + +[patch.crates-io] +alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} diff --git a/crates/rpc/rpc-eth-api/src/core.rs b/crates/rpc/rpc-eth-api/src/core.rs index c2103857aa1e..114a84cd22d4 100644 --- a/crates/rpc/rpc-eth-api/src/core.rs +++ b/crates/rpc/rpc-eth-api/src/core.rs @@ -9,7 +9,7 @@ use reth_primitives::{transaction::AccessListResult, BlockId, BlockNumberOrTag}; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; use reth_rpc_types::{ serde_helpers::JsonStorageKey, - simulate::{SimBlock, SimulatedBlock}, + simulate::{SimulatePayload, SimulatedBlock}, state::{EvmOverrides, StateOverride}, AnyTransactionReceipt, BlockOverrides, Bundle, EIP1186AccountProofResponse, EthCallResponse, FeeHistory, Header, Index, StateContext, SyncStatus, TransactionRequest, Work, @@ -211,7 +211,7 @@ pub trait EthApi { #[method(name = "simulateV1")] async fn simulate_v1( &self, - opts: SimBlock, + opts: SimulatePayload, block_number: Option, ) -> RpcResult>; @@ -618,11 +618,11 @@ where /// Handler for: `eth_simulateV1` async fn simulate_v1( &self, - opts: SimBlock, + payload: SimulatePayload, block_number: Option, ) -> RpcResult> { trace!(target: "rpc::eth", ?block_number, "Serving eth_simulateV1"); - Ok(EthCall::simulate_v1(self, opts, block_number).await?) + Ok(EthCall::simulate_v1(self, payload, block_number).await?) } /// Handler for: `eth_call` diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index d32f0104b33f..8feb0612feec 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -28,10 +28,12 @@ use reth_rpc_server_types::constants::gas_oracle::{ CALL_STIPEND_GAS, ESTIMATE_GAS_ERROR_RATIO, MIN_TRANSACTION_GAS, }; use reth_rpc_types::{ - simulate::{SimBlock, SimulatedBlock}, + simulate::{SimBlock, SimCallResult, SimulateError, SimulatePayload, SimulatedBlock}, state::{EvmOverrides, StateOverride}, - BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo, TransactionRequest, + Block, BlockId, Bundle, EthCallResponse, Log, StateContext, TransactionInfo, + TransactionRequest, }; +use reth_rpc_types_compat::block::from_primitive_with_hash; use revm::{Database, DatabaseCommit}; use revm_inspectors::access_list::AccessListInspector; use tracing::trace; @@ -57,10 +59,147 @@ pub trait EthCall: Call + LoadPendingBlock { /// See also: fn simulate_v1( &self, - _opts: SimBlock, - _block_number: Option, - ) -> impl Future, Self::Error>> + Send { - async move { Err(EthApiError::Unsupported("eth_simulateV1 is not supported.").into()) } + payload: SimulatePayload, + block: Option, + ) -> impl Future, Self::Error>> + Send + where + Self: LoadBlock, + { + async move { + let SimulatePayload { + block_state_calls, + trace_transfers, + validation, + return_full_transactions, + } = payload; + + if block_state_calls.is_empty() { + return Err(EthApiError::InvalidParams(String::from("calls are empty.")).into()) + } + + let (cfg, mut block_env, block) = self.evm_env_at(block.unwrap_or_default()).await?; + let gas_limit = self.call_gas_limit(); + let mut parent_hash = + self.block(block).await?.map(|block| block.hash()).unwrap_or_default(); + + let this = self.clone(); + self.spawn_with_state_at_block(block, move |state| { + let mut db = CacheDB::new(StateProviderDatabase::new(state)); + let mut blocks = Vec::with_capacity(block_state_calls.len()); + + for block in block_state_calls { + let SimBlock { block_overrides, state_overrides, calls } = block; + + if let Some(block_overrides) = block_overrides { + apply_block_overrides(block_overrides, &mut db, &mut block_env); + } + if let Some(state_overrides) = state_overrides { + apply_state_overrides(state_overrides, &mut db)?; + } + + let mut transactions = calls.into_iter().peekable(); + let mut calls = Vec::with_capacity(transactions.len()); + + let mut log_index = 0; + while let Some(tx) = transactions.next() { + let env = this.prepare_call_env( + cfg.clone(), + block_env.clone(), + tx, + gas_limit, + &mut db, + None, + )?; + + let (res, _) = this.transact(&mut db, env)?; + + match res.result { + ExecutionResult::Halt { reason, gas_used } => { + let error = RpcInvalidTransactionError::halt(reason, gas_limit); + calls.push(SimCallResult { + return_value: Bytes::new(), + error: Some(SimulateError { + code: error.error_code(), + message: error.to_string(), + }), + gas_used, + logs: Vec::new(), + status: false, + }); + } + ExecutionResult::Revert { output, gas_used } => { + let error = RevertError::new(output.clone()); + calls.push(SimCallResult { + return_value: output, + error: Some(SimulateError { + code: error.error_code(), + message: error.to_string(), + }), + gas_used, + status: false, + logs: Vec::new(), + }); + } + ExecutionResult::Success { output, gas_used, logs, .. } => { + calls.push(SimCallResult { + return_value: output.into_data(), + error: None, + gas_used, + logs: logs + .into_iter() + .map(|log| { + log_index += 1; + Log { + inner: log, + log_index: Some(log_index - 1), + transaction_index: Some(calls.len() as u64), + ..Default::default() + } + }) + .collect(), + status: true, + }); + } + }; + + if transactions.peek().is_some() { + // need to apply the state changes of this call before executing the + // next call + db.commit(res.state); + } + } + + let header = reth_primitives::Header { + beneficiary: block_env.coinbase, + difficulty: block_env.difficulty, + number: block_env.number.to(), + timestamp: block_env.timestamp.to(), + base_fee_per_gas: Some(block_env.basefee.to()), + gas_used: calls.iter().map(|tx| tx.gas_used).sum(), + blob_gas_used: Some(0), + parent_hash, + gas_limit, + ..Default::default() + }; + + let block = SimulatedBlock { + inner: Block { + header: from_primitive_with_hash(header.seal_slow()), + ..Default::default() + }, + calls, + }; + + parent_hash = block.inner.header.hash; + block_env.number += U256::from(1); + + blocks.push(block); + } + + Ok(blocks) + }) + .await + } } /// Executes the call request (`eth_call`) and returns the output @@ -162,7 +301,7 @@ pub trait EthCall: Call + LoadPendingBlock { tx, gas_limit, &mut db, - overrides, + Some(overrides), ) .map(Into::into)?; let (res, _) = this.transact(&mut db, env)?; @@ -398,7 +537,7 @@ pub trait Call: LoadState + SpawnBlocking { request, this.call_gas_limit(), &mut db, - overrides, + Some(overrides), )?; f(StateCacheDbRefMutWrapper(&mut db), env) @@ -935,7 +1074,7 @@ pub trait Call: LoadState + SpawnBlocking { mut request: TransactionRequest, gas_limit: u64, db: &mut CacheDB, - overrides: EvmOverrides, + overrides: Option, ) -> Result where DB: DatabaseRef, @@ -957,25 +1096,18 @@ pub trait Call: LoadState + SpawnBlocking { // set nonce to None so that the correct nonce is chosen by the EVM request.nonce = None; - // apply block overrides, we need to apply them first so that they take effect when we we - // create the evm env via `build_call_evm_env`, e.g. basefee - if let Some(mut block_overrides) = overrides.block { - if let Some(block_hashes) = block_overrides.block_hash.take() { - // override block hashes - db.block_hashes - .extend(block_hashes.into_iter().map(|(num, hash)| (U256::from(num), hash))) + if let Some(overrides) = overrides { + if let Some(block_overrides) = overrides.block { + apply_block_overrides(*block_overrides, db, &mut block); + } + if let Some(state_overrides) = overrides.state { + apply_state_overrides(state_overrides, db)?; } - apply_block_overrides(*block_overrides, &mut block); } let request_gas = request.gas; let mut env = self.build_call_evm_env(cfg, block, request)?; - // apply state overrides - if let Some(state_overrides) = overrides.state { - apply_state_overrides(state_overrides, db)?; - } - if request_gas.is_none() { // No gas limit was provided in the request, so we need to cap the transaction gas limit if env.tx.gas_price > U256::ZERO { diff --git a/crates/rpc/rpc-eth-types/src/error.rs b/crates/rpc/rpc-eth-types/src/error.rs index f38cb6ee412c..2ee9592524cd 100644 --- a/crates/rpc/rpc-eth-types/src/error.rs +++ b/crates/rpc/rpc-eth-types/src/error.rs @@ -417,7 +417,7 @@ impl RpcInvalidTransactionError { impl RpcInvalidTransactionError { /// Returns the rpc error code for this error. - const fn error_code(&self) -> i32 { + pub const fn error_code(&self) -> i32 { match self { Self::InvalidChainId | Self::GasTooLow | Self::GasTooHigh => { EthRpcErrorCode::InvalidInput.code() @@ -567,7 +567,8 @@ impl RevertError { } } - const fn error_code(&self) -> i32 { + /// Returns error code to return for this error. + pub const fn error_code(&self) -> i32 { EthRpcErrorCode::ExecutionError.code() } } diff --git a/crates/rpc/rpc-eth-types/src/revm_utils.rs b/crates/rpc/rpc-eth-types/src/revm_utils.rs index bfcc2bf42062..7d24046eb129 100644 --- a/crates/rpc/rpc-eth-types/src/revm_utils.rs +++ b/crates/rpc/rpc-eth-types/src/revm_utils.rs @@ -203,7 +203,11 @@ impl CallFees { } /// Applies the given block overrides to the env -pub fn apply_block_overrides(overrides: BlockOverrides, env: &mut BlockEnv) { +pub fn apply_block_overrides( + overrides: BlockOverrides, + db: &mut CacheDB, + env: &mut BlockEnv, +) { let BlockOverrides { number, difficulty, @@ -212,9 +216,14 @@ pub fn apply_block_overrides(overrides: BlockOverrides, env: &mut BlockEnv) { coinbase, random, base_fee, - block_hash: _, + block_hash, } = overrides; + if let Some(block_hashes) = block_hash { + // override block hashes + db.block_hashes.extend(block_hashes.into_iter().map(|(num, hash)| (U256::from(num), hash))) + } + if let Some(number) = number { env.number = number; } diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 95af81a7bd86..29191792383c 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -536,7 +536,7 @@ where tx, gas_limit, &mut db, - overrides, + Some(overrides), )?; let (trace, state) = From 5a9a374ae43084034c301e5ccbe7950611aee90b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 11 Sep 2024 12:27:40 +0400 Subject: [PATCH 02/13] wip --- Cargo.lock | 1 + crates/rpc/rpc-eth-api/src/core.rs | 9 +- crates/rpc/rpc-eth-api/src/helpers/call.rs | 184 +++++++------ crates/rpc/rpc-eth-types/Cargo.toml | 1 + crates/rpc/rpc-eth-types/src/lib.rs | 1 + crates/rpc/rpc-eth-types/src/simulate.rs | 292 +++++++++++++++++++++ 6 files changed, 390 insertions(+), 98 deletions(-) create mode 100644 crates/rpc/rpc-eth-types/src/simulate.rs diff --git a/Cargo.lock b/Cargo.lock index 54fc26273d78..6df1b23403a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8445,6 +8445,7 @@ dependencies = [ name = "reth-rpc-eth-types" version = "1.0.6" dependencies = [ + "alloy-consensus", "alloy-primitives", "alloy-sol-types", "derive_more", diff --git a/crates/rpc/rpc-eth-api/src/core.rs b/crates/rpc/rpc-eth-api/src/core.rs index 114a84cd22d4..bf4fb9ea3755 100644 --- a/crates/rpc/rpc-eth-api/src/core.rs +++ b/crates/rpc/rpc-eth-api/src/core.rs @@ -11,8 +11,9 @@ use reth_rpc_types::{ serde_helpers::JsonStorageKey, simulate::{SimulatePayload, SimulatedBlock}, state::{EvmOverrides, StateOverride}, - AnyTransactionReceipt, BlockOverrides, Bundle, EIP1186AccountProofResponse, EthCallResponse, - FeeHistory, Header, Index, StateContext, SyncStatus, TransactionRequest, Work, + AnyTransactionReceipt, Block, BlockOverrides, Bundle, EIP1186AccountProofResponse, + EthCallResponse, FeeHistory, Header, Index, StateContext, SyncStatus, TransactionRequest, + WithOtherFields, Work, }; use tracing::trace; @@ -213,7 +214,7 @@ pub trait EthApi { &self, opts: SimulatePayload, block_number: Option, - ) -> RpcResult>; + ) -> RpcResult>>; /// Executes a new message call immediately without creating a transaction on the block chain. #[method(name = "call")] @@ -620,7 +621,7 @@ where &self, payload: SimulatePayload, block_number: Option, - ) -> RpcResult> { + ) -> RpcResult>>>> { trace!(target: "rpc::eth", ?block_number, "Serving eth_simulateV1"); Ok(EthCall::simulate_v1(self, payload, block_number).await?) } diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 8feb0612feec..ecbd3d239d77 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -13,7 +13,7 @@ use reth_primitives::{ transaction::AccessListResult, TransactionSignedEcRecovered, }; -use reth_provider::{ChainSpecProvider, StateProvider}; +use reth_provider::{ChainSpecProvider, HeaderProvider, StateProvider}; use reth_revm::{database::StateProviderDatabase, db::CacheDB, DatabaseRef}; use reth_rpc_eth_types::{ cache::db::{StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper}, @@ -22,19 +22,19 @@ use reth_rpc_eth_types::{ apply_block_overrides, apply_state_overrides, caller_gas_allowance, cap_tx_gas_limit_with_caller_allowance, get_precompiles, CallFees, }, + simulate::{self, EthSimulateError, TransferInspector}, EthApiError, RevertError, RpcInvalidTransactionError, StateCacheDb, }; use reth_rpc_server_types::constants::gas_oracle::{ CALL_STIPEND_GAS, ESTIMATE_GAS_ERROR_RATIO, MIN_TRANSACTION_GAS, }; use reth_rpc_types::{ - simulate::{SimBlock, SimCallResult, SimulateError, SimulatePayload, SimulatedBlock}, + simulate::{SimBlock, SimulatePayload, SimulatedBlock}, state::{EvmOverrides, StateOverride}, - Block, BlockId, Bundle, EthCallResponse, Log, StateContext, TransactionInfo, - TransactionRequest, + Block, BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo, TransactionRequest, + WithOtherFields, }; -use reth_rpc_types_compat::block::from_primitive_with_hash; -use revm::{Database, DatabaseCommit}; +use revm::{Database, DatabaseCommit, GetInspector}; use revm_inspectors::access_list::AccessListInspector; use tracing::trace; @@ -61,7 +61,12 @@ pub trait EthCall: Call + LoadPendingBlock { &self, payload: SimulatePayload, block: Option, - ) -> impl Future, Self::Error>> + Send + ) -> impl Future< + Output = Result< + Vec>>>, + Self::Error, + >, + > + Send where Self: LoadBlock, { @@ -77,18 +82,36 @@ pub trait EthCall: Call + LoadPendingBlock { return Err(EthApiError::InvalidParams(String::from("calls are empty.")).into()) } - let (cfg, mut block_env, block) = self.evm_env_at(block.unwrap_or_default()).await?; - let gas_limit = self.call_gas_limit(); - let mut parent_hash = - self.block(block).await?.map(|block| block.hash()).unwrap_or_default(); + // Build cfg and block env, we'll reuse those. + let (mut cfg, mut block_env, block) = + self.evm_env_at(block.unwrap_or_default()).await?; + + // Gas cap for entire operation + let total_gas_limit = self.call_gas_limit() as u128; + + let base_block = self.block(block).await?.ok_or(EthApiError::HeaderNotFound(block))?; + let mut parent_hash = base_block.header.hash(); + let total_difficulty = LoadPendingBlock::provider(self) + .header_td_by_number(block_env.number.to()) + .map_err(Self::Error::from_eth_err)? + .ok_or(EthApiError::HeaderNotFound(block))?; + + // Only enforce base fee if validation is enabled + cfg.disable_base_fee = !validation; + // Always disable EIP-3607 + cfg.disable_eip3607 = true; let this = self.clone(); self.spawn_with_state_at_block(block, move |state| { let mut db = CacheDB::new(StateProviderDatabase::new(state)); let mut blocks = Vec::with_capacity(block_state_calls.len()); - + let mut gas_used = 0; for block in block_state_calls { - let SimBlock { block_overrides, state_overrides, calls } = block; + // Increase number and timestamp for every new block + block_env.number += U256::from(1); + block_env.timestamp += U256::from(1); + + let SimBlock { block_overrides, state_overrides, mut calls } = block; if let Some(block_overrides) = block_overrides { apply_block_overrides(block_overrides, &mut db, &mut block_env); @@ -97,101 +120,55 @@ pub trait EthCall: Call + LoadPendingBlock { apply_state_overrides(state_overrides, &mut db)?; } - let mut transactions = calls.into_iter().peekable(); - let mut calls = Vec::with_capacity(transactions.len()); + if (total_gas_limit - gas_used) < block_env.gas_limit.to() { + return Err( + EthApiError::Other(Box::new(EthSimulateError::GasLimitReached)).into() + ) + } - let mut log_index = 0; - while let Some(tx) = transactions.next() { - let env = this.prepare_call_env( - cfg.clone(), - block_env.clone(), - tx, - gas_limit, - &mut db, - None, - )?; + // Resolve transactions, populate missing fields and enforce calls correctness. + let transactions = simulate::resolve_transactions( + &mut calls, + validation, + block_env.gas_limit.to(), + cfg.chain_id, + &mut db, + )?; - let (res, _) = this.transact(&mut db, env)?; + let mut calls = calls.into_iter().peekable(); + let mut results = Vec::with_capacity(calls.len()); - match res.result { - ExecutionResult::Halt { reason, gas_used } => { - let error = RpcInvalidTransactionError::halt(reason, gas_limit); - calls.push(SimCallResult { - return_value: Bytes::new(), - error: Some(SimulateError { - code: error.error_code(), - message: error.to_string(), - }), - gas_used, - logs: Vec::new(), - status: false, - }); - } - ExecutionResult::Revert { output, gas_used } => { - let error = RevertError::new(output.clone()); - calls.push(SimCallResult { - return_value: output, - error: Some(SimulateError { - code: error.error_code(), - message: error.to_string(), - }), - gas_used, - status: false, - logs: Vec::new(), - }); - } - ExecutionResult::Success { output, gas_used, logs, .. } => { - calls.push(SimCallResult { - return_value: output.into_data(), - error: None, - gas_used, - logs: logs - .into_iter() - .map(|log| { - log_index += 1; - Log { - inner: log, - log_index: Some(log_index - 1), - transaction_index: Some(calls.len() as u64), - ..Default::default() - } - }) - .collect(), - status: true, - }); + while let Some(tx) = calls.next() { + let env = this.build_call_evm_env(cfg.clone(), block_env.clone(), tx)?; + + let (res, env) = { + if trace_transfers { + this.transact_with_inspector(&mut db, env, TransferInspector)? + } else { + this.transact(&mut db, env)? } }; - if transactions.peek().is_some() { + if calls.peek().is_some() { // need to apply the state changes of this call before executing the // next call db.commit(res.state); } + + results.push((env.tx.caller, res.result)); } - let header = reth_primitives::Header { - beneficiary: block_env.coinbase, - difficulty: block_env.difficulty, - number: block_env.number.to(), - timestamp: block_env.timestamp.to(), - base_fee_per_gas: Some(block_env.basefee.to()), - gas_used: calls.iter().map(|tx| tx.gas_used).sum(), - blob_gas_used: Some(0), + let block = simulate::build_block( + results, + transactions, + &block_env, parent_hash, - gas_limit, - ..Default::default() - }; - - let block = SimulatedBlock { - inner: Block { - header: from_primitive_with_hash(header.seal_slow()), - ..Default::default() - }, - calls, - }; + total_difficulty, + return_full_transactions, + )?; parent_hash = block.inner.header.hash; - block_env.number += U256::from(1); + gas_used += block.inner.header.gas_used; blocks.push(block); } @@ -474,6 +451,24 @@ pub trait Call: LoadState + SpawnBlocking { Ok((res, env)) } + /// Executes the [`EnvWithHandlerCfg`] against the given [Database] without committing state + /// changes. + fn transact_with_inspector( + &self, + db: DB, + env: EnvWithHandlerCfg, + inspector: impl GetInspector, + ) -> Result<(ResultAndState, EnvWithHandlerCfg), Self::Error> + where + DB: Database, + EthApiError: From, + { + let mut evm = self.evm_config().evm_with_env_and_inspector(db, env, inspector); + let res = evm.transact().map_err(Self::Error::from_evm_err)?; + let (_, env) = evm.into_db_and_env_with_handler_cfg(); + Ok((res, env)) + } + /// Executes the call request at the given [`BlockId`]. fn transact_call_at( &self, @@ -994,7 +989,8 @@ pub trait Call: LoadState + SpawnBlocking { blob_versioned_hashes, max_fee_per_blob_gas, authorization_list, - .. + transaction_type: _, + sidecar: _, } = request; let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } = diff --git a/crates/rpc/rpc-eth-types/Cargo.toml b/crates/rpc/rpc-eth-types/Cargo.toml index 839eb219e639..18d8767f0527 100644 --- a/crates/rpc/rpc-eth-types/Cargo.toml +++ b/crates/rpc/rpc-eth-types/Cargo.toml @@ -30,6 +30,7 @@ reth-trie.workspace = true # ethereum alloy-primitives.workspace = true +alloy-consensus.workspace = true alloy-sol-types.workspace = true revm.workspace = true revm-inspectors.workspace = true diff --git a/crates/rpc/rpc-eth-types/src/lib.rs b/crates/rpc/rpc-eth-types/src/lib.rs index 1897a9fd703d..fba893c15f59 100644 --- a/crates/rpc/rpc-eth-types/src/lib.rs +++ b/crates/rpc/rpc-eth-types/src/lib.rs @@ -18,6 +18,7 @@ pub mod logs_utils; pub mod pending_block; pub mod receipt; pub mod revm_utils; +pub mod simulate; pub mod transaction; pub mod utils; diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs new file mode 100644 index 000000000000..97587a01e174 --- /dev/null +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -0,0 +1,292 @@ +//! Utilities for serving `eth_simulateV1` + +use alloy_consensus::{TxEip4844Variant, TxType, TypedTransaction}; +use alloy_sol_types::SolValue; +use jsonrpsee_types::ErrorObject; +use reth_primitives::{ + BlockWithSenders, Signature, Transaction, TransactionSigned, TransactionSignedNoHash, +}; +use reth_rpc_server_types::result::rpc_err; +use reth_rpc_types::{ + simulate::{SimCallResult, SimulateError, SimulatedBlock}, + Block, BlockTransactionsKind, ToRpcError, TransactionRequest, WithOtherFields, +}; +use reth_rpc_types_compat::block::from_block; +use revm::{interpreter::CallValue, Database, Inspector}; +use revm_primitives::{ + address, b256, Address, BlockEnv, Bytes, ExecutionResult, Log, LogData, TxKind, B256, U256, +}; + +use crate::{EthApiError, RevertError, RpcInvalidTransactionError}; + +/// Sender of ETH transfer log per `eth_simulateV1` spec. +/// +/// +pub const TRANSFER_LOG_EMITTER: Address = address!("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + +/// Topic of `Transfer(address,address,uint256)` event. +pub const TRANSFER_EVENT_TOPIC: B256 = + b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"); + +/// Helper inspector for `eth_simulateV1` which injects `Transfer` logs for all ETH transfers. +#[derive(Debug, Default, Clone)] +pub struct TransferInspector; + +impl Inspector for TransferInspector { + fn call( + &mut self, + context: &mut revm::EvmContext, + inputs: &mut revm::interpreter::CallInputs, + ) -> Option { + if let CallValue::Transfer(value) = inputs.value { + if !value.is_zero() { + let from = B256::from_slice(&inputs.caller.abi_encode()); + let to = B256::from_slice(&inputs.target_address.abi_encode()); + let data = value.abi_encode(); + + context.journaled_state.log(Log { + address: TRANSFER_LOG_EMITTER, + data: LogData::new_unchecked(vec![TRANSFER_EVENT_TOPIC, from, to], data.into()), + }); + } + } + + None + } +} + +/// Errors which may occur during `eth_simulateV1` execution. +#[derive(Debug, thiserror::Error)] +pub enum EthSimulateError { + /// Total gas limit of transactions for the block exceeds the block gas limit. + #[error("Block gas limit exceeded by the block's transactions")] + BlockGasLimitExceeded, + /// Max gas limit for entire operation exceeded. + #[error("Client adjustable limit reached")] + GasLimitReached, +} + +impl EthSimulateError { + const fn error_code(&self) -> i32 { + match self { + Self::BlockGasLimitExceeded => -38015, + Self::GasLimitReached => -38026, + } + } +} + +impl ToRpcError for EthSimulateError { + fn to_rpc_error(&self) -> ErrorObject<'static> { + rpc_err(self.error_code(), self.to_string(), None) + } +} + +/// Goes over the list of [`TransactionRequest`]s and populates missing fields trying to resolve +/// them into [`TransactionSigned`]. +/// +/// If validation is enabled, the function will return error if any of the transactions can't be +/// built right away. +pub fn resolve_transactions( + txs: &mut [TransactionRequest], + validation: bool, + block_gas_limit: u128, + chain_id: u64, + db: &mut DB, +) -> Result, EthApiError> +where + EthApiError: From, +{ + let mut transactions = Vec::with_capacity(txs.len()); + + let default_gas_limit = { + let total_specified_gas = txs.iter().filter_map(|tx| tx.gas).sum::(); + let txs_without_gas_limit = txs.iter().filter(|tx| tx.gas.is_none()).count(); + + if total_specified_gas > block_gas_limit { + return Err(EthApiError::Other(Box::new(EthSimulateError::BlockGasLimitExceeded))) + } + + (block_gas_limit - total_specified_gas) / txs_without_gas_limit as u128 + }; + + for tx in txs { + if tx.buildable_type().is_none() && validation { + return Err(EthApiError::TransactionConversionError); + } + // If we're missing any fields and validation is disabled, we try filling nonce, gas and + // gas price. + let tx_type = tx.preferred_type(); + + let from = if let Some(from) = tx.from { + from + } else { + tx.from = Some(Address::ZERO); + Address::ZERO + }; + + if tx.nonce.is_none() { + tx.nonce = Some(db.basic(from)?.map(|acc| acc.nonce).unwrap_or_default()); + } + + if tx.gas.is_none() { + tx.gas = Some(default_gas_limit); + } + + if tx.chain_id.is_none() { + tx.chain_id = Some(chain_id); + } + + if tx.to.is_none() { + tx.to = Some(TxKind::Create); + } + + match tx_type { + TxType::Legacy | TxType::Eip2930 => { + if tx.gas_price.is_none() { + tx.gas_price = Some(0); + } + } + _ => { + if tx.max_fee_per_gas.is_none() { + tx.max_fee_per_gas = Some(0); + tx.max_priority_fee_per_gas = Some(0); + } + } + } + + let Ok(tx) = tx.clone().build_typed_tx() else { + return Err(EthApiError::TransactionConversionError) + }; + + // Create an empty signature for the transaction. + let signature = + Signature { odd_y_parity: false, r: Default::default(), s: Default::default() }; + + let tx = match tx { + TypedTransaction::Legacy(tx) => { + TransactionSignedNoHash { transaction: Transaction::Legacy(tx), signature } + .with_hash() + } + TypedTransaction::Eip2930(tx) => { + TransactionSignedNoHash { transaction: Transaction::Eip2930(tx), signature } + .with_hash() + } + TypedTransaction::Eip1559(tx) => { + TransactionSignedNoHash { transaction: Transaction::Eip1559(tx), signature } + .with_hash() + } + TypedTransaction::Eip4844(tx) => { + let tx = match tx { + TxEip4844Variant::TxEip4844(tx) => tx, + TxEip4844Variant::TxEip4844WithSidecar(tx) => tx.tx, + }; + TransactionSignedNoHash { transaction: Transaction::Eip4844(tx), signature } + .with_hash() + } + TypedTransaction::Eip7702(tx) => { + TransactionSignedNoHash { transaction: Transaction::Eip7702(tx), signature } + .with_hash() + } + }; + + transactions.push(tx); + } + + Ok(transactions) +} + +/// Handles outputs of the calls execution and builds a [`SimulatedBlock`]. +pub fn build_block( + results: Vec<(Address, ExecutionResult)>, + transactions: Vec, + block_env: &BlockEnv, + parent_hash: B256, + total_difficulty: U256, + full_transactions: bool, +) -> Result>>, EthApiError> { + let mut calls = Vec::with_capacity(results.len()); + let mut senders = Vec::with_capacity(results.len()); + + let mut log_index = 0; + for (transaction_index, ((sender, result), tx)) in + results.into_iter().zip(transactions.iter()).enumerate() + { + senders.push(sender); + + let call = match result { + ExecutionResult::Halt { reason, gas_used } => { + let error = RpcInvalidTransactionError::halt(reason, tx.gas_limit()); + SimCallResult { + return_value: Bytes::new(), + error: Some(SimulateError { + code: error.error_code(), + message: error.to_string(), + }), + gas_used, + logs: Vec::new(), + status: false, + } + } + ExecutionResult::Revert { output, gas_used } => { + let error = RevertError::new(output.clone()); + SimCallResult { + return_value: output, + error: Some(SimulateError { + code: error.error_code(), + message: error.to_string(), + }), + gas_used, + status: false, + logs: Vec::new(), + } + } + ExecutionResult::Success { output, gas_used, logs, .. } => SimCallResult { + return_value: output.into_data(), + error: None, + gas_used, + logs: logs + .into_iter() + .map(|log| { + log_index += 1; + reth_rpc_types::Log { + inner: log, + log_index: Some(log_index - 1), + transaction_index: Some(transaction_index as u64), + transaction_hash: Some(tx.hash()), + block_number: Some(block_env.number.to()), + block_timestamp: Some(block_env.timestamp.to()), + ..Default::default() + } + }) + .collect(), + status: true, + }, + }; + + calls.push(call); + } + + let header = reth_primitives::Header { + beneficiary: block_env.coinbase, + difficulty: block_env.difficulty, + number: block_env.number.to(), + timestamp: block_env.timestamp.to(), + base_fee_per_gas: Some(block_env.basefee.to()), + gas_limit: block_env.gas_limit.to(), + gas_used: 0, + blob_gas_used: Some(0), + parent_hash, + ..Default::default() + }; + + let block = BlockWithSenders { + block: reth_primitives::Block { header, body: transactions, ..Default::default() }, + senders, + }; + + let txs_kind = + if full_transactions { BlockTransactionsKind::Full } else { BlockTransactionsKind::Hashes }; + + let block = from_block(block, total_difficulty, txs_kind, None)?; + Ok(SimulatedBlock { inner: block, calls }) +} From 6e597ac96cebae7b5e4e7d2bfca8b208261a5e9e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 11 Sep 2024 12:45:56 +0400 Subject: [PATCH 03/13] handle basefee correctly --- crates/rpc/rpc-eth-api/src/helpers/call.rs | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index ecbd3d239d77..57ce3cbaa179 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -6,6 +6,7 @@ use alloy_primitives::{Bytes, TxKind, B256, U256}; use futures::Future; use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; use reth_primitives::{ + basefee::calc_next_block_base_fee, revm_primitives::{ BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, HaltReason, ResultAndState, TransactTo, TxEnv, @@ -104,13 +105,35 @@ pub trait EthCall: Call + LoadPendingBlock { let this = self.clone(); self.spawn_with_state_at_block(block, move |state| { let mut db = CacheDB::new(StateProviderDatabase::new(state)); - let mut blocks = Vec::with_capacity(block_state_calls.len()); + let mut blocks: Vec< + SimulatedBlock>>, + > = Vec::with_capacity(block_state_calls.len()); let mut gas_used = 0; for block in block_state_calls { // Increase number and timestamp for every new block block_env.number += U256::from(1); block_env.timestamp += U256::from(1); + if !validation { + block_env.basefee = U256::ZERO; + } else { + let chain_spec = LoadPendingBlock::provider(&this).chain_spec(); + let base_fee_params = + chain_spec.base_fee_params_at_timestamp(block_env.timestamp.to()); + let base_fee = if let Some(latest) = blocks.last() { + let header = &latest.inner.header; + calc_next_block_base_fee( + header.gas_used, + header.gas_limit, + header.base_fee_per_gas.unwrap_or_default(), + base_fee_params, + ) + } else { + base_block.header.next_block_base_fee(base_fee_params).unwrap_or_default() as u128 + }; + block_env.basefee = U256::from(base_fee); + } + let SimBlock { block_overrides, state_overrides, mut calls } = block; if let Some(block_overrides) = block_overrides { From 92365ef2b4ab92ea892e3f999d3a06f598fbc585 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 11 Sep 2024 13:01:15 +0400 Subject: [PATCH 04/13] fmt --- crates/rpc/rpc-eth-api/src/helpers/call.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 57ce3cbaa179..827dbf989ca0 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -129,7 +129,10 @@ pub trait EthCall: Call + LoadPendingBlock { base_fee_params, ) } else { - base_block.header.next_block_base_fee(base_fee_params).unwrap_or_default() as u128 + base_block + .header + .next_block_base_fee(base_fee_params) + .unwrap_or_default() as u128 }; block_env.basefee = U256::from(base_fee); } From a0b2389dadb611e825e4311002589d97d5a4cabc Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 12 Sep 2024 11:54:30 +0400 Subject: [PATCH 05/13] fixes --- crates/rpc/rpc-eth-api/src/helpers/call.rs | 18 ++++++++---------- crates/rpc/rpc-eth-types/src/revm_utils.rs | 2 +- crates/rpc/rpc/src/debug.rs | 2 +- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 827dbf989ca0..e56caf58b00e 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -304,7 +304,7 @@ pub trait EthCall: Call + LoadPendingBlock { tx, gas_limit, &mut db, - Some(overrides), + overrides, ) .map(Into::into)?; let (res, _) = this.transact(&mut db, env)?; @@ -558,7 +558,7 @@ pub trait Call: LoadState + SpawnBlocking { request, this.call_gas_limit(), &mut db, - Some(overrides), + overrides, )?; f(StateCacheDbRefMutWrapper(&mut db), env) @@ -1096,7 +1096,7 @@ pub trait Call: LoadState + SpawnBlocking { mut request: TransactionRequest, gas_limit: u64, db: &mut CacheDB, - overrides: Option, + overrides: EvmOverrides, ) -> Result where DB: DatabaseRef, @@ -1118,13 +1118,11 @@ pub trait Call: LoadState + SpawnBlocking { // set nonce to None so that the correct nonce is chosen by the EVM request.nonce = None; - if let Some(overrides) = overrides { - if let Some(block_overrides) = overrides.block { - apply_block_overrides(*block_overrides, db, &mut block); - } - if let Some(state_overrides) = overrides.state { - apply_state_overrides(state_overrides, db)?; - } + if let Some(block_overrides) = overrides.block { + apply_block_overrides(*block_overrides, db, &mut block); + } + if let Some(state_overrides) = overrides.state { + apply_state_overrides(state_overrides, db)?; } let request_gas = request.gas; diff --git a/crates/rpc/rpc-eth-types/src/revm_utils.rs b/crates/rpc/rpc-eth-types/src/revm_utils.rs index 7d24046eb129..55362620f6e0 100644 --- a/crates/rpc/rpc-eth-types/src/revm_utils.rs +++ b/crates/rpc/rpc-eth-types/src/revm_utils.rs @@ -202,7 +202,7 @@ impl CallFees { } } -/// Applies the given block overrides to the env +/// Applies the given block overrides to the env and updates overriden block hashes in the db. pub fn apply_block_overrides( overrides: BlockOverrides, db: &mut CacheDB, diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 29191792383c..95af81a7bd86 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -536,7 +536,7 @@ where tx, gas_limit, &mut db, - Some(overrides), + overrides, )?; let (trace, state) = From 3635e95a56bf8037e57cbcea8d79cbc5e501f9fb Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 13 Sep 2024 21:04:28 +0400 Subject: [PATCH 06/13] reuse revm-inspectors inspector --- Cargo.lock | 4 +- crates/rpc/rpc-eth-api/src/helpers/call.rs | 10 +++-- crates/rpc/rpc-eth-types/src/simulate.rs | 43 +--------------------- crates/rpc/rpc/src/otterscan.rs | 1 + 4 files changed, 12 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6df1b23403a8..fff3073c46f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4383,7 +4383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -11074,7 +11074,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index e56caf58b00e..7f1e96bc4829 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -23,7 +23,7 @@ use reth_rpc_eth_types::{ apply_block_overrides, apply_state_overrides, caller_gas_allowance, cap_tx_gas_limit_with_caller_allowance, get_precompiles, CallFees, }, - simulate::{self, EthSimulateError, TransferInspector}, + simulate::{self, EthSimulateError}, EthApiError, RevertError, RpcInvalidTransactionError, StateCacheDb, }; use reth_rpc_server_types::constants::gas_oracle::{ @@ -36,7 +36,7 @@ use reth_rpc_types::{ WithOtherFields, }; use revm::{Database, DatabaseCommit, GetInspector}; -use revm_inspectors::access_list::AccessListInspector; +use revm_inspectors::{access_list::AccessListInspector, transfer::TransferInspector}; use tracing::trace; use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace}; @@ -169,7 +169,11 @@ pub trait EthCall: Call + LoadPendingBlock { let (res, env) = { if trace_transfers { - this.transact_with_inspector(&mut db, env, TransferInspector)? + this.transact_with_inspector( + &mut db, + env, + TransferInspector::new(false).with_logs(true), + )? } else { this.transact(&mut db, env)? } diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index 97587a01e174..2f109fd62d13 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -1,7 +1,6 @@ //! Utilities for serving `eth_simulateV1` use alloy_consensus::{TxEip4844Variant, TxType, TypedTransaction}; -use alloy_sol_types::SolValue; use jsonrpsee_types::ErrorObject; use reth_primitives::{ BlockWithSenders, Signature, Transaction, TransactionSigned, TransactionSignedNoHash, @@ -12,49 +11,11 @@ use reth_rpc_types::{ Block, BlockTransactionsKind, ToRpcError, TransactionRequest, WithOtherFields, }; use reth_rpc_types_compat::block::from_block; -use revm::{interpreter::CallValue, Database, Inspector}; -use revm_primitives::{ - address, b256, Address, BlockEnv, Bytes, ExecutionResult, Log, LogData, TxKind, B256, U256, -}; +use revm::Database; +use revm_primitives::{Address, BlockEnv, Bytes, ExecutionResult, TxKind, B256, U256}; use crate::{EthApiError, RevertError, RpcInvalidTransactionError}; -/// Sender of ETH transfer log per `eth_simulateV1` spec. -/// -/// -pub const TRANSFER_LOG_EMITTER: Address = address!("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - -/// Topic of `Transfer(address,address,uint256)` event. -pub const TRANSFER_EVENT_TOPIC: B256 = - b256!("ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"); - -/// Helper inspector for `eth_simulateV1` which injects `Transfer` logs for all ETH transfers. -#[derive(Debug, Default, Clone)] -pub struct TransferInspector; - -impl Inspector for TransferInspector { - fn call( - &mut self, - context: &mut revm::EvmContext, - inputs: &mut revm::interpreter::CallInputs, - ) -> Option { - if let CallValue::Transfer(value) = inputs.value { - if !value.is_zero() { - let from = B256::from_slice(&inputs.caller.abi_encode()); - let to = B256::from_slice(&inputs.target_address.abi_encode()); - let data = value.abi_encode(); - - context.journaled_state.log(Log { - address: TRANSFER_LOG_EMITTER, - data: LogData::new_unchecked(vec![TRANSFER_EVENT_TOPIC, from, to], data.into()), - }); - } - } - - None - } -} - /// Errors which may occur during `eth_simulateV1` execution. #[derive(Debug, thiserror::Error)] pub enum EthSimulateError { diff --git a/crates/rpc/rpc/src/otterscan.rs b/crates/rpc/rpc/src/otterscan.rs index 61ff794e56cb..353436002a79 100644 --- a/crates/rpc/rpc/src/otterscan.rs +++ b/crates/rpc/rpc/src/otterscan.rs @@ -120,6 +120,7 @@ where TransferKind::Create => OperationType::OpCreate, TransferKind::Create2 => OperationType::OpCreate2, TransferKind::SelfDestruct => OperationType::OpSelfDestruct, + TransferKind::EofCreate => OperationType::OpCreate, }, }) .collect::>() From 66ae785eb3c0f88f3e1d7f59d51d43fe99f0e67e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 14 Sep 2024 04:57:12 +0400 Subject: [PATCH 07/13] receipts + state root --- crates/rpc/rpc-eth-api/src/helpers/call.rs | 7 +-- crates/rpc/rpc-eth-types/src/simulate.rs | 62 +++++++++++++++++++--- crates/rpc/rpc/src/otterscan.rs | 5 +- 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 7f1e96bc4829..506411ebe825 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -114,9 +114,7 @@ pub trait EthCall: Call + LoadPendingBlock { block_env.number += U256::from(1); block_env.timestamp += U256::from(1); - if !validation { - block_env.basefee = U256::ZERO; - } else { + if validation { let chain_spec = LoadPendingBlock::provider(&this).chain_spec(); let base_fee_params = chain_spec.base_fee_params_at_timestamp(block_env.timestamp.to()); @@ -135,6 +133,8 @@ pub trait EthCall: Call + LoadPendingBlock { .unwrap_or_default() as u128 }; block_env.basefee = U256::from(base_fee); + } else { + block_env.basefee = U256::ZERO; } let SimBlock { block_overrides, state_overrides, mut calls } = block; @@ -195,6 +195,7 @@ pub trait EthCall: Call + LoadPendingBlock { parent_hash, total_difficulty, return_full_transactions, + &db, )?; parent_hash = block.inner.header.hash; diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index 2f109fd62d13..196f273767ec 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -3,18 +3,25 @@ use alloy_consensus::{TxEip4844Variant, TxType, TypedTransaction}; use jsonrpsee_types::ErrorObject; use reth_primitives::{ - BlockWithSenders, Signature, Transaction, TransactionSigned, TransactionSignedNoHash, + logs_bloom, + proofs::{calculate_receipt_root, calculate_transaction_root}, + BlockWithSenders, Receipt, Signature, Transaction, TransactionSigned, TransactionSignedNoHash, }; +use reth_revm::database::StateProviderDatabase; use reth_rpc_server_types::result::rpc_err; use reth_rpc_types::{ simulate::{SimCallResult, SimulateError, SimulatedBlock}, Block, BlockTransactionsKind, ToRpcError, TransactionRequest, WithOtherFields, }; use reth_rpc_types_compat::block::from_block; -use revm::Database; -use revm_primitives::{Address, BlockEnv, Bytes, ExecutionResult, TxKind, B256, U256}; +use reth_storage_api::StateRootProvider; +use reth_trie::{HashedPostState, HashedStorage}; +use revm::{db::CacheDB, Database}; +use revm_primitives::{keccak256, Address, BlockEnv, Bytes, ExecutionResult, TxKind, B256, U256}; -use crate::{EthApiError, RevertError, RpcInvalidTransactionError}; +use crate::{ + cache::db::StateProviderTraitObjWrapper, EthApiError, RevertError, RpcInvalidTransactionError, +}; /// Errors which may occur during `eth_simulateV1` execution. #[derive(Debug, thiserror::Error)] @@ -67,7 +74,11 @@ where return Err(EthApiError::Other(Box::new(EthSimulateError::BlockGasLimitExceeded))) } - (block_gas_limit - total_specified_gas) / txs_without_gas_limit as u128 + if txs_without_gas_limit > 0 { + (block_gas_limit - total_specified_gas) / txs_without_gas_limit as u128 + } else { + 0 + } }; for tx in txs { @@ -164,9 +175,11 @@ pub fn build_block( parent_hash: B256, total_difficulty: U256, full_transactions: bool, + db: &CacheDB>>, ) -> Result>>, EthApiError> { - let mut calls = Vec::with_capacity(results.len()); + let mut calls: Vec = Vec::with_capacity(results.len()); let mut senders = Vec::with_capacity(results.len()); + let mut receipts = Vec::new(); let mut log_index = 0; for (transaction_index, ((sender, result), tx)) in @@ -224,9 +237,39 @@ pub fn build_block( }, }; + receipts.push( + Receipt { + tx_type: tx.tx_type(), + success: call.status, + cumulative_gas_used: call.gas_used + calls.iter().map(|c| c.gas_used).sum::(), + logs: call.logs.iter().map(|log| &log.inner).cloned().collect(), + ..Default::default() + } + .into(), + ); + calls.push(call); } + let mut hashed_state = HashedPostState::default(); + for (address, account) in &db.accounts { + let hashed_address = keccak256(address); + hashed_state.accounts.insert(hashed_address, Some(account.info.clone().into())); + + let storage = hashed_state + .storages + .entry(hashed_address) + .or_insert_with(|| HashedStorage::new(account.account_state.is_storage_cleared())); + + for (slot, value) in &account.storage { + let slot = B256::from(*slot); + let hashed_slot = keccak256(slot); + storage.storage.insert(hashed_slot, *value); + } + } + + let state_root = db.db.0.state_root(hashed_state)?; + let header = reth_primitives::Header { beneficiary: block_env.coinbase, difficulty: block_env.difficulty, @@ -234,9 +277,14 @@ pub fn build_block( timestamp: block_env.timestamp.to(), base_fee_per_gas: Some(block_env.basefee.to()), gas_limit: block_env.gas_limit.to(), - gas_used: 0, + gas_used: calls.iter().map(|c| c.gas_used).sum::(), blob_gas_used: Some(0), parent_hash, + receipts_root: calculate_receipt_root(&receipts), + transactions_root: calculate_transaction_root(&transactions), + state_root, + logs_bloom: logs_bloom(receipts.iter().flat_map(|r| r.receipt.logs.iter())), + mix_hash: block_env.prevrandao.unwrap_or_default(), ..Default::default() }; diff --git a/crates/rpc/rpc/src/otterscan.rs b/crates/rpc/rpc/src/otterscan.rs index 353436002a79..94bc8c2d13d8 100644 --- a/crates/rpc/rpc/src/otterscan.rs +++ b/crates/rpc/rpc/src/otterscan.rs @@ -117,10 +117,11 @@ where value: op.value, r#type: match op.kind { TransferKind::Call => OperationType::OpTransfer, - TransferKind::Create => OperationType::OpCreate, + TransferKind::Create | TransferKind::EofCreate => { + OperationType::OpCreate + } TransferKind::Create2 => OperationType::OpCreate2, TransferKind::SelfDestruct => OperationType::OpSelfDestruct, - TransferKind::EofCreate => OperationType::OpCreate, }, }) .collect::>() From 3914aeb63d636b4d8ca3c58a1a55518a77f7aabb Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 14 Sep 2024 05:22:54 +0400 Subject: [PATCH 08/13] lint --- crates/rpc/rpc-eth-types/src/revm_utils.rs | 2 +- crates/rpc/rpc-eth-types/src/simulate.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/rpc/rpc-eth-types/src/revm_utils.rs b/crates/rpc/rpc-eth-types/src/revm_utils.rs index 55362620f6e0..81fb88b5e30e 100644 --- a/crates/rpc/rpc-eth-types/src/revm_utils.rs +++ b/crates/rpc/rpc-eth-types/src/revm_utils.rs @@ -202,7 +202,7 @@ impl CallFees { } } -/// Applies the given block overrides to the env and updates overriden block hashes in the db. +/// Applies the given block overrides to the env and updates overridden block hashes in the db. pub fn apply_block_overrides( overrides: BlockOverrides, db: &mut CacheDB, diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index 196f273767ec..c36f77599aa6 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -238,6 +238,7 @@ pub fn build_block( }; receipts.push( + #[allow(clippy::needless_update)] Receipt { tx_type: tx.tx_type(), success: call.status, From 0e11919b863e4d265ef77f72ee63d649da53c288 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 14 Sep 2024 05:49:36 +0400 Subject: [PATCH 09/13] --rpc.max-simulate-blocks --- crates/node/core/src/args/rpc_server.rs | 9 +++++++++ crates/optimism/rpc/src/eth/call.rs | 5 +++++ crates/rpc/rpc-builder/src/config.rs | 1 + crates/rpc/rpc-eth-api/src/helpers/call.rs | 7 +++++++ crates/rpc/rpc-eth-types/src/builder/config.rs | 11 ++++++++++- crates/rpc/rpc-server-types/src/constants.rs | 3 +++ crates/rpc/rpc/src/eth/core.rs | 18 +++++++++++++++++- crates/rpc/rpc/src/eth/helpers/call.rs | 5 +++++ crates/rpc/rpc/src/eth/helpers/state.rs | 6 +++++- crates/rpc/rpc/src/eth/helpers/transaction.rs | 5 ++++- 10 files changed, 66 insertions(+), 4 deletions(-) diff --git a/crates/node/core/src/args/rpc_server.rs b/crates/node/core/src/args/rpc_server.rs index 23af07521670..15771e9897ef 100644 --- a/crates/node/core/src/args/rpc_server.rs +++ b/crates/node/core/src/args/rpc_server.rs @@ -156,6 +156,14 @@ pub struct RpcServerArgs { )] pub rpc_gas_cap: u64, + /// Maximum number of blocks for `eth_simulateV1` call. + #[arg( + long = "rpc.max-simulate-blocks", + value_name = "BLOCKS_COUNT", + default_value_t = constants::DEFAULT_MAX_SIMULATE_BLOCKS + )] + pub rpc_max_simulate_blocks: u64, + /// The maximum proof window for historical proof generation. /// This value allows for generating historical proofs up to /// configured number of blocks from current tip (up to `tip - window`). @@ -300,6 +308,7 @@ impl Default for RpcServerArgs { rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(), rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(), rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP, + rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS, rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW, gas_price_oracle: GasPriceOracleArgs::default(), rpc_state_cache: RpcStateCacheArgs::default(), diff --git a/crates/optimism/rpc/src/eth/call.rs b/crates/optimism/rpc/src/eth/call.rs index 40e7a6aa2049..f2218ef877a4 100644 --- a/crates/optimism/rpc/src/eth/call.rs +++ b/crates/optimism/rpc/src/eth/call.rs @@ -30,6 +30,11 @@ where self.inner.gas_cap() } + #[inline] + fn max_simulate_blocks(&self) -> u64 { + self.inner.max_simulate_blocks() + } + #[inline] fn evm_config(&self) -> &impl ConfigureEvm { self.inner.evm_config() diff --git a/crates/rpc/rpc-builder/src/config.rs b/crates/rpc/rpc-builder/src/config.rs index 1cac81f4c8d2..4e86b2c81f6d 100644 --- a/crates/rpc/rpc-builder/src/config.rs +++ b/crates/rpc/rpc-builder/src/config.rs @@ -95,6 +95,7 @@ impl RethRpcServerConfig for RpcServerArgs { .max_logs_per_response(self.rpc_max_logs_per_response.unwrap_or_max() as usize) .eth_proof_window(self.rpc_eth_proof_window) .rpc_gas_cap(self.rpc_gas_cap) + .rpc_max_simulate_blocks(self.rpc_max_simulate_blocks) .state_cache(self.state_cache_config()) .gpo_config(self.gas_price_oracle_config()) .proof_permits(self.rpc_proof_permits) diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 506411ebe825..381c4747a9d5 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -72,6 +72,10 @@ pub trait EthCall: Call + LoadPendingBlock { Self: LoadBlock, { async move { + if payload.block_state_calls.len() > self.max_simulate_blocks() as usize { + return Err(EthApiError::InvalidParams("too many blocks.".to_string()).into()) + } + let SimulatePayload { block_state_calls, trace_transfers, @@ -451,6 +455,9 @@ pub trait Call: LoadState + SpawnBlocking { /// Data access in default trait method implementations. fn call_gas_limit(&self) -> u64; + /// Returns the maximum number of blocks accepted for `eth_simulateV1`. + fn max_simulate_blocks(&self) -> u64; + /// Returns a handle for reading evm config. /// /// Data access in default (L1) trait method implementations. diff --git a/crates/rpc/rpc-eth-types/src/builder/config.rs b/crates/rpc/rpc-eth-types/src/builder/config.rs index 2edc81e8d7ce..a016d021586b 100644 --- a/crates/rpc/rpc-eth-types/src/builder/config.rs +++ b/crates/rpc/rpc-eth-types/src/builder/config.rs @@ -7,7 +7,7 @@ use crate::{ }; use reth_rpc_server_types::constants::{ default_max_tracing_requests, DEFAULT_ETH_PROOF_WINDOW, DEFAULT_MAX_BLOCKS_PER_FILTER, - DEFAULT_MAX_LOGS_PER_RESPONSE, DEFAULT_PROOF_PERMITS, + DEFAULT_MAX_LOGS_PER_RESPONSE, DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_PROOF_PERMITS, }; use serde::{Deserialize, Serialize}; @@ -33,6 +33,8 @@ pub struct EthConfig { /// /// Defaults to [`RPC_DEFAULT_GAS_CAP`] pub rpc_gas_cap: u64, + /// Max number of blocks for `eth_simulateV1`. + pub rpc_max_simulate_blocks: u64, /// /// Sets TTL for stale filters pub stale_filter_ttl: Duration, @@ -62,6 +64,7 @@ impl Default for EthConfig { max_blocks_per_filter: DEFAULT_MAX_BLOCKS_PER_FILTER, max_logs_per_response: DEFAULT_MAX_LOGS_PER_RESPONSE, rpc_gas_cap: RPC_DEFAULT_GAS_CAP.into(), + rpc_max_simulate_blocks: DEFAULT_MAX_SIMULATE_BLOCKS, stale_filter_ttl: DEFAULT_STALE_FILTER_TTL, fee_history_cache: FeeHistoryCacheConfig::default(), proof_permits: DEFAULT_PROOF_PERMITS, @@ -106,6 +109,12 @@ impl EthConfig { self } + /// Configures the maximum gas limit for `eth_call` and call tracing RPC methods + pub const fn rpc_max_simulate_blocks(mut self, max_blocks: u64) -> Self { + self.rpc_max_simulate_blocks = max_blocks; + self + } + /// Configures the maximum proof window for historical proof generation. pub const fn eth_proof_window(mut self, window: u64) -> Self { self.eth_proof_window = window; diff --git a/crates/rpc/rpc-server-types/src/constants.rs b/crates/rpc/rpc-server-types/src/constants.rs index 1fad38a75834..ede0263d8819 100644 --- a/crates/rpc/rpc-server-types/src/constants.rs +++ b/crates/rpc/rpc-server-types/src/constants.rs @@ -45,6 +45,9 @@ pub const DEFAULT_ENGINE_API_IPC_ENDPOINT: &str = r"\\.\pipe\reth_engine_api.ipc #[cfg(not(windows))] pub const DEFAULT_ENGINE_API_IPC_ENDPOINT: &str = "/tmp/reth_engine_api.ipc"; +/// The default limit for blocks count in `eth_simulateV1`. +pub const DEFAULT_MAX_SIMULATE_BLOCKS: u64 = 256; + /// The default eth historical proof window. pub const DEFAULT_ETH_PROOF_WINDOW: u64 = 0; diff --git a/crates/rpc/rpc/src/eth/core.rs b/crates/rpc/rpc/src/eth/core.rs index b1848d9a49e3..73b10b05eca0 100644 --- a/crates/rpc/rpc/src/eth/core.rs +++ b/crates/rpc/rpc/src/eth/core.rs @@ -57,6 +57,7 @@ where eth_cache: EthStateCache, gas_oracle: GasPriceOracle, gas_cap: impl Into, + max_simulate_blocks: u64, eth_proof_window: u64, blocking_task_pool: BlockingTaskPool, fee_history_cache: FeeHistoryCache, @@ -70,6 +71,7 @@ where eth_cache, gas_oracle, gas_cap, + max_simulate_blocks, eth_proof_window, blocking_task_pool, fee_history_cache, @@ -107,6 +109,7 @@ where ctx.cache.clone(), ctx.new_gas_price_oracle(), ctx.config.rpc_gas_cap, + ctx.config.rpc_max_simulate_blocks, ctx.config.eth_proof_window, blocking_task_pool, ctx.new_fee_history_cache(), @@ -186,6 +189,8 @@ pub struct EthApiInner { gas_oracle: GasPriceOracle, /// Maximum gas limit for `eth_call` and call tracing RPC methods. gas_cap: u64, + /// Maximum number of blocks for `eth_simulateV1`. + max_simulate_blocks: u64, /// The maximum number of blocks into the past for generating state proofs. eth_proof_window: u64, /// The block number at which the node started @@ -218,6 +223,7 @@ where eth_cache: EthStateCache, gas_oracle: GasPriceOracle, gas_cap: impl Into, + max_simulate_blocks: u64, eth_proof_window: u64, blocking_task_pool: BlockingTaskPool, fee_history_cache: FeeHistoryCache, @@ -244,6 +250,7 @@ where eth_cache, gas_oracle, gas_cap: gas_cap.into().into(), + max_simulate_blocks, eth_proof_window, starting_block, task_spawner: Box::new(task_spawner), @@ -305,6 +312,12 @@ impl EthApiInner u64 { + self.max_simulate_blocks + } + /// Returns a handle to the gas oracle. #[inline] pub const fn gas_oracle(&self) -> &GasPriceOracle { @@ -364,7 +377,9 @@ mod tests { use reth_rpc_eth_types::{ EthStateCache, FeeHistoryCache, FeeHistoryCacheConfig, GasPriceOracle, }; - use reth_rpc_server_types::constants::{DEFAULT_ETH_PROOF_WINDOW, DEFAULT_PROOF_PERMITS}; + use reth_rpc_server_types::constants::{ + DEFAULT_ETH_PROOF_WINDOW, DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_PROOF_PERMITS, + }; use reth_rpc_types::FeeHistory; use reth_tasks::pool::BlockingTaskPool; use reth_testing_utils::{generators, generators::Rng}; @@ -397,6 +412,7 @@ mod tests { cache.clone(), GasPriceOracle::new(provider, Default::default(), cache), gas_cap, + DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_ETH_PROOF_WINDOW, BlockingTaskPool::build().expect("failed to build tracing pool"), fee_history_cache, diff --git a/crates/rpc/rpc/src/eth/helpers/call.rs b/crates/rpc/rpc/src/eth/helpers/call.rs index c442c46b4b80..6ba52611de7a 100644 --- a/crates/rpc/rpc/src/eth/helpers/call.rs +++ b/crates/rpc/rpc/src/eth/helpers/call.rs @@ -20,6 +20,11 @@ where self.inner.gas_cap() } + #[inline] + fn max_simulate_blocks(&self) -> u64 { + self.inner.max_simulate_blocks() + } + #[inline] fn evm_config(&self) -> &impl ConfigureEvm { self.inner.evm_config() diff --git a/crates/rpc/rpc/src/eth/helpers/state.rs b/crates/rpc/rpc/src/eth/helpers/state.rs index 2d2ce5d5a261..c0696adc5367 100644 --- a/crates/rpc/rpc/src/eth/helpers/state.rs +++ b/crates/rpc/rpc/src/eth/helpers/state.rs @@ -52,7 +52,9 @@ mod tests { use reth_rpc_eth_types::{ EthStateCache, FeeHistoryCache, FeeHistoryCacheConfig, GasPriceOracle, }; - use reth_rpc_server_types::constants::{DEFAULT_ETH_PROOF_WINDOW, DEFAULT_PROOF_PERMITS}; + use reth_rpc_server_types::constants::{ + DEFAULT_ETH_PROOF_WINDOW, DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_PROOF_PERMITS, + }; use reth_tasks::pool::BlockingTaskPool; use reth_transaction_pool::test_utils::{testing_pool, TestPool}; use std::collections::HashMap; @@ -70,6 +72,7 @@ mod tests { cache.clone(), GasPriceOracle::new(NoopProvider::default(), Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, + DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_ETH_PROOF_WINDOW, BlockingTaskPool::build().expect("failed to build tracing pool"), FeeHistoryCache::new(cache, FeeHistoryCacheConfig::default()), @@ -96,6 +99,7 @@ mod tests { cache.clone(), GasPriceOracle::new(mock_provider, Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, + DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_ETH_PROOF_WINDOW, BlockingTaskPool::build().expect("failed to build tracing pool"), FeeHistoryCache::new(cache, FeeHistoryCacheConfig::default()), diff --git a/crates/rpc/rpc/src/eth/helpers/transaction.rs b/crates/rpc/rpc/src/eth/helpers/transaction.rs index 7188ecf0a1e3..17eff7b35772 100644 --- a/crates/rpc/rpc/src/eth/helpers/transaction.rs +++ b/crates/rpc/rpc/src/eth/helpers/transaction.rs @@ -62,7 +62,9 @@ mod tests { use reth_rpc_eth_types::{ EthStateCache, FeeHistoryCache, FeeHistoryCacheConfig, GasPriceOracle, }; - use reth_rpc_server_types::constants::{DEFAULT_ETH_PROOF_WINDOW, DEFAULT_PROOF_PERMITS}; + use reth_rpc_server_types::constants::{ + DEFAULT_ETH_PROOF_WINDOW, DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_PROOF_PERMITS, + }; use reth_tasks::pool::BlockingTaskPool; use reth_transaction_pool::{test_utils::testing_pool, TransactionPool}; @@ -86,6 +88,7 @@ mod tests { cache.clone(), GasPriceOracle::new(noop_provider, Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, + DEFAULT_MAX_SIMULATE_BLOCKS, DEFAULT_ETH_PROOF_WINDOW, BlockingTaskPool::build().expect("failed to build tracing pool"), fee_history_cache, From 59d425ddbe5a060f237fe6ebf2b497b2a97ac4cd Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sat, 14 Sep 2024 05:53:26 +0400 Subject: [PATCH 10/13] simplify return type --- crates/rpc/rpc-eth-api/src/core.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/rpc/rpc-eth-api/src/core.rs b/crates/rpc/rpc-eth-api/src/core.rs index bf4fb9ea3755..60adae2a6ed4 100644 --- a/crates/rpc/rpc-eth-api/src/core.rs +++ b/crates/rpc/rpc-eth-api/src/core.rs @@ -11,9 +11,8 @@ use reth_rpc_types::{ serde_helpers::JsonStorageKey, simulate::{SimulatePayload, SimulatedBlock}, state::{EvmOverrides, StateOverride}, - AnyTransactionReceipt, Block, BlockOverrides, Bundle, EIP1186AccountProofResponse, - EthCallResponse, FeeHistory, Header, Index, StateContext, SyncStatus, TransactionRequest, - WithOtherFields, Work, + AnyTransactionReceipt, BlockOverrides, Bundle, EIP1186AccountProofResponse, EthCallResponse, + FeeHistory, Header, Index, StateContext, SyncStatus, TransactionRequest, Work, }; use tracing::trace; @@ -621,7 +620,7 @@ where &self, payload: SimulatePayload, block_number: Option, - ) -> RpcResult>>>> { + ) -> RpcResult>>> { trace!(target: "rpc::eth", ?block_number, "Serving eth_simulateV1"); Ok(EthCall::simulate_v1(self, payload, block_number).await?) } From 0bd71f9c807e456597f35a782ad3d5a29ae40205 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 16 Sep 2024 14:59:35 +0300 Subject: [PATCH 11/13] fix --- crates/optimism/rpc/src/eth/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs index 25af6f029393..0c9c01f0fd06 100644 --- a/crates/optimism/rpc/src/eth/mod.rs +++ b/crates/optimism/rpc/src/eth/mod.rs @@ -86,6 +86,7 @@ where ctx.cache.clone(), ctx.new_gas_price_oracle(), ctx.config.rpc_gas_cap, + ctx.config.rpc_max_simulate_blocks, ctx.config.eth_proof_window, blocking_task_pool, ctx.new_fee_history_cache(), From 4e6e1cd877b3240bd932125a8f9c1245fb6ba241 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Mon, 16 Sep 2024 16:44:51 +0300 Subject: [PATCH 12/13] update book --- book/cli/reth/node.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/book/cli/reth/node.md b/book/cli/reth/node.md index b73b1319b857..d996cf317dc9 100644 --- a/book/cli/reth/node.md +++ b/book/cli/reth/node.md @@ -347,6 +347,11 @@ RPC: [default: 50000000] + --rpc.max-simulate-blocks + Maximum number of blocks for `eth_simulateV1` call + + [default: 256] + --rpc.eth-proof-window The maximum proof window for historical proof generation. This value allows for generating historical proofs up to configured number of blocks from current tip (up to `tip - window`) From 07caf48f11f04b928bbec96b5fe807f65208cc41 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 18 Sep 2024 19:24:48 +0200 Subject: [PATCH 13/13] chore: remove patches --- Cargo.lock | 130 ----------------------------------------------------- Cargo.toml | 54 +++++++++++----------- 2 files changed, 27 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea624b39f50b..63243863d7e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11626,133 +11626,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "alloy-consensus" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-eips" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-genesis" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-json-rpc" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-network" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-node-bindings" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-provider" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-pubsub" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-client" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-admin" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-anvil" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-beacon" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-debug" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-engine" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-eth" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-mev" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-trace" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-rpc-types-txpool" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-serde" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-signer" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-signer-local" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-transport" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-transport-http" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-transport-ipc" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" - -[[patch.unused]] -name = "alloy-transport-ws" -version = "0.3.5" -source = "git+https://github.com/alloy-rs/alloy?rev=8c499409#8c4994094f35f253ff0cd9250e9315704fe39a7c" diff --git a/Cargo.toml b/Cargo.toml index 6c039ff0f676..9b00540dab67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -584,30 +584,30 @@ tikv-jemalloc-ctl = "0.6" tikv-jemallocator = "0.6" tracy-client = "0.17.3" -[patch.crates-io] -alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#[patch.crates-io] +#alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"}