diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e141a4f056..6a6ece4310 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -45,7 +45,7 @@ jobs: ${{ runner.os }}-cargo- - name: Run sccache - uses: mozilla-actions/sccache-action@v0.0.6 + uses: mozilla-actions/sccache-action@v0.0.7 - name: Install Rust toolchain run: make setup diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6222a665ea..7198406dd5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: ${{ runner.os }}-cargo- - name: Run sccache - uses: mozilla-actions/sccache-action@v0.0.6 + uses: mozilla-actions/sccache-action@v0.0.7 - name: Install Rust toolchain run: make setup @@ -75,7 +75,7 @@ jobs: ${{ runner.os }}-cargo- - name: Run sccache - uses: mozilla-actions/sccache-action@v0.0.6 + uses: mozilla-actions/sccache-action@v0.0.7 - name: Install Rust toolchain run: make setup diff --git a/Cargo.lock b/Cargo.lock index 51f58b8050..f3ed711783 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -493,7 +493,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.31", + "rustix 0.38.42", "slab", "tracing", "windows-sys 0.52.0", @@ -512,9 +512,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", @@ -659,7 +659,7 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "proc-macro2", "quote", "regex", @@ -1723,6 +1723,26 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "diff" version = "0.1.13" @@ -2031,9 +2051,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2209,9 +2229,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.0.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fc-api" @@ -2646,6 +2666,7 @@ dependencies = [ name = "fp-evm" version = "3.0.0-dev" dependencies = [ + "environmental", "evm", "frame-support", "num_enum", @@ -3838,13 +3859,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.90", ] [[package]] @@ -3956,7 +3977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.31", + "rustix 0.38.42", "windows-sys 0.48.0", ] @@ -4004,9 +4025,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd1ead9fb95614e8dc5556d12a8681c2f6d352d0c1d3efc8708c7ccbba47bc6" +checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" dependencies = [ "jsonrpsee-core", "jsonrpsee-proc-macros", @@ -4018,9 +4039,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff79651479f69ada7bda604ef2acf3f1aa50755d97cc36d25ff04c2664f9d96f" +checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" dependencies = [ "async-trait", "bytes", @@ -4041,9 +4062,9 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d4c6bec4909c966f59f52db3655c0e9d4685faae8b49185973d9d7389bb884" +checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" dependencies = [ "heck 0.5.0", "proc-macro-crate 3.1.0", @@ -4054,9 +4075,9 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe2198e5fd96cf2153ecc123364f699b6e2151317ea09c7bf799c43c2fe1415" +checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" dependencies = [ "futures-util", "http 1.1.0", @@ -4081,9 +4102,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531e386460425e49679587871a056f2895a47dade21457324ad1262cd78ef6d9" +checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" dependencies = [ "http 1.1.0", "serde", @@ -4170,9 +4191,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -4750,9 +4771,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lioness" @@ -4941,7 +4962,7 @@ dependencies = [ "basic-toml", "diff", "glob", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "serde", "serde_derive", "serde_json", @@ -4997,7 +5018,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" dependencies = [ - "rustix 0.38.31", + "rustix 0.38.42", ] [[package]] @@ -6567,7 +6588,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.31", + "rustix 0.38.42", "tracing", "windows-sys 0.52.0", ] @@ -6623,7 +6644,7 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" name = "precompile-utils" version = "0.1.0" dependencies = [ - "derive_more", + "derive_more 0.99.18", "environmental", "evm", "fp-evm", @@ -6657,7 +6678,7 @@ dependencies = [ "macrotest", "num_enum", "precompile-utils", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "proc-macro2", "quote", "sp-crypto-hashing", @@ -6737,9 +6758,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", "syn 2.0.90", @@ -6945,7 +6966,7 @@ dependencies = [ "multimap", "once_cell", "petgraph", - "prettyplease 0.2.24", + "prettyplease 0.2.25", "prost 0.12.6", "prost-types 0.12.6", "regex", @@ -7576,14 +7597,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] @@ -8604,7 +8625,7 @@ name = "sc-sysinfo" version = "38.0.0" source = "git+https://github.com/paritytech/polkadot-sdk?branch=stable2409#660da7a8f53e4d9bf3a509c427627a5ef8ccafba" dependencies = [ - "derive_more", + "derive_more 0.99.18", "futures", "libc", "log", @@ -8754,7 +8775,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e98f3262c250d90e700bb802eb704e1f841e03331c2eb815e46516c4edbf5b27" dependencies = [ - "derive_more", + "derive_more 0.99.18", "parity-scale-codec", "scale-bits", "scale-type-resolver", @@ -8763,13 +8784,13 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "bitvec", "cfg-if", - "derive_more", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", "serde", @@ -8777,14 +8798,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.90", ] [[package]] @@ -8966,9 +8987,9 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] @@ -8984,9 +9005,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", @@ -8995,9 +9016,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -10501,13 +10522,14 @@ checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", - "rustix 0.38.31", + "once_cell", + "rustix 0.38.42", "windows-sys 0.52.0", ] @@ -10526,7 +10548,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.38.31", + "rustix 0.38.42", "windows-sys 0.48.0", ] @@ -10645,9 +10667,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -10663,9 +10685,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 3608ef8d86..e0f3e84ab4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,8 +62,8 @@ hash-db = { version = "0.16.0", default-features = false } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } hex-literal = "0.4.1" impl-serde = { version = "0.4.0", default-features = false } -impl-trait-for-tuples = "0.2.1" -jsonrpsee = { version = "0.24.4" } +impl-trait-for-tuples = "0.2.3" +jsonrpsee = { version = "0.24.7" } jsonrpsee-core = { version = "0.24.4" } kvdb-rocksdb = "0.19.0" libsecp256k1 = { version = "0.7.1", default-features = false } @@ -74,13 +74,13 @@ parking_lot = "0.12.3" quote = "1.0.37" rlp = { version = "0.5.2", default-features = false } scale-codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = ["derive"] } -scale-info = { version = "2.11.3", default-features = false, features = ["derive"] } +scale-info = { version = "2.11.6", default-features = false, features = ["derive"] } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } serde_json = "1.0" similar-asserts = "1.6.0" sqlx = { version = "0.7.4", default-features = false, features = ["macros"] } thiserror = "1.0" -tokio = "1.40.0" +tokio = "1.43.0" # Substrate Client sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409" } diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 8a0406883e..e2b200a5d6 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -53,6 +53,9 @@ pub trait Backend: Send + Sync { self.log_indexer().is_indexed() } + /// Get the hash of the oldest substrate block fully indexed by the backend. + async fn first_block_hash(&self) -> Result; + /// Get the hash of the latest substrate block fully indexed by the backend. async fn latest_block_hash(&self) -> Result; } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 6d188f4e82..063fb4a940 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -40,7 +40,7 @@ fp-storage = { workspace = true, features = ["default"] } [dev-dependencies] futures = { workspace = true } maplit = "1.0.2" -tempfile = "3.10.1" +tempfile = "3.14.0" # Substrate sc-block-builder = { workspace = true } sp-consensus = { workspace = true } diff --git a/client/db/src/kv/mod.rs b/client/db/src/kv/mod.rs index af82834e0d..3a39c4c836 100644 --- a/client/db/src/kv/mod.rs +++ b/client/db/src/kv/mod.rs @@ -90,6 +90,10 @@ impl> fc_api::Backend for Backend< &self.log_indexer } + async fn first_block_hash(&self) -> Result { + Ok(self.client.info().genesis_hash) + } + async fn latest_block_hash(&self) -> Result { Ok(self.client.info().best_hash) } diff --git a/client/db/src/sql/mod.rs b/client/db/src/sql/mod.rs index d5c335a954..b05271c76c 100644 --- a/client/db/src/sql/mod.rs +++ b/client/db/src/sql/mod.rs @@ -820,6 +820,15 @@ impl> fc_api::Backend for Backend { self } + async fn first_block_hash(&self) -> Result { + // Retrieves the block hash for the earliest indexed block, maybe it's not canon. + sqlx::query("SELECT substrate_block_hash FROM blocks ORDER BY block_number ASC LIMIT 1") + .fetch_one(self.pool()) + .await + .map(|row| H256::from_slice(&row.get::, _>(0)[..])) + .map_err(|e| format!("Failed to fetch oldest block hash: {}", e)) + } + async fn latest_block_hash(&self) -> Result { // Retrieves the block hash for the latest indexed block, maybe it's not canon. sqlx::query("SELECT substrate_block_hash FROM blocks ORDER BY block_number DESC LIMIT 1") diff --git a/client/mapping-sync/Cargo.toml b/client/mapping-sync/Cargo.toml index 6a0a048df1..74a2178687 100644 --- a/client/mapping-sync/Cargo.toml +++ b/client/mapping-sync/Cargo.toml @@ -35,7 +35,7 @@ ethereum = { workspace = true } ethereum-types = { workspace = true } scale-codec = { package = "parity-scale-codec", workspace = true } sqlx = { workspace = true, features = ["runtime-tokio-native-tls", "sqlite"] } -tempfile = "3.10.1" +tempfile = "3.14.0" tokio = { workspace = true, features = ["sync"] } # Substrate sc-block-builder = { workspace = true } diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index 4db1948c5e..43784f36f4 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -24,14 +24,14 @@ rand = "0.8" rlp = { workspace = true } scale-codec = { package = "parity-scale-codec", workspace = true } schnellru = "0.2.3" -serde = { workspace = true } +serde = { workspace = true, optional = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["sync"] } # Substrate prometheus-endpoint = { workspace = true } sc-client-api = { workspace = true } -sc-consensus-aura = { workspace = true } +sc-consensus-aura = { workspace = true, optional = true } sc-network = { workspace = true } sc-network-sync = { workspace = true } sc-rpc = { workspace = true } @@ -43,7 +43,7 @@ sp-api = { workspace = true, features = ["default"] } sp-block-builder = { workspace = true, features = ["default"] } sp-blockchain = { workspace = true } sp-consensus = { workspace = true } -sp-consensus-aura = { workspace = true, features = ["default"] } +sp-consensus-aura = { workspace = true, features = ["default"], optional = true } sp-core = { workspace = true, features = ["default"] } sp-externalities = { workspace = true, features = ["default"] } sp-inherents = { workspace = true, features = ["default"] } @@ -51,7 +51,7 @@ sp-io = { workspace = true, features = ["default"] } sp-runtime = { workspace = true, features = ["default"] } sp-state-machine = { workspace = true, features = ["default"] } sp-storage = { workspace = true, features = ["default"] } -sp-timestamp = { workspace = true, features = ["default"] } +sp-timestamp = { workspace = true, features = ["default"], optional = true } # Frontier fc-api = { workspace = true } fc-mapping-sync = { workspace = true } @@ -63,7 +63,7 @@ fp-storage = { workspace = true, features = ["default"] } pallet-evm = { workspace = true, features = ["default"] } [dev-dependencies] -tempfile = "3.10.1" +tempfile = "3.14.0" # Substrate sc-block-builder = { workspace = true } sc-client-db = { workspace = true, features = ["rocksdb"] } @@ -73,11 +73,16 @@ substrate-test-runtime-client = { workspace = true } fc-db = { workspace = true } [features] -default = ["rocksdb"] +default = ["aura", "rocksdb"] +aura = [ + "sc-consensus-aura", + "sp-consensus-aura", + "sp-timestamp", +] rocksdb = [ "sc-service/rocksdb", "fc-db/rocksdb", "fc-mapping-sync/rocksdb", ] -txpool = ["fc-rpc-core/txpool"] +txpool = ["fc-rpc-core/txpool", "serde"] rpc-binary-search-estimate = [] diff --git a/client/rpc/src/eth/pending.rs b/client/rpc/src/eth/pending.rs index f03522c9a3..48726b6cd7 100644 --- a/client/rpc/src/eth/pending.rs +++ b/client/rpc/src/eth/pending.rs @@ -16,13 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::{marker::PhantomData, sync::Arc}; - // Substrate -use sc_client_api::{ - backend::{AuxStore, Backend, StorageProvider}, - UsageProvider, -}; +use sc_client_api::backend::{Backend, StorageProvider}; use sc_transaction_pool::ChainApi; use sc_transaction_pool_api::InPoolTransaction; use sp_api::{ApiExt, ApiRef, Core, ProvideRuntimeApi}; @@ -30,11 +25,10 @@ use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{ApplyExtrinsicFailed, HeaderBackend}; use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider}; use sp_runtime::{ - generic::{Digest, DigestItem}, + generic::Digest, traits::{Block as BlockT, Header as HeaderT, One}, TransactionOutcome, }; -use sp_timestamp::TimestampInherentData; use crate::eth::Eth; use fp_rpc::EthereumRuntimeRPCApi; @@ -169,14 +163,20 @@ impl ConsensusDataProvider for () { } } +#[cfg(feature = "aura")] pub use self::aura::AuraConsensusDataProvider; +#[cfg(feature = "aura")] mod aura { use super::*; + use sc_client_api::{AuxStore, UsageProvider}; use sp_consensus_aura::{ digests::CompatibleDigestItem, sr25519::{AuthorityId, AuthoritySignature}, AuraApi, Slot, SlotDuration, }; + use sp_runtime::generic::DigestItem; + use sp_timestamp::TimestampInherentData; + use std::{marker::PhantomData, sync::Arc}; /// Consensus data provider for Aura. pub struct AuraConsensusDataProvider { diff --git a/docs/accounts.md b/docs/accounts.md index 207a06d768..c61681ca70 100644 --- a/docs/accounts.md +++ b/docs/accounts.md @@ -4,7 +4,7 @@ Frontier provides two different strategies for handling `H160` addresses. ## H256 -> H160 mapping -The first strategy consists of of a truncated hash scheme, where the first 160 LE bytes of a `H256` address are used to form the `H160` address. +The first strategy consists of a truncated hash scheme, where the first 160 LE bytes of a `H256` address are used to form the `H160` address. `AccountId32` is the Account type used for `frame_system::pallet::Config::AccountId`. diff --git a/docs/overview.md b/docs/overview.md index 9954fe6c6f..6e7e674b5f 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -31,10 +31,10 @@ If this is the intended way of usage, take a look at the [`pallet-ethereum`](../ An Ethereum-based blockchain can use the pre-block feeding strategy to migrate to Substrate. In the post-block generation model, the Ethereum block is generated *after* runtime execution. -In the pre-block feeding model, the Ethereum block is feeded in *before* runtime execution. +In the pre-block feeding model, the Ethereum block is fed in *before* runtime execution. A blockchain can first use pre-block feeding with empty extrinsic requirement. -In this way, because no other external information is feeded, combined with a suitable consensus engine, one Ethereum block will have an exact corresponding Substrate block. +In this way, because no other external information is fed, combined with a suitable consensus engine, one Ethereum block will have an exact corresponding Substrate block. This is called the [wrapper block](https://corepaper.org/substrate/wrapper/) strategy, and it allows Frontier to function as a normal Ethereum client. With a sufficient number of the network running a Frontier node, the blockchain can then initiate a hard fork, allowing extrinsic to be added in. diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index a211f2478e..2a4b4d36f9 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -255,7 +255,6 @@ pub mod pallet { UniqueSaturatedInto::::unique_saturated_into(to_remove), )); } - Pending::::kill(); } fn on_initialize(_: BlockNumberFor) -> Weight { @@ -348,10 +347,10 @@ pub mod pallet { PreLogExists, } - /// Current building block's transactions and receipts. + /// Mapping from transaction index to transaction in the current building block. #[pallet::storage] pub type Pending = - StorageValue<_, Vec<(Transaction, TransactionStatus, Receipt)>, ValueQuery>; + CountedStorageMap<_, Identity, u32, (Transaction, TransactionStatus, Receipt), OptionQuery>; /// The current Ethereum block. #[pallet::storage] @@ -436,22 +435,25 @@ impl Pallet { } fn store_block(post_log: Option, block_number: U256) { - let mut transactions = Vec::new(); - let mut statuses = Vec::new(); - let mut receipts = Vec::new(); + let transactions_count = Pending::::count(); + let mut transactions = Vec::with_capacity(transactions_count as usize); + let mut statuses = Vec::with_capacity(transactions_count as usize); + let mut receipts = Vec::with_capacity(transactions_count as usize); let mut logs_bloom = Bloom::default(); let mut cumulative_gas_used = U256::zero(); - for (transaction, status, receipt) in Pending::::get() { - transactions.push(transaction); - statuses.push(status); - receipts.push(receipt.clone()); - let (logs, used_gas) = match receipt { - Receipt::Legacy(d) | Receipt::EIP2930(d) | Receipt::EIP1559(d) => { - (d.logs.clone(), d.used_gas) - } - }; - cumulative_gas_used = used_gas; - Self::logs_bloom(logs, &mut logs_bloom); + for transaction_index in 0..transactions_count { + if let Some((transaction, status, receipt)) = Pending::::take(transaction_index) { + transactions.push(transaction); + statuses.push(status); + receipts.push(receipt.clone()); + let (logs, used_gas) = match receipt { + Receipt::Legacy(d) | Receipt::EIP2930(d) | Receipt::EIP1559(d) => { + (d.logs.clone(), d.used_gas) + } + }; + cumulative_gas_used = used_gas; + Self::logs_bloom(logs, &mut logs_bloom); + } } let ommers = Vec::::new(); @@ -597,9 +599,8 @@ impl Pallet { ) -> Result<(PostDispatchInfo, CallOrCreateInfo), DispatchErrorWithPostInfo> { let (to, _, info) = Self::execute(source, &transaction, None)?; - let pending = Pending::::get(); let transaction_hash = transaction.hash(); - let transaction_index = pending.len() as u32; + let transaction_index = Pending::::count(); let (reason, status, weight_info, used_gas, dest, extra_data) = match info.clone() { CallOrCreateInfo::Call(info) => ( @@ -675,7 +676,9 @@ impl Pallet { }; let logs_bloom = status.logs_bloom; let logs = status.clone().logs; - let cumulative_gas_used = if let Some((_, _, receipt)) = pending.last() { + let cumulative_gas_used = if let Some((_, _, receipt)) = + Pending::::get(transaction_index.saturating_sub(1)) + { match receipt { Receipt::Legacy(d) | Receipt::EIP2930(d) | Receipt::EIP1559(d) => { d.used_gas.saturating_add(used_gas.effective) @@ -706,7 +709,7 @@ impl Pallet { } }; - Pending::::append((transaction, status, receipt)); + Pending::::insert(transaction_index, (transaction, status, receipt)); Self::deposit_event(Event::Executed { from: source, diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index a5ee70e2b0..d190d8b6be 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -16,7 +16,7 @@ environmental = { workspace = true, optional = true } evm = { workspace = true, features = ["with-codec"] } hash-db = { workspace = true } hex-literal = { workspace = true } -impl-trait-for-tuples = "0.2.2" +impl-trait-for-tuples = "0.2.3" log = { workspace = true } scale-codec = { package = "parity-scale-codec", workspace = true } scale-info = { workspace = true } diff --git a/frame/evm/precompile/bls12377/src/lib.rs b/frame/evm/precompile/bls12377/src/lib.rs index 9d7700b0ed..47310156dc 100644 --- a/frame/evm/precompile/bls12377/src/lib.rs +++ b/frame/evm/precompile/bls12377/src/lib.rs @@ -590,7 +590,7 @@ impl Bls12377MapG1 { impl Precompile for Bls12377MapG1 { /// Implements EIP-2539 Map_To_G1 precompile. - /// > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field. + /// > Field-to-curve call expects `64` bytes as an input that is interpreted as an element of the base field. /// > Output of this call is `128` bytes and is G1 point following respective encoding rules. fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { handle.record_cost(Bls12377MapG1::GAS_COST)?; @@ -629,7 +629,7 @@ impl Bls12377MapG2 { impl Precompile for Bls12377MapG2 { /// Implements EIP-2539 Map_FP2_TO_G2 precompile logic. - /// > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field. + /// > Field-to-curve call expects `128` bytes as an input that is interpreted as an element of the quadratic extension field. /// > Output of this call is `256` bytes and is G2 point following respective encoding rules. fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { handle.record_cost(Bls12377MapG2::GAS_COST)?; diff --git a/frame/evm/precompile/storage-cleaner/src/lib.rs b/frame/evm/precompile/storage-cleaner/src/lib.rs index 1660785aa0..07a3dc6194 100644 --- a/frame/evm/precompile/storage-cleaner/src/lib.rs +++ b/frame/evm/precompile/storage-cleaner/src/lib.rs @@ -91,7 +91,7 @@ where .count(); deleted_entries = deleted_entries.saturating_add(deleted as u64); - // Check if the storage of this contract has been completly removed + // Check if the storage of this contract has been completely removed if pallet_evm::AccountStorages::::iter_key_prefix(address) .next() .is_none() diff --git a/frame/evm/src/runner/meter.rs b/frame/evm/src/runner/meter.rs index 374895a753..bd185e9da1 100644 --- a/frame/evm/src/runner/meter.rs +++ b/frame/evm/src/runner/meter.rs @@ -50,12 +50,13 @@ impl StorageMeter { /// Records the given amount of storage usage. The amount is added to the current usage. /// If the limit is reached, an error is returned. pub fn record(&mut self, amount: u64) -> Result<(), MeterError> { - let usage = self - .usage - .checked_add(amount) - .ok_or(MeterError::LimitExceeded)?; + let usage = self.usage.checked_add(amount).ok_or_else(|| { + fp_evm::set_storage_oog(); + MeterError::LimitExceeded + })?; if usage > self.limit { + fp_evm::set_storage_oog(); return Err(MeterError::LimitExceeded); } self.usage = usage; diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index e666a36532..616a80efed 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -262,7 +262,6 @@ where let fee = T::OnChargeTransaction::withdraw_fee(&source, total_fee) .map_err(|e| RunnerError { error: e, weight })?; - // Execute the EVM call. let vicinity = Vicinity { gas_price: base_fee, origin: source, @@ -281,28 +280,34 @@ where let state = SubstrateStackState::new(&vicinity, metadata, maybe_weight_info, storage_limit); let mut executor = StackExecutor::new_with_precompiles(state, config, precompiles); - let (reason, retv) = f(&mut executor); + // Execute the EVM call. + let (reason, retv, used_gas, effective_gas) = + fp_evm::handle_storage_oog::(gas_limit, || { + let (reason, retv) = f(&mut executor); + + // Compute the storage gas cost based on the storage growth. + let storage_gas = match &executor.state().storage_meter { + Some(storage_meter) => storage_meter.storage_to_gas(storage_growth_ratio), + None => 0, + }; - // Compute the storage gas cost based on the storage growth. - let storage_gas = match &executor.state().storage_meter { - Some(storage_meter) => storage_meter.storage_to_gas(storage_growth_ratio), - None => 0, - }; + let pov_gas = match executor.state().weight_info() { + Some(weight_info) => weight_info + .proof_size_usage + .unwrap_or_default() + .saturating_mul(T::GasLimitPovSizeRatio::get()), + None => 0, + }; - let pov_gas = match executor.state().weight_info() { - Some(weight_info) => weight_info - .proof_size_usage - .unwrap_or_default() - .saturating_mul(T::GasLimitPovSizeRatio::get()), - None => 0, - }; + // Post execution. + let used_gas = executor.used_gas(); + let effective_gas = U256::from(core::cmp::max( + core::cmp::max(used_gas, pov_gas), + storage_gas, + )); - // Post execution. - let used_gas = executor.used_gas(); - let effective_gas = U256::from(core::cmp::max( - core::cmp::max(used_gas, pov_gas), - storage_gas, - )); + (reason, retv, used_gas, effective_gas) + }); let actual_fee = effective_gas.saturating_mul(total_fee_per_gas); let actual_base_fee = effective_gas.saturating_mul(base_fee); @@ -1038,6 +1043,7 @@ where let actual_size = Pallet::::account_code_metadata(address).size; if actual_size > pre_size { + fp_evm::set_storage_oog(); return Err(ExitError::OutOfGas); } // Refund unused proof size diff --git a/precompiles/macro/Cargo.toml b/precompiles/macro/Cargo.toml index 37d7d46e68..dc8b9cb7b6 100644 --- a/precompiles/macro/Cargo.toml +++ b/precompiles/macro/Cargo.toml @@ -15,7 +15,7 @@ path = "tests/tests.rs" [dependencies] case = "1.0" num_enum = { workspace = true } -prettyplease = "0.2.24" +prettyplease = "0.2.25" proc-macro2 = "1.0" quote = "1.0" sp-crypto-hashing = { workspace = true } diff --git a/precompiles/src/evm/costs.rs b/precompiles/src/evm/costs.rs index fe18b88e3d..ac0d5b9198 100644 --- a/precompiles/src/evm/costs.rs +++ b/precompiles/src/evm/costs.rs @@ -25,7 +25,7 @@ use sp_core::U256; pub fn log_costs(topics: usize, data_len: usize) -> EvmResult { // Cost calculation is copied from EVM code that is not publicly exposed by the crates. - // https://github.com/rust-blockchain/evm/blob/master/gasometer/src/costs.rs#L148 + // https://github.com/rust-ethereum/evm/blob/master/src/standard/gasometer/costs.rs#L148 const G_LOG: u64 = 375; const G_LOGDATA: u64 = 8; diff --git a/precompiles/src/precompile_set.rs b/precompiles/src/precompile_set.rs index 2873f28682..6f54214bfb 100644 --- a/precompiles/src/precompile_set.rs +++ b/precompiles/src/precompile_set.rs @@ -129,6 +129,7 @@ impl From> for IsPrecompileResult { pub enum PrecompileKind { Single(H160), Prefixed(Vec), + Multiple(Vec), } #[derive(Debug, Clone)] @@ -716,7 +717,7 @@ where Ok(mut recursion_level_map) => { let recursion_level = match recursion_level_map.get_mut(&code_address) { Some(recursion_level) => recursion_level, - None => return Some(Err(revert("Couldn't retreive precompile nesting"))), + None => return Some(Err(revert("Couldn't retrieve precompile nesting"))), }; *recursion_level -= 1; @@ -833,6 +834,68 @@ impl IsActivePrecompile for RevertPrecompile { } } +/// Precompiles that were removed from a precompile set. +/// Still considered precompiles but are inactive and always revert. +pub struct RemovedPrecompilesAt(PhantomData); +impl PrecompileSetFragment for RemovedPrecompilesAt +where + A: Get>, +{ + #[inline(always)] + fn new() -> Self { + Self(PhantomData) + } + + #[inline(always)] + fn execute( + &self, + handle: &mut impl PrecompileHandle, + ) -> Option { + if A::get().contains(&handle.code_address()) { + Some(Err(revert("Removed precompile"))) + } else { + None + } + } + + #[inline(always)] + fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: A::get().contains(&address), + extra_cost: 0, + } + } + + #[inline(always)] + fn used_addresses(&self) -> Vec { + A::get() + } + + fn summarize_checks(&self) -> Vec { + vec![PrecompileCheckSummary { + name: None, + precompile_kind: PrecompileKind::Multiple(A::get()), + recursion_limit: Some(0), + accept_delegate_call: true, + callable_by_smart_contract: "Reverts in all cases".into(), + callable_by_precompile: "Reverts in all cases".into(), + }] + } +} + +impl IsActivePrecompile for RemovedPrecompilesAt +where + Self: PrecompileSetFragment, +{ + #[inline(always)] + fn is_active_precompile(&self, _address: H160, _gas: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } + } +} + /// A precompile that was removed from a precompile set. /// Still considered a precompile but is inactive and always revert. pub struct RemovedPrecompileAt(PhantomData); diff --git a/precompiles/src/solidity/codec/bytes.rs b/precompiles/src/solidity/codec/bytes.rs index d237a3b8b8..26566dea00 100644 --- a/precompiles/src/solidity/codec/bytes.rs +++ b/precompiles/src/solidity/codec/bytes.rs @@ -53,7 +53,7 @@ impl Kind for StringKind { /// The `bytes/string` type of Solidity. /// It is different from `Vec` which will be serialized with padding for each `u8` element /// of the array, while `Bytes` is tightly packed. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug)] pub struct BoundedBytesString { data: Vec, _phantom: PhantomData<(K, S)>, @@ -68,6 +68,20 @@ impl> Clone for BoundedBytesString { } } +impl PartialEq> for BoundedBytesString { + fn eq(&self, other: &BoundedBytesString) -> bool { + self.data.eq(&other.data) + } +} + +impl Eq for BoundedBytesString {} + +impl Default for BoundedBytesString { + fn default() -> Self { + Vec::default().into() + } +} + impl> BoundedBytesString { pub fn as_bytes(&self) -> &[u8] { &self.data diff --git a/precompiles/src/solidity/codec/mod.rs b/precompiles/src/solidity/codec/mod.rs index ae49888a1f..52384a67b3 100644 --- a/precompiles/src/solidity/codec/mod.rs +++ b/precompiles/src/solidity/codec/mod.rs @@ -37,7 +37,7 @@ pub use native::{Address, BoundedVec}; // derive macro pub use precompile_utils_macro::Codec; -/// Data that can be encoded/encoded followiong the Solidity ABI Specification. +/// Data that can be encoded/encoded following the Solidity ABI Specification. pub trait Codec: Sized { fn read(reader: &mut Reader) -> MayRevert; fn write(writer: &mut Writer, value: Self); diff --git a/precompiles/src/solidity/codec/native.rs b/precompiles/src/solidity/codec/native.rs index c83eed4d46..bc35cf3c27 100644 --- a/precompiles/src/solidity/codec/native.rs +++ b/precompiles/src/solidity/codec/native.rs @@ -322,7 +322,7 @@ impl> Codec for BoundedVec { for inner in value { // Any offset in items are relative to the start of the item instead of the - // start of the array. However if there is offseted data it must but appended after + // start of the array. However if there is offset data it must but appended after // all items (offsets) are written. We thus need to rely on `compute_offsets` to do // that, and must store a "shift" to correct the offsets. let shift = inner_writer.data.len(); @@ -380,3 +380,22 @@ impl From> for Vec { value.inner } } + +impl Default for BoundedVec { + fn default() -> Self { + Self { + inner: Default::default(), + _phantom: PhantomData, + } + } +} + +impl BoundedVec { + pub fn len(&self) -> usize { + self.inner.len() + } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} diff --git a/precompiles/src/testing/account.rs b/precompiles/src/testing/account.rs index 4d524eae29..2834bf6a5f 100644 --- a/precompiles/src/testing/account.rs +++ b/precompiles/src/testing/account.rs @@ -19,7 +19,7 @@ use pallet_evm::AddressMapping; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; -use sp_core::{Decode, Encode, MaxEncodedLen, H160, H256}; +use sp_core::{keccak_256, Decode, Encode, MaxEncodedLen, H160, H256}; #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] #[derive(Serialize, Deserialize, derive_more::Display)] @@ -99,6 +99,94 @@ impl sp_runtime::traits::Convert for MockAccount { } } +#[derive( + Eq, + PartialEq, + Clone, + Encode, + Decode, + sp_core::RuntimeDebug, + TypeInfo, + Serialize, + Deserialize +)] +pub struct MockSignature(sp_core::ecdsa::Signature); + +impl From for MockSignature { + fn from(x: sp_core::ecdsa::Signature) -> Self { + MockSignature(x) + } +} + +impl From for MockSignature { + fn from(signature: sp_runtime::MultiSignature) -> Self { + match signature { + sp_runtime::MultiSignature::Ed25519(_) => { + panic!("Ed25519 not supported for MockSignature") + } + sp_runtime::MultiSignature::Sr25519(_) => { + panic!("Sr25519 not supported for MockSignature") + } + sp_runtime::MultiSignature::Ecdsa(sig) => Self(sig), + } + } +} + +impl sp_runtime::traits::Verify for MockSignature { + type Signer = MockSigner; + fn verify>(&self, mut msg: L, signer: &MockAccount) -> bool { + let mut m = [0u8; 32]; + m.copy_from_slice(keccak_256(msg.get()).as_slice()); + match sp_io::crypto::secp256k1_ecdsa_recover(self.0.as_ref(), &m) { + Ok(pubkey) => { + MockAccount(sp_core::H160::from_slice( + &keccak_256(&pubkey).as_slice()[12..32], + )) == *signer + } + Err(sp_io::EcdsaVerifyError::BadRS) => { + log::error!(target: "evm", "Error recovering: Incorrect value of R or S"); + false + } + Err(sp_io::EcdsaVerifyError::BadV) => { + log::error!(target: "evm", "Error recovering: Incorrect value of V"); + false + } + Err(sp_io::EcdsaVerifyError::BadSignature) => { + log::error!(target: "evm", "Error recovering: Invalid signature"); + false + } + } + } +} + +/// Public key for an Ethereum compatible account +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Clone, + Encode, + Decode, + sp_core::RuntimeDebug, + TypeInfo +)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] +pub struct MockSigner([u8; 20]); + +impl sp_runtime::traits::IdentifyAccount for MockSigner { + type AccountId = MockAccount; + fn into_account(self) -> MockAccount { + MockAccount(self.0.into()) + } +} + +impl From<[u8; 20]> for MockSigner { + fn from(x: [u8; 20]) -> Self { + MockSigner(x) + } +} + #[macro_export] macro_rules! mock_account { ($name:ident, $convert:expr) => { diff --git a/precompiles/src/testing/handle.rs b/precompiles/src/testing/handle.rs index ab6207786a..da6c2e286b 100644 --- a/precompiles/src/testing/handle.rs +++ b/precompiles/src/testing/handle.rs @@ -178,17 +178,17 @@ impl PrecompileHandle for MockHandle { Ok(()) } - /// Retreive the code address (what is the address of the precompile being called). + /// Retrieve the code address (what is the address of the precompile being called). fn code_address(&self) -> H160 { self.code_address } - /// Retreive the input data the precompile is called with. + /// Retrieve the input data the precompile is called with. fn input(&self) -> &[u8] { &self.input } - /// Retreive the context in which the precompile is executed. + /// Retrieve the context in which the precompile is executed. fn context(&self) -> &Context { &self.context } @@ -198,7 +198,7 @@ impl PrecompileHandle for MockHandle { self.is_static } - /// Retreive the gas limit of this call. + /// Retrieve the gas limit of this call. fn gas_limit(&self) -> Option { Some(self.gas_limit) } diff --git a/primitives/evm/Cargo.toml b/primitives/evm/Cargo.toml index 9cacc4e27d..1542bd5464 100644 --- a/primitives/evm/Cargo.toml +++ b/primitives/evm/Cargo.toml @@ -11,11 +11,13 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] +environmental = { workspace = true } evm = { workspace = true, features = ["with-codec"] } num_enum = { workspace = true, default-features = false } scale-codec = { package = "parity-scale-codec", workspace = true } scale-info = { workspace = true } serde = { workspace = true, optional = true } + # Substrate frame-support = { workspace = true } sp-core = { workspace = true } @@ -26,6 +28,7 @@ default = ["std"] std = [ "evm/std", "evm/with-serde", + "environmental/std", "num_enum/std", "serde/std", "scale-codec/std", diff --git a/primitives/evm/src/lib.rs b/primitives/evm/src/lib.rs index aca1a0e68a..9249fc85b8 100644 --- a/primitives/evm/src/lib.rs +++ b/primitives/evm/src/lib.rs @@ -22,6 +22,7 @@ extern crate alloc; mod account_provider; mod precompile; +mod storage_oog; mod validation; use alloc::{collections::BTreeMap, vec::Vec}; @@ -45,6 +46,7 @@ pub use self::{ Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, Transfer, }, + storage_oog::{handle_storage_oog, set_storage_oog}, validation::{ CheckEvmTransaction, CheckEvmTransactionConfig, CheckEvmTransactionInput, TransactionValidationError, @@ -118,6 +120,7 @@ impl WeightInfo { fn try_consume(&self, cost: u64, limit: u64, usage: u64) -> Result { let usage = usage.checked_add(cost).ok_or(ExitError::OutOfGas)?; if usage > limit { + storage_oog::set_storage_oog(); return Err(ExitError::OutOfGas); } Ok(usage) @@ -142,6 +145,7 @@ impl WeightInfo { { let proof_size_usage = self.try_consume(cost, proof_size_limit, proof_size_usage)?; if proof_size_usage > proof_size_limit { + storage_oog::set_storage_oog(); return Err(ExitError::OutOfGas); } self.proof_size_usage = Some(proof_size_usage); diff --git a/primitives/evm/src/storage_oog.rs b/primitives/evm/src/storage_oog.rs new file mode 100644 index 0000000000..dd4ad32092 --- /dev/null +++ b/primitives/evm/src/storage_oog.rs @@ -0,0 +1,52 @@ +// This file is part of Frontier. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +environmental::environmental!(STORAGE_OOG: bool); + +use crate::{ExitError, ExitReason}; +use sp_core::U256; + +pub fn handle_storage_oog(gas_limit: u64, f: F) -> (ExitReason, R, u64, U256) +where + F: FnOnce() -> (ExitReason, R, u64, U256), + R: Default, +{ + STORAGE_OOG::using_once(&mut false, || { + let (reason, retv, used_gas, effective_gas) = f(); + + STORAGE_OOG::with(|storage_oog| { + if *storage_oog { + ( + ExitReason::Error(ExitError::OutOfGas), + Default::default(), + used_gas, + U256([gas_limit, 0, 0, 0]), + ) + } else { + (reason, retv, used_gas, effective_gas) + } + }) + // This should always return `Some`, but let's play it safe. + .expect("STORAGE_OOG not defined") + }) +} + +pub fn set_storage_oog() { + STORAGE_OOG::with(|storage_oog| { + *storage_oog = true; + }); +} diff --git a/ts-tests/tests/test-gas.ts b/ts-tests/tests/test-gas.ts index 7da474b7da..0aecf3b4ce 100644 --- a/ts-tests/tests/test-gas.ts +++ b/ts-tests/tests/test-gas.ts @@ -229,6 +229,7 @@ describeWithFrontier("Frontier RPC (Gas limit Weightv2 ref time)", (context) => let nonce = await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT); + const txHashs = []; for (var i = 0; i < CALLS_PER_BLOCK; i++) { let data = contract.methods.storageLoop(1000, TEST_ACCOUNT, i); let tx = await context.web3.eth.accounts.signTransaction( @@ -243,6 +244,7 @@ describeWithFrontier("Frontier RPC (Gas limit Weightv2 ref time)", (context) => GENESIS_ACCOUNT_PRIVATE_KEY ); await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); + txHashs.push(tx.transactionHash); nonce++; } // because we are using Math.floor for everything, at the end there is room for an additional @@ -260,14 +262,22 @@ describeWithFrontier("Frontier RPC (Gas limit Weightv2 ref time)", (context) => GENESIS_ACCOUNT_PRIVATE_KEY ); let r = await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); + txHashs.push(r.result); nonce++; } await createAndFinalizeBlock(context.web3); + for (let tx of txHashs) { + const receipt = await context.web3.eth.getTransactionReceipt(tx); + console.log("gas used", receipt.gasUsed); + } + let latest = await context.web3.eth.getBlock("latest"); expect(latest.transactions.length).to.be.eq(CALLS_PER_BLOCK + TRANSFERS_PER_BLOCK + 1); expect(latest.gasUsed).to.be.lessThanOrEqual(ETH_BLOCK_GAS_LIMIT); + console.log("block gas used", latest.gasUsed); + console.log("value tested", ETH_BLOCK_GAS_LIMIT - latest.gasUsed); expect(ETH_BLOCK_GAS_LIMIT - latest.gasUsed).to.be.lessThan(21_000); }); });