diff --git a/Cargo.lock b/Cargo.lock index a9a57a88d2..da7099925e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -104,9 +104,9 @@ checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] name = "alloc-stdlib" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" dependencies = [ "alloc-no-stdlib", ] @@ -119,9 +119,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-consensus" -version = "1.0.41" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b151e38e42f1586a01369ec52a6934702731d07e8509a7307331b09f6c46dc" +checksum = "12870ab65b131f609257436935047eec3cfabee8809732f6bf5a69fe2a18cf2e" dependencies = [ "alloy-eips", "alloy-primitives", @@ -133,7 +133,7 @@ dependencies = [ "either", "k256", "once_cell", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -146,7 +146,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -172,7 +172,7 @@ dependencies = [ "borsh", "k256", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -186,7 +186,7 @@ dependencies = [ "borsh", "once_cell", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -209,7 +209,7 @@ dependencies = [ "serde", "serde_with", "sha2", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -223,7 +223,7 @@ dependencies = [ "cfg-if 1.0.4", "const-hex", "derive_more 2.1.1", - "foldhash 0.2.0", + "foldhash", "hashbrown 0.16.1", "indexmap 2.14.0", "itoa", @@ -231,34 +231,34 @@ dependencies = [ "keccak-asm", "paste", "proptest", - "rand 0.9.4", + "rand 0.9.5", "rapidhash", "ruint", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "serde", "sha3", ] [[package]] name = "alloy-rlp" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" +checksum = "24671b1f62edcf0f9b62994c7bf72cd621a04a4b99f5020ece1a647b40e2f103" dependencies = [ "alloy-rlp-derive", - "arrayvec 0.7.6", + "arrayvec 0.7.8", "bytes", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" +checksum = "9d4311c03125e8a18296504560b9de3d75ecbd0dcda7f71e6cf2a196d57e6fba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -283,28 +283,27 @@ dependencies = [ "derive_more 2.1.1", "nybbles", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", ] [[package]] name = "alloy-tx-macros" -version = "1.0.41" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e52276fdb553d3c11563afad2898f4085165e4093604afe3d78b69afbf408f" +checksum = "d69722eddcdf1ce096c3ab66cf8116999363f734eb36fe94a148f4f71c85da84" dependencies = [ - "alloy-primitives", - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -376,9 +375,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "aquamarine" @@ -486,7 +485,7 @@ dependencies = [ "ark-ff-macros 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", - "arrayvec 0.7.6", + "arrayvec 0.7.8", "digest 0.10.7", "educe 0.6.0", "itertools 0.13.0", @@ -496,6 +495,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a806ac6c8307b929df4645776290a50ee2aac754ad09d8bdf73391309e43af" +dependencies = [ + "ark-ff-asm 0.6.0", + "ark-ff-macros 0.6.0", + "ark-serialize 0.6.0", + "ark-std 0.6.0", + "digest 0.10.7", + "educe 0.6.0", + "num-bigint", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff-asm" version = "0.3.0" @@ -523,7 +539,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "ark-ff-asm" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1479009684adc073dff49a1025d3a7065b317a9ead25aaaca38cdc70058ba8a2" +dependencies = [ + "quote", + "syn 2.0.119", ] [[package]] @@ -561,7 +587,20 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "ark-ff-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0691ed21ef00ef89c1e9bda832eba493dda3ec2f8d892fb25b705f73f06bb8" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -635,13 +674,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ - "ark-serialize-derive", + "ark-serialize-derive 0.5.0", "ark-std 0.5.0", - "arrayvec 0.7.6", + "arrayvec 0.7.8", "digest 0.10.7", "num-bigint", ] +[[package]] +name = "ark-serialize" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74dd304fd536fb95d0a328e72be759209cc496a9da094c5bc56e5fea4f9e86b" +dependencies = [ + "ark-serialize-derive 0.6.0", + "ark-std 0.6.0", + "digest 0.10.7", + "num-bigint", + "serde_with", +] + [[package]] name = "ark-serialize-derive" version = "0.5.0" @@ -650,7 +702,18 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f153690697a2b91e5e1251ff98411ee5371500a111a0fd317a70e588eb300f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -660,7 +723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -670,7 +733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -680,7 +743,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", +] + +[[package]] +name = "ark-std" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "367c9c827ed431bff6868b7aa926e05b16eb46603cc8b6e768e4a5553fa1d155" +dependencies = [ + "num-traits", + "rand 0.8.7", ] [[package]] @@ -700,9 +773,9 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "assert-json-diff" @@ -722,9 +795,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" +checksum = "3976abdc8fe7d1133d43d304afd42abdf5bc3e1319d263d223bde07b5efc4be8" dependencies = [ "compression-codecs", "compression-core", @@ -734,13 +807,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -788,7 +861,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -863,7 +936,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -874,7 +947,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex 1.3.0", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -892,20 +965,40 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bitcoin-consensus-encoding" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +dependencies = [ + "bitcoin-internals", + "hex-conservative 1.2.0", + "serde", +] + +[[package]] +name = "bitcoin-internals" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" + [[package]] name = "bitcoin-io" -version = "0.1.100" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11301df0b06f22dea7bb1916403fdd88a371031e495c49b8f96931b28189e175" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] [[package]] name = "bitcoin_hashes" -version = "0.14.100" +version = "0.14.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9901a56e133a1fc86eeb1113e2591f45f4682451ca893bff494d2f88918e3f" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", - "hex-conservative", + "hex-conservative 0.2.2", ] [[package]] @@ -916,18 +1009,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" dependencies = [ "serde_core", ] [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -976,12 +1069,11 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.5" +version = "1.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" dependencies = [ - "arrayref", - "arrayvec 0.7.6", + "arrayvec 0.7.8", "cc", "cfg-if 1.0.4", "constant_time_eq 0.4.2", @@ -999,12 +1091,12 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +checksum = "c20659f9bbee16cbbd2f7393e40ab6309f5a98f76a2eb57a995ec508b72387fe" dependencies = [ "cc", - "glob 0.3.3", + "glob 0.3.4", "threadpool", "zeroize", ] @@ -1022,9 +1114,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" dependencies = [ "borsh-derive", "bytes", @@ -1033,22 +1125,22 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +checksum = "d8f347189c62a579b8cd5f80714efa178f52e461dc2e6d701d264f5ff22e566c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "brotli" -version = "8.0.3" +version = "8.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1057,9 +1149,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "5.0.1" +version = "5.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1107,12 +1199,12 @@ dependencies = [ [[package]] name = "bytecheck" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0caa33a2c0edca0419d15ac723dff03f1956f7978329b1e3b5fdaaaed9d3ca8b" +checksum = "26333eeac754f0ad8a6bcd0eb0ac012156302e4e16b852b72ee399aea4f12c29" dependencies = [ - "bytecheck_derive 0.8.2", - "ptr_meta 0.3.1", + "bytecheck_derive 0.8.3", + "ptr_meta 0.3.2", "rancor", "simdutf8", ] @@ -1130,20 +1222,20 @@ dependencies = [ [[package]] name = "bytecheck_derive" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" +checksum = "46d07918caa9eeaaf06b7873925c53a61daac173539b4f7715090745e44e4e69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] name = "byteorder" @@ -1153,22 +1245,22 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" dependencies = [ "serde", ] [[package]] name = "c-kzg" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" +checksum = "38d04308254695569fdb9bfe3bacc1c91837a670d0806605eb82d63748fbd3a6" dependencies = [ "blst", "cc", - "glob 0.3.3", + "glob 0.3.4", "hex", "libc", "once_cell", @@ -1201,9 +1293,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.2" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" dependencies = [ "serde_core", ] @@ -1225,7 +1317,7 @@ dependencies = [ "cargo_metadata 0.19.2", "casper-contract-sdk", "casper-contract-sdk-sys", - "clap 4.6.1", + "clap 4.6.6", "clap-cargo", "crossterm", "include_dir", @@ -1233,7 +1325,7 @@ dependencies = [ "once_cell", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", "wabt", ] @@ -1271,7 +1363,7 @@ dependencies = [ "semver 1.0.28", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1284,7 +1376,7 @@ dependencies = [ "num-derive", "num-traits", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_json", "serde_test", @@ -1317,7 +1409,7 @@ dependencies = [ "proc-macro2", "quote", "static_assertions", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -1325,7 +1417,7 @@ name = "casper-contract-sdk" version = "0.1.3" dependencies = [ "base16", - "bitflags 2.13.0", + "bitflags 2.13.1", "bnum", "borsh", "bytes", @@ -1333,15 +1425,15 @@ dependencies = [ "casper-contract-sdk-sys", "casper-executor-wasm-common", "cfg-if 1.0.4", - "clap 4.6.1", + "clap 4.6.6", "const-fnv1a-hash", "impl-trait-for-tuples", "linkme", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1354,7 +1446,7 @@ dependencies = [ "indexmap 2.14.0", "serde", "serde_json", - "syn 2.0.117", + "syn 2.0.119", "tempfile", "trybuild", ] @@ -1379,7 +1471,7 @@ dependencies = [ "num-rational", "num-traits", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "serde", "tempfile", "toml 0.5.11", @@ -1412,7 +1504,7 @@ dependencies = [ "num-rational", "num-traits", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "regex", "serde", "serde_json", @@ -1437,7 +1529,7 @@ dependencies = [ "casper-wasm", "casper-wasm-utils", "casper-wasmi", - "clap 4.6.1", + "clap 4.6.6", "criterion", "datasize", "either", @@ -1455,7 +1547,7 @@ dependencies = [ "num_cpus", "once_cell", "proptest", - "rand 0.8.6", + "rand 0.8.7", "rand_chacha 0.3.1", "schemars 0.8.22", "serde", @@ -1482,7 +1574,7 @@ dependencies = [ "casper-storage", "casper-types", "revm", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1507,7 +1599,7 @@ dependencies = [ "parking_lot", "serde_json", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", ] @@ -1515,7 +1607,7 @@ dependencies = [ name = "casper-executor-wasm-common" version = "0.1.3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "blake2 0.10.6", "borsh", "casper-contract-sdk-sys", @@ -1524,7 +1616,7 @@ dependencies = [ "num-traits", "safe-transmute", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1542,7 +1634,7 @@ dependencies = [ "num-traits", "parking_lot", "safe-transmute", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", ] @@ -1556,7 +1648,7 @@ dependencies = [ "casper-storage", "casper-types", "parking_lot", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1642,7 +1734,7 @@ dependencies = [ "proptest", "proptest-derive", "quanta", - "rand 0.8.6", + "rand 0.8.7", "rand_chacha 0.3.1", "rand_core 0.6.4", "regex", @@ -1708,7 +1800,7 @@ dependencies = [ "parking_lot", "pprof", "proptest", - "rand 0.8.6", + "rand 0.8.7", "rand_chacha 0.3.1", "serde", "serde_json", @@ -1754,7 +1846,7 @@ dependencies = [ "proptest", "proptest-attr-macro", "proptest-derive", - "rand 0.8.6", + "rand 0.8.7", "rand_pcg", "schemars 0.8.22", "serde", @@ -1776,7 +1868,7 @@ dependencies = [ name = "casper-updater" version = "0.4.0" dependencies = [ - "clap 4.6.1", + "clap 4.6.6", "once_cell", "regex", "semver 1.0.28", @@ -1855,9 +1947,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.64" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "shlex 2.0.1", @@ -1886,9 +1978,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "change_bid_public_key" @@ -1939,11 +2031,11 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" dependencies = [ - "glob 0.3.3", + "glob 0.3.4", "libc", "libloading", ] @@ -1982,12 +2074,12 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", - "clap_derive 4.6.1", + "clap_derive 4.6.4", ] [[package]] @@ -1998,14 +2090,14 @@ checksum = "23b2ea69cefa96b848b73ad516ad1d59a195cdf9263087d977f648a818c8b43e" dependencies = [ "anstyle", "cargo_metadata 0.18.1", - "clap 4.6.1", + "clap 4.6.6", ] [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -2029,14 +2121,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -2315,9 +2407,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if 1.0.4", ] @@ -2385,7 +2477,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.6.1", + "clap 4.6.6", "criterion-plot", "is-terminal", "itertools 0.10.5", @@ -2414,9 +2506,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -2433,18 +2525,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crossterm" @@ -2452,11 +2544,11 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "crossterm_winapi", "derive_more 2.1.1", "document-features", - "mio 1.2.1", + "mio 1.2.2", "parking_lot", "rustix", "signal-hook", @@ -2545,7 +2637,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2589,7 +2681,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2602,8 +2694,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2616,7 +2707,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2627,7 +2718,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2638,7 +2729,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2649,7 +2740,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2668,9 +2759,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "datasize" @@ -2702,7 +2793,38 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "uuid 1.23.3", + "uuid 1.25.0", +] + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.20", ] [[package]] @@ -2751,7 +2873,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2764,7 +2886,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2786,7 +2908,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.117", + "syn 2.0.119", "unicode-xid", ] @@ -2885,13 +3007,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -2969,13 +3091,13 @@ version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d36219658beb39702975c707dee7895943ca281ca46eebbc5ea395171b9c182b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "byteorder", "lazy_static", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -2987,7 +3109,7 @@ dependencies = [ "byteorder", "dynasm", "fnv", - "memmap2 0.9.10", + "memmap2 0.9.11", ] [[package]] @@ -3046,10 +3168,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" dependencies = [ - "enum-ordinalize 4.3.2", + "enum-ordinalize 4.4.2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3233,9 +3355,9 @@ version = "0.1.0" [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" dependencies = [ "serde", ] @@ -3342,34 +3464,34 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "enum-ordinalize" -version = "4.3.2" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +checksum = "89dd01549b09589510cf0647475075d12071456586d70f5c75c98ae2a5537677" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.2" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] name = "enumset" -version = "1.1.13" +version = "1.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839c4174b41e75c8f7306110b2c51996a293b8d1d850edd529011841d9fede7d" +checksum = "ccc5801fd11762e24d1e420d01d2ac518f2a2ca4329d4fbb6639f2412b6204e0" dependencies = [ "enumset_derive", ] @@ -3383,7 +3505,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3416,7 +3538,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -3472,9 +3594,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fastrlp" @@ -3482,7 +3604,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" dependencies = [ - "arrayvec 0.7.6", + "arrayvec 0.7.8", "auto_impl", "bytes", ] @@ -3493,7 +3615,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" dependencies = [ - "arrayvec 0.7.6", + "arrayvec 0.7.8", "auto_impl", "bytes", ] @@ -3560,9 +3682,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "findshlibs" @@ -3583,7 +3705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.6", + "rand 0.8.7", "rustc-hex", "static_assertions", ] @@ -3604,12 +3726,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "foldhash" version = "0.2.0" @@ -3664,9 +3780,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -3679,9 +3795,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -3689,15 +3805,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -3706,38 +3822,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -3877,15 +3993,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if 1.0.4", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] @@ -4005,9 +4119,9 @@ checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "global-state-update-gen" @@ -4022,7 +4136,7 @@ dependencies = [ "clap 2.34.0", "itertools 0.10.5", "lmdb-rkv", - "rand 0.8.6", + "rand 0.8.7", "serde", "toml 0.5.11", ] @@ -4061,7 +4175,7 @@ dependencies = [ "indexmap 2.14.0", "slab", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.19", "tracing", ] @@ -4098,7 +4212,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "foldhash 0.1.5", ] [[package]] @@ -4107,7 +4220,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "foldhash 0.2.0", + "foldhash", "serde", "serde_core", ] @@ -4221,7 +4334,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ - "arrayvec 0.7.6", + "arrayvec 0.7.8", +] + +[[package]] +name = "hex-conservative" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +dependencies = [ + "arrayvec 0.7.8", ] [[package]] @@ -4259,7 +4381,7 @@ version = "0.1.0" dependencies = [ "casper-contract", "casper-types", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -4286,9 +4408,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -4319,9 +4441,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "hyper" @@ -4386,9 +4508,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -4400,9 +4522,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -4413,9 +4535,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -4427,16 +4549,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -4447,15 +4570,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -4516,7 +4639,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -4595,9 +4718,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "ipnetwork" @@ -4667,11 +4790,64 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "js-sys" -version = "0.3.100" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if 1.0.4", "futures-util", @@ -4749,9 +4925,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc46bac87ef8093eed6f272babb833b6443374399985ac8ed28471ee0918545" +checksum = "c83bff1d572d6b9aeef67ddfc8448e4a3737909cb28e81f97c791b9018703e52" [[package]] name = "leb128fmt" @@ -4761,9 +4937,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libloading" @@ -4810,7 +4986,7 @@ checksum = "b01f197a15988fb5b2ec0a5a9800c97e70771499c456ad757d63b3c5e9b96e75" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -4837,9 +5013,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "litrs" @@ -4889,9 +5065,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" dependencies = [ "serde_core", "value-bag", @@ -4921,7 +5097,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb4bdc8b0ce69932332cf76d24af69c3a155242af95c226b2ab6c2e371ed1149" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.20", "zerocopy", "zerocopy-derive", ] @@ -4967,9 +5143,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" @@ -4982,9 +5158,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -5066,9 +5242,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "log", @@ -5096,7 +5272,7 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.8", + "spin 0.9.9", "version_check", ] @@ -5125,7 +5301,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5260,9 +5436,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -5291,7 +5467,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5300,26 +5476,25 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ - "arrayvec 0.7.6", + "arrayvec 0.7.8", "itoa", ] [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -5375,7 +5550,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5438,11 +5613,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.80" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if 1.0.4", "foreign-types", "libc", @@ -5458,7 +5633,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5478,9 +5653,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.116" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -5538,7 +5713,7 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ - "arrayvec 0.7.6", + "arrayvec 0.7.8", "bitvec", "byte-slice-cast", "const_format", @@ -5557,7 +5732,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5616,9 +5791,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.6" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "5a07a60cc7a4d00c91f95c685609d1d2f79050e6804b70ebedd7650f0b839bcf" dependencies = [ "memchr", "ucd-trie", @@ -5655,7 +5830,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5684,7 +5859,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5705,9 +5880,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "plotters" @@ -5797,7 +5972,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8588067671d03c9f4254b2e66fecb4d8b93b5d3e703195b84f311cd137e32130" dependencies = [ - "glob 0.3.3", + "glob 0.3.4", "pnet_base", "pnet_macros", "pnet_macros_support", @@ -5825,11 +6000,26 @@ dependencies = [ "pnet_sys", ] +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -5857,7 +6047,7 @@ dependencies = [ "nix", "once_cell", "smallvec", - "spin 0.10.0", + "spin 0.10.1", "symbolic-demangle", "tempfile", "thiserror 1.0.69", @@ -5891,7 +6081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -5920,7 +6110,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -5966,14 +6156,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -6000,9 +6190,9 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.13.0", + "bitflags 2.13.1", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -6030,7 +6220,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -6044,11 +6234,11 @@ dependencies = [ [[package]] name = "ptr_meta" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" +checksum = "743da816b98c921cdbe8628ef7381b76f25ecf4da599fc80aca90eae7ef70cc0" dependencies = [ - "ptr_meta_derive 0.3.1", + "ptr_meta_derive 0.3.2", ] [[package]] @@ -6064,13 +6254,13 @@ dependencies = [ [[package]] name = "ptr_meta_derive" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" +checksum = "1c8d9ca532f185d5d4db7a7c9d51420b452168ea1c2b913953281bd6fe1fcbd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -6079,7 +6269,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "memchr", "unicase", ] @@ -6149,9 +6339,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -6176,18 +6366,18 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rancor" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a063ea72381527c2a0561da9c80000ef822bdd7c3241b1cc1b12100e3df081ee" +checksum = "9b534442d0fcdb55d66f373d9cac6d33b6293a2335bc2136dbd06ce0e87d2572" dependencies = [ - "ptr_meta 0.3.1", + "ptr_meta 0.3.2", ] [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6196,9 +6386,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -6280,9 +6470,9 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.4.1" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +checksum = "5da7e78a036ce858e8d55b7e7dc8ba3a88b78350fd2155d3591bbd966b58589e" dependencies = [ "rustversion", ] @@ -6346,27 +6536,27 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -6379,9 +6569,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -6391,9 +6581,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -6564,11 +6754,11 @@ dependencies = [ [[package]] name = "rend" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadadef317c2f20755a64d7fdc48f9e7178ee6b0e1f7fce33fa60f1d68a276e6" +checksum = "663ba70707f96e871406fe10d68128412e619b06d1d47cb91c3a4c6501176240" dependencies = [ - "bytecheck 0.8.2", + "bytecheck 0.8.3", ] [[package]] @@ -6603,7 +6793,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-util 0.7.18", + "tokio-util 0.7.19", "tower-service", "url", "wasm-bindgen", @@ -6726,7 +6916,7 @@ dependencies = [ "revm-primitives", "revm-state", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -6823,7 +7013,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c32490ed687dba31c3c882beb8c20408bdd30ef96690d8f145b0ee9a87040bfe" dependencies = [ "alloy-eip7928", - "bitflags 2.13.0", + "bitflags 2.13.1", "revm-bytecode", "revm-primitives", "serde", @@ -6873,32 +7063,32 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.8.16" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73389e0c99e664f919275ab5b5b0471391fe9a8de61e1dff9b1eaf56a90f16e3" +checksum = "d9776093b7ca170454ab1406954f7b7d97a57c51dc6c0642957fb2ef25c2d399" dependencies = [ - "bytecheck 0.8.2", + "bytecheck 0.8.3", "bytes", "hashbrown 0.17.1", "indexmap 2.14.0", "munge", - "ptr_meta 0.3.1", + "ptr_meta 0.3.2", "rancor", "rend", "rkyv_derive", "tinyvec", - "uuid 1.23.3", + "uuid 1.25.0", ] [[package]] name = "rkyv_derive" -version = "0.8.16" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2ed0b54125315fb36bd021e82d314d1c126548f871634b483f46b31d13cac6" +checksum = "1c25ef604ac7dd839d44d64648952ea23c97866f124ff671b0ed2cf3ad9bb06e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -6935,14 +7125,15 @@ dependencies = [ [[package]] name = "ruint" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" +checksum = "f5e99bff0393163bb25029a6af25d3d8d202ba5b5438a74d1bd8789f5c822970" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", "ark-ff 0.4.2", "ark-ff 0.5.0", + "ark-ff 0.6.0", "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", @@ -6952,8 +7143,8 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand 0.8.6", - "rand 0.9.4", + "rand 0.8.7", + "rand 0.9.5", "rlp", "ruint-macro", "serde_core", @@ -6969,9 +7160,9 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" [[package]] name = "rustc-hash" @@ -6981,9 +7172,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc-hex" @@ -7015,7 +7206,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys", @@ -7033,18 +7224,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -7053,9 +7244,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rusty-fork" @@ -7137,9 +7328,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", "ref-cast", @@ -7156,7 +7347,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7192,7 +7383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ "bitcoin_hashes", - "rand 0.9.4", + "rand 0.9.5", "secp256k1-sys", ] @@ -7211,7 +7402,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -7230,9 +7421,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -7264,9 +7455,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -7314,22 +7505,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -7340,14 +7531,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "indexmap 2.14.0", "itoa", @@ -7359,13 +7550,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -7409,9 +7600,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" dependencies = [ "base64 0.22.1", "bs58", @@ -7419,8 +7610,9 @@ dependencies = [ "hex", "indexmap 1.9.3", "indexmap 2.14.0", + "jiff", "schemars 0.9.0", - "schemars 1.2.1", + "schemars 1.2.2", "serde_core", "serde_json", "serde_with_macros", @@ -7429,14 +7621,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7449,9 +7641,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.17", @@ -7537,7 +7729,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", - "mio 1.2.1", + "mio 1.2.2", "signal-hook", ] @@ -7569,9 +7761,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simdutf8" @@ -7612,9 +7804,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -7622,15 +7814,15 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" [[package]] name = "spin" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" dependencies = [ "lock_api", ] @@ -7792,7 +7984,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7808,9 +8000,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "332615d90111d8eeaf86a84dc9bbe9f65d0d8c5cf11b4caccedc37754eb0dcfd" dependencies = [ "debugid", - "memmap2 0.9.10", + "memmap2 0.9.11", "stable_deref_trait", - "uuid 1.23.3", + "uuid 1.25.0", ] [[package]] @@ -7837,9 +8029,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -7860,7 +8063,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -7927,9 +8130,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "target-triple" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" +checksum = "c3a6bfce3d99adfa72d24750a61f782f3036a81e7f86d8841ee1326deaebd171" [[package]] name = "tempfile" @@ -7938,7 +8141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -7997,11 +8200,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -8012,25 +8215,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if 1.0.4", ] @@ -8046,9 +8249,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.48" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1aa89044e7786ffb2ec017acb22cb7de5b0be46d0f21aea2b224b8561e5db2" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "num-conv", @@ -8066,9 +8269,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.28" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d3bfe86347f0cc659f586f01e26303ccd32418f26f30c7b0309b3ca3a07d695" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -8076,9 +8279,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -8096,9 +8299,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -8111,28 +8314,28 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", - "mio 1.2.1", + "mio 1.2.2", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.6.5", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] @@ -8173,14 +8376,14 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.19", ] [[package]] @@ -8211,13 +8414,14 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -8258,9 +8462,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap 2.14.0", "serde_core", @@ -8268,7 +8472,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -8329,23 +8533,23 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -8356,9 +8560,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tower" @@ -8369,7 +8573,7 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util 0.7.18", + "tokio-util 0.7.19", "tower-layer", "tower-service", "tracing", @@ -8407,7 +8611,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -8619,17 +8823,17 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.116" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" +checksum = "1e605bf6b39357663d8ba4e984f8be8da8df6bb32e81031d6889024ea8fd68e4" dependencies = [ - "glob 0.3.3", + "glob 0.3.4", "serde", "serde_derive", "serde_json", "target-triple", "termcolor", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", ] [[package]] @@ -8641,10 +8845,10 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.4.2", + "http 1.5.0", "httparse", "log", - "rand 0.8.6", + "rand 0.8.7", "sha1", "thiserror 1.0.69", "url", @@ -8813,9 +9017,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.23.3" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" dependencies = [ "js-sys", "wasm-bindgen", @@ -8829,9 +9033,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "value-bag" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" +checksum = "068e763e8279de7ab94b6afebded2cb701678af094feb1c12ccb061b4783c1be" [[package]] name = "vcpkg" @@ -8863,7 +9067,7 @@ dependencies = [ "pulldown-cmark", "regex", "semver 1.0.28", - "syn 2.0.117", + "syn 2.0.119", "toml 0.7.8", "url", ] @@ -8931,7 +9135,7 @@ dependencies = [ "casper-executor-wasm-common", "impls", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -9082,7 +9286,7 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-tungstenite", - "tokio-util 0.7.18", + "tokio-util 0.7.19", "tower-service", "tracing", ] @@ -9101,27 +9305,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if 1.0.4", "once_cell", @@ -9132,9 +9327,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.73" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54568702fabf5d4849ce2b90fadfa64168a097eaf4b351ce9df8b687a0086aaf" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -9142,9 +9337,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9152,22 +9347,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.123" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] @@ -9183,34 +9378,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.244.0" +version = "0.258.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +checksum = "e974fe6821a8cf64575d51ea2194e2c8f77e7b66e9afe7419ce8a97f9ee0d251" dependencies = [ "leb128fmt", - "wasmparser 0.244.0", -] - -[[package]] -name = "wasm-encoder" -version = "0.251.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a879a421bd17c528b74721b2abf4c62e8f1d1889c2ba8c3c50d02deaf2ce395" -dependencies = [ - "leb128fmt", - "wasmparser 0.251.0", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder 0.244.0", - "wasmparser 0.244.0", + "wasmparser 0.258.0", ] [[package]] @@ -9391,7 +9564,7 @@ version = "0.219.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5220ee4c6ffcc0cb9d7c47398052203bc902c8ef3985b0c8134118440c0b2921" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "indexmap 2.14.0", ] @@ -9401,28 +9574,16 @@ version = "0.224.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04f17a5917c2ddd3819e84c661fae0d6ba29d7b9c1f0e96c708c65a9c4188e11" dependencies = [ - "bitflags 2.13.0", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.0", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver 1.0.28", + "bitflags 2.13.1", ] [[package]] name = "wasmparser" -version = "0.251.0" +version = "0.258.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437970b35b1a85cfde9c74b2398352d8d653f3bd8e3a3db0c063ea8f5b4b36ff" +checksum = "d9a61719f93a87b16d325921e251800c4833f8fab50fa21c7de73aed50086313" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "indexmap 2.14.0", "semver 1.0.28", ] @@ -9440,31 +9601,31 @@ dependencies = [ [[package]] name = "wast" -version = "251.0.0" +version = "258.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7467dda0a96142eb2c980329dfb62480b1e1d3622fdeb1a44e2bca6ceed74" +checksum = "97f7defc7ecca8b19ac7f824598eadd0c53985ee00c74060d65051e9da5b58a1" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.251.0", + "wasm-encoder 0.258.0", ] [[package]] name = "wat" -version = "1.251.0" +version = "1.258.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b1086c9e85b95bd6a229a928bc6c6d0662e42af0250c88d067b418831ea4d4" +checksum = "7555c008cca87f2ac58d9f83ccda7e7b44611093ce28eb28f052e7c78024b9bf" dependencies = [ "wast", ] [[package]] name = "web-sys" -version = "0.3.100" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -9540,7 +9701,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9551,7 +9712,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9755,9 +9916,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -9772,100 +9933,12 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap 2.14.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.0", - "indexmap 2.14.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder 0.244.0", - "wasm-metadata", - "wasmparser 0.244.0", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.14.0", - "log", - "semver 1.0.28", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser 0.244.0", -] - [[package]] name = "withdraw-bid" version = "0.1.0" @@ -9876,9 +9949,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "wyz" @@ -9901,9 +9974,9 @@ dependencies = [ [[package]] name = "xxhash-rust" -version = "0.8.15" +version = "0.8.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6" [[package]] name = "yoke" @@ -9924,28 +9997,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] @@ -9965,35 +10038,35 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.119", ] [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -10002,9 +10075,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -10013,17 +10086,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 3.0.4", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/execution_engine/src/engine_state/wasm_v1.rs b/execution_engine/src/engine_state/wasm_v1.rs index 19cb5c91fe..ef1ae62e4e 100644 --- a/execution_engine/src/engine_state/wasm_v1.rs +++ b/execution_engine/src/engine_state/wasm_v1.rs @@ -19,20 +19,21 @@ use crate::engine_state::Error as EngineError; const DEFAULT_ENTRY_POINT: &str = "call"; /// Structure that needs to be filled with data so the engine can assemble wasm for deploy. -pub struct SessionDataDeploy<'a> { - deploy_hash: &'a DeployHash, - session: &'a ExecutableDeployItem, - initiator_addr: &'a InitiatorAddr, +#[derive(Clone, Debug)] +pub struct SessionDataDeploy { + deploy_hash: DeployHash, + session: ExecutableDeployItem, + initiator_addr: InitiatorAddr, signers: BTreeSet, is_standard_payment: bool, } -impl<'a> SessionDataDeploy<'a> { +impl SessionDataDeploy { /// Constructor pub fn new( - deploy_hash: &'a DeployHash, - session: &'a ExecutableDeployItem, - initiator_addr: &'a InitiatorAddr, + deploy_hash: DeployHash, + session: ExecutableDeployItem, + initiator_addr: InitiatorAddr, signers: BTreeSet, is_standard_payment: bool, ) -> Self { @@ -46,18 +47,18 @@ impl<'a> SessionDataDeploy<'a> { } /// Deploy hash of the deploy - pub fn deploy_hash(&self) -> &DeployHash { + pub fn deploy_hash(&self) -> DeployHash { self.deploy_hash } /// executable item of the deploy - pub fn session(&self) -> &ExecutableDeployItem { - self.session + pub fn session(&self) -> ExecutableDeployItem { + self.session.clone() } /// initiator address of the deploy - pub fn initiator_addr(&self) -> &InitiatorAddr { - self.initiator_addr + pub fn initiator_addr(&self) -> InitiatorAddr { + self.initiator_addr.clone() } /// signers of the deploy @@ -67,29 +68,30 @@ impl<'a> SessionDataDeploy<'a> { } /// Structure that needs to be filled with data so the engine can assemble wasm for v1. -pub struct SessionDataV1<'a> { - args: &'a RuntimeArgs, - target: &'a TransactionTarget, - entry_point: &'a TransactionEntryPoint, +#[derive(Clone, Debug)] +pub struct SessionDataV1 { + args: RuntimeArgs, + target: TransactionTarget, + entry_point: TransactionEntryPoint, is_install_upgrade: bool, - hash: &'a TransactionV1Hash, - pricing_mode: &'a PricingMode, - initiator_addr: &'a InitiatorAddr, + hash: TransactionV1Hash, + pricing_mode: PricingMode, + initiator_addr: InitiatorAddr, signers: BTreeSet, is_standard_payment: bool, } -impl<'a> SessionDataV1<'a> { +impl SessionDataV1 { #[allow(clippy::too_many_arguments)] /// Constructor pub fn new( - args: &'a RuntimeArgs, - target: &'a TransactionTarget, - entry_point: &'a TransactionEntryPoint, + args: RuntimeArgs, + target: TransactionTarget, + entry_point: TransactionEntryPoint, is_install_upgrade: bool, - hash: &'a TransactionV1Hash, - pricing_mode: &'a PricingMode, - initiator_addr: &'a InitiatorAddr, + hash: TransactionV1Hash, + pricing_mode: PricingMode, + initiator_addr: InitiatorAddr, signers: BTreeSet, is_standard_payment: bool, ) -> Self { @@ -107,18 +109,18 @@ impl<'a> SessionDataV1<'a> { } /// Runtime args passed with the transaction. - pub fn args(&self) -> &RuntimeArgs { - self.args + pub fn args(&self) -> RuntimeArgs { + self.args.clone() } /// Target of the transaction. - pub fn target(&self) -> &TransactionTarget { - self.target + pub fn target(&self) -> TransactionTarget { + self.target.clone() } /// Entry point of the transaction - pub fn entry_point(&self) -> &TransactionEntryPoint { - self.entry_point + pub fn entry_point(&self) -> TransactionEntryPoint { + self.entry_point.clone() } /// Should session be allowed to perform install/upgrade operations @@ -127,13 +129,13 @@ impl<'a> SessionDataV1<'a> { } /// Hash of the transaction - pub fn hash(&self) -> &TransactionV1Hash { + pub fn hash(&self) -> TransactionV1Hash { self.hash } /// initiator address of the transaction - pub fn initiator_addr(&self) -> &InitiatorAddr { - self.initiator_addr + pub fn initiator_addr(&self) -> InitiatorAddr { + self.initiator_addr.clone() } /// signers of the transaction @@ -142,38 +144,39 @@ impl<'a> SessionDataV1<'a> { } /// Pricing mode of the transaction - pub fn pricing_mode(&self) -> &PricingMode { - self.pricing_mode + pub fn pricing_mode(&self) -> PricingMode { + self.pricing_mode.clone() } } /// Wrapper enum abstracting data for assmbling WasmV1Requests -pub enum SessionInputData<'a> { +#[derive(Clone, Debug)] +pub enum SessionInputData { /// Variant for sessions created from deploy transactions DeploySessionData { /// Deploy session data - data: SessionDataDeploy<'a>, + data: SessionDataDeploy, }, /// Variant for sessions created from v1 transactions SessionDataV1 { /// v1 session data - data: SessionDataV1<'a>, + data: SessionDataV1, }, } -impl SessionInputData<'_> { +impl SessionInputData { /// Transaction hash for the session pub fn transaction_hash(&self) -> TransactionHash { match self { SessionInputData::DeploySessionData { data } => { - TransactionHash::Deploy(*data.deploy_hash()) + TransactionHash::Deploy(data.deploy_hash()) } - SessionInputData::SessionDataV1 { data } => TransactionHash::V1(*data.hash()), + SessionInputData::SessionDataV1 { data } => TransactionHash::V1(data.hash()), } } /// Initiator address for the session - pub fn initiator_addr(&self) -> &InitiatorAddr { + pub fn initiator_addr(&self) -> InitiatorAddr { match self { SessionInputData::DeploySessionData { data } => data.initiator_addr(), SessionInputData::SessionDataV1 { data } => data.initiator_addr(), @@ -617,7 +620,7 @@ impl WasmV1Result { pub fn from_transfer_result(transfer_result: TransferResult, consumed: Gas) -> Option { // NOTE: for native / wasmless operations limit and consumed are always equal, and // we can get away with simplifying to one or the other here. - // this is NOT true of wasm based operations however. + // this is NOT true of wasm based operations, however. match transfer_result { TransferResult::RootNotFound => None, TransferResult::Success { @@ -701,7 +704,7 @@ impl Executable for SessionInfo { } } -impl TryFrom<&SessionInputData<'_>> for PaymentInfo { +impl TryFrom<&SessionInputData> for PaymentInfo { type Error = InvalidRequest; fn try_from(input_data: &SessionInputData) -> Result { @@ -712,7 +715,7 @@ impl TryFrom<&SessionInputData<'_>> for PaymentInfo { } } -impl TryFrom<&SessionInputData<'_>> for SessionInfo { +impl TryFrom<&SessionInputData> for SessionInfo { type Error = InvalidRequest; fn try_from(input_data: &SessionInputData) -> Result { @@ -723,13 +726,12 @@ impl TryFrom<&SessionInputData<'_>> for SessionInfo { } } -impl TryFrom<&SessionDataDeploy<'_>> for SessionInfo { +impl TryFrom<&SessionDataDeploy> for SessionInfo { type Error = InvalidRequest; fn try_from(deploy_data: &SessionDataDeploy) -> Result { - let transaction_hash = TransactionHash::Deploy(*deploy_data.deploy_hash()); - let session_item = deploy_data.session(); - build_session_info_for_executable_item(session_item, transaction_hash) + let transaction_hash = TransactionHash::Deploy(deploy_data.deploy_hash()); + build_session_info_for_executable_item(&deploy_data.session(), transaction_hash) } } @@ -811,11 +813,11 @@ fn build_session_info_for_executable_item( })) } -impl TryFrom<&SessionDataV1<'_>> for SessionInfo { +impl TryFrom<&SessionDataV1> for SessionInfo { type Error = InvalidRequest; fn try_from(v1_txn: &SessionDataV1) -> Result { - let transaction_hash = TransactionHash::V1(*v1_txn.hash()); + let transaction_hash = TransactionHash::V1(v1_txn.hash()); let args = v1_txn.args().clone(); let session = match v1_txn.target() { TransactionTarget::Native => { @@ -839,7 +841,7 @@ impl TryFrom<&SessionDataV1<'_>> for SessionInfo { } } TransactionTarget::Session { module_bytes, .. } => { - if *v1_txn.entry_point() != TransactionEntryPoint::Call { + if v1_txn.entry_point() != TransactionEntryPoint::Call { return Err(InvalidRequest::InvalidEntryPoint( transaction_hash, v1_txn.entry_point().to_string(), @@ -886,13 +888,12 @@ impl Executable for PaymentInfo { } } -impl TryFrom<&SessionDataDeploy<'_>> for PaymentInfo { +impl TryFrom<&SessionDataDeploy> for PaymentInfo { type Error = InvalidRequest; fn try_from(deploy_data: &SessionDataDeploy) -> Result { - let payment_item = deploy_data.session(); - let transaction_hash = TransactionHash::Deploy(*deploy_data.deploy_hash()); - build_payment_info_for_executable_item(payment_item, transaction_hash) + let transaction_hash = TransactionHash::Deploy(deploy_data.deploy_hash()); + build_payment_info_for_executable_item(&deploy_data.session(), transaction_hash) } } @@ -969,33 +970,35 @@ fn build_payment_info_for_executable_item( } } -impl TryFrom<&SessionDataV1<'_>> for PaymentInfo { +impl TryFrom<&SessionDataV1> for PaymentInfo { type Error = InvalidRequest; fn try_from(v1_txn: &SessionDataV1) -> Result { - let transaction_hash = TransactionHash::V1(*v1_txn.hash()); - match v1_txn.pricing_mode() { - mode @ PricingMode::PaymentLimited { + let transaction_hash = TransactionHash::V1(v1_txn.hash()); + + let pricing_mode = v1_txn.pricing_mode(); + match pricing_mode { + PricingMode::PaymentLimited { standard_payment, .. } => { - if *standard_payment { + if standard_payment { return Err(InvalidRequest::UnsupportedMode( transaction_hash, - mode.to_string(), + pricing_mode.to_string(), )); } } - mode @ PricingMode::Fixed { .. } | mode @ PricingMode::Prepaid { .. } => { + PricingMode::Fixed { .. } | PricingMode::Prepaid { .. } => { return Err(InvalidRequest::UnsupportedMode( transaction_hash, - mode.to_string(), + pricing_mode.to_string(), )); } - }; + } let payment = match v1_txn.target() { TransactionTarget::Session { module_bytes, .. } => { - if *v1_txn.entry_point() != TransactionEntryPoint::Call { + if v1_txn.entry_point() != TransactionEntryPoint::Call { return Err(InvalidRequest::InvalidEntryPoint( transaction_hash, v1_txn.entry_point().to_string(), diff --git a/execution_engine_testing/test_support/src/execute_request_builder.rs b/execution_engine_testing/test_support/src/execute_request_builder.rs index c8e371c95d..e0841c011c 100644 --- a/execution_engine_testing/test_support/src/execute_request_builder.rs +++ b/execution_engine_testing/test_support/src/execute_request_builder.rs @@ -323,17 +323,18 @@ impl ExecuteRequestBuilder { }; let mut signers = BTreeSet::new(); signers.insert(sender); + let is_standard_payment = pricing_mode.is_standard_payment(); let session_input_data = SessionInputData::SessionDataV1 { data: SessionDataV1::new( - &args, - &target, - &entry_point, + args, + target, + entry_point, false, - &hash, - &pricing_mode, - &initiator_addr, + hash, + pricing_mode, + initiator_addr, signers, - pricing_mode.is_standard_payment(), + is_standard_payment, ), }; Self::from_session_input_data(&session_input_data) @@ -390,17 +391,18 @@ impl ExecuteRequestBuilder { }; let mut signers = BTreeSet::new(); signers.insert(sender); + let is_standard_payment = pricing_mode.is_standard_payment(); let session_input_data = SessionInputData::SessionDataV1 { data: SessionDataV1::new( - &args, - &target, - &entry_point, + args, + target, + entry_point, false, - &hash, - &pricing_mode, - &initiator_addr, + hash, + pricing_mode, + initiator_addr, signers, - pricing_mode.is_standard_payment(), + is_standard_payment, ), }; Self::from_session_input_data(&session_input_data) diff --git a/execution_engine_testing/tests/src/test/calling_packages_by_version_query.rs b/execution_engine_testing/tests/src/test/calling_packages_by_version_query.rs index ec064ed8fe..fe73e2c811 100644 --- a/execution_engine_testing/tests/src/test/calling_packages_by_version_query.rs +++ b/execution_engine_testing/tests/src/test/calling_packages_by_version_query.rs @@ -518,13 +518,13 @@ fn builder_for_calling_entrypoint( }; let initiator_addr = casper_types::InitiatorAddr::AccountHash(*DEFAULT_ACCOUNT_ADDR); let session_data_v1 = SessionDataV1::new( - &args, - &target, - &entry_point, + args, + target, + entry_point, true, - &v1_hash, - &pricing_mode, - &initiator_addr, + v1_hash, + pricing_mode, + initiator_addr, signers, true, ); diff --git a/execution_engine_testing/tests/src/test/contract_api/add_contract_version.rs b/execution_engine_testing/tests/src/test/contract_api/add_contract_version.rs index 063e4cb0ce..c9ae57c066 100644 --- a/execution_engine_testing/tests/src/test/contract_api/add_contract_version.rs +++ b/execution_engine_testing/tests/src/test/contract_api/add_contract_version.rs @@ -67,7 +67,7 @@ fn try_add_contract_version( let tx_args = txn .deserialize_field::(ARGS_MAP_KEY) .unwrap(); - let args = tx_args.as_named().unwrap(); + let args = tx_args.as_named().unwrap().clone(); let target = txn .deserialize_field::(TARGET_MAP_KEY) .unwrap(); @@ -76,11 +76,11 @@ fn try_add_contract_version( .unwrap(); let session_input_data = to_v1_session_input_data( is_standard_payment, - initiator_addr, + initiator_addr.clone(), args, - &target, - &entry_point, - &wrapped, + target, + entry_point, + wrapped, ); assert_eq!( session_input_data.is_install_upgrade_allowed(), @@ -173,33 +173,34 @@ fn build_transaction( transaction } -fn to_v1_session_input_data<'a>( +fn to_v1_session_input_data( is_standard_payment: bool, - initiator_addr: &'a InitiatorAddr, - args: &'a RuntimeArgs, - target: &'a TransactionTarget, - entry_point: &'a TransactionEntryPoint, - txn: &'a Transaction, -) -> SessionInputData<'a> { + initiator_addr: InitiatorAddr, + args: RuntimeArgs, + target: TransactionTarget, + entry_point: TransactionEntryPoint, + txn: Transaction, +) -> SessionInputData { let is_install_upgrade = match target { TransactionTarget::Session { is_install_upgrade, .. - } => *is_install_upgrade, + } => is_install_upgrade, _ => false, }; - match txn { + match &txn { Transaction::Deploy(_) => panic!("unexpected deploy transaction"), Transaction::Evm(_) => panic!("unexpected EVM transaction"), Transaction::V1(transaction_v1) => { + let signers = txn.signers(); let data = SessionDataV1::new( args, target, entry_point, is_install_upgrade, - transaction_v1.hash(), - transaction_v1.pricing_mode(), + *transaction_v1.hash(), + transaction_v1.pricing_mode().clone(), initiator_addr, - txn.signers().clone(), + signers, is_standard_payment, ); SessionInputData::SessionDataV1 { data } diff --git a/executor/evm/Cargo.toml b/executor/evm/Cargo.toml index 4b3fd5dcde..25884f9fc2 100644 --- a/executor/evm/Cargo.toml +++ b/executor/evm/Cargo.toml @@ -16,5 +16,5 @@ revm = { version = "38", features = ["dev", "optional_fee_charge"] } thiserror = "2" [dev-dependencies] -alloy-consensus = { version = "=1.0.41", default-features = false, features = ["k256"] } -alloy-primitives = { version = "=1.5.7", default-features = false, features = ["rlp", "sha3-keccak"] } +alloy-consensus = { version = "1.0.41", default-features = false, features = ["k256"] } +alloy-primitives = { version = "1.5.7", default-features = false, features = ["rlp", "sha3-keccak"] } diff --git a/executor/evm/src/executor.rs b/executor/evm/src/executor.rs index d067c4c6c5..4192aca08d 100644 --- a/executor/evm/src/executor.rs +++ b/executor/evm/src/executor.rs @@ -107,7 +107,7 @@ impl EvmExecutor { } let tx_env = tx::build_tx_env(&self.config, &request.kind)?; - let block = request.block.to_revm_block(&self.config)?; + let block = request.block.to_revm_block()?; let execution_mode = match &request.kind { ExecuteKind::Transaction(_) => EvmExecutionMode::Checked, ExecuteKind::Call(call) if call.validation.is_unchecked_simulation() => { @@ -134,8 +134,7 @@ impl EvmExecutor { let mut state = result_and_state.state; // revm skips the upfront fee debit but still applies the // post-execution gas reimbursement and beneficiary reward. - let disabled_fee_transfers = - disabled_fee_transfers(&self.config, &request, &result_and_state.result); + let disabled_fee_transfers = disabled_fee_transfers(&request, &result_and_state.result); state::remove_disabled_fee_transfers(&mut state, disabled_fee_transfers)?; state::apply(tracking_copy, state)?; Ok(outcome) @@ -156,7 +155,7 @@ impl EvmExecutor { return Err(Error::Disabled); } - let block = request.block.to_revm_block(&self.config)?; + let block = request.block.to_revm_block()?; let result_and_state = { let db = CasperDb::new(tracking_copy, block_hash_provider); let mut evm = Context::mainnet() @@ -198,15 +197,11 @@ fn configure_evm_cfg(cfg: &mut CfgEnv, config: &EvmConfig, execution_mode: EvmEx } fn disabled_fee_transfers( - config: &EvmConfig, request: &ExecuteRequest, result: &RevmExecutionResult, ) -> state::DisabledFeeTransfers { let gas = result_gas(result); - let base_fee = request - .block - .base_fee - .unwrap_or_else(|| config.base_fee_wei()); + let base_fee = request.block.base_fee_wei; let (caller, gas_limit, effective_gas_price) = match &request.kind { ExecuteKind::Transaction(transaction) => ( tx::to_revm_address(transaction.from()), diff --git a/executor/evm/src/outcome.rs b/executor/evm/src/outcome.rs index b76f946759..b85e656071 100644 --- a/executor/evm/src/outcome.rs +++ b/executor/evm/src/outcome.rs @@ -1,12 +1,11 @@ //! Public execution outcome types. -use casper_types::evm; +use crate::tx; +use casper_types::{evm, evm::HaltReason}; use revm::context_interface::result::{ ExecutionResult, HaltReason as RevmHaltReason, OutOfGasError as RevmOutOfGasError, Output, }; -use crate::tx; - /// Result returned by [`crate::EvmExecutor::execute`]. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExecutionOutcome { @@ -23,6 +22,17 @@ pub struct ExecutionOutcome { } impl ExecutionOutcome { + /// Creates a Halt (no exec) outcome. + pub fn no_exec() -> Self { + ExecutionOutcome { + status: ExecutionStatus::Halt(HaltReason::NoExec), + gas_used: 0, + output: Vec::new(), + logs: Vec::new(), + created_contract_address: None, + } + } + pub(crate) fn from_revm_result(result: &ExecutionResult) -> Self { match result { ExecutionResult::Success { diff --git a/executor/evm/src/request.rs b/executor/evm/src/request.rs index f46b51d1b6..326a5d7cc2 100644 --- a/executor/evm/src/request.rs +++ b/executor/evm/src/request.rs @@ -1,8 +1,7 @@ //! Public execution request types. -use casper_types::{evm, EvmConfig, EvmTransaction, U256}; - use crate::{tx, Error}; +use casper_types::{evm, BlockTime, EvmTransaction, U256}; /// Request passed to [`crate::EvmExecutor::execute`]. #[derive(Clone, Debug, Eq, PartialEq)] @@ -46,7 +45,7 @@ pub struct CallRequest { pub to: Option, /// Amount of wei to send, encoded as a big-endian 256-bit word. pub value: U256, - /// Calldata or contract init code. + /// Call data or contract init code. pub input: Vec, /// Gas available for execution. pub gas_limit: u64, @@ -83,28 +82,41 @@ pub struct BlockContext { /// Block beneficiary address. pub beneficiary: evm::Address, /// Optional gas limit override. Defaults to chainspec `[evm].block_gas_limit`. - pub gas_limit: Option, + pub block_gas_limit: u64, /// Optional base-fee override in wei. /// /// Defaults to chainspec `[evm].base_fee * [evm].wei_per_mote`. - pub base_fee: Option, + pub base_fee_wei: u128, } impl BlockContext { - pub(crate) fn to_revm_block( - &self, - config: &EvmConfig, - ) -> Result { - let base_fee = self.base_fee.unwrap_or_else(|| config.base_fee_wei()); - let basefee = u64::try_from(base_fee).map_err(|_| { + pub fn new( + block_height: u64, // aka number + block_time: BlockTime, // aka timestamp + block_gas_limit: u64, + base_fee_wei: u128, + beneficiary: evm::Address, + ) -> Self { + BlockContext { + number: block_height, + timestamp: block_time.value() / 1000, + beneficiary, + block_gas_limit, + base_fee_wei, + } + } + + pub(crate) fn to_revm_block(&self) -> Result { + let base_fee_wei = self.base_fee_wei; + let base_fee = u64::try_from(base_fee_wei).map_err(|_| { Error::Transaction("configured EVM base fee overflows revm u64".to_string()) })?; Ok(revm::context::BlockEnv { number: revm::primitives::U256::from(self.number), beneficiary: tx::to_revm_address(self.beneficiary), timestamp: revm::primitives::U256::from(self.timestamp), - gas_limit: self.gas_limit.unwrap_or(config.block_gas_limit), - basefee, + gas_limit: self.block_gas_limit, + basefee: base_fee, ..Default::default() }) } @@ -118,23 +130,24 @@ mod tests { #[test] fn should_use_wei_denominated_base_fee_for_revm_block() { - let config = EvmConfig { + let config = casper_types::EvmConfig { base_fee: 3, wei_per_mote: DEFAULT_WEI_PER_MOTE, ..Default::default() }; + let base_fee_wei = u128::from(config.base_fee) * u128::from(config.wei_per_mote); let context = BlockContext { number: 1, timestamp: 1, beneficiary: evm::Address::ZERO, - gas_limit: None, - base_fee: None, + block_gas_limit: config.block_gas_limit, + base_fee_wei, }; let block = context - .to_revm_block(&config) + .to_revm_block() .expect("base fee should fit in revm block context"); - assert_eq!(u128::from(block.basefee), config.base_fee_wei()); + assert_eq!(u128::from(block.basefee), base_fee_wei); assert_ne!(block.basefee, config.base_fee); } } diff --git a/executor/evm/tests/executor.rs b/executor/evm/tests/executor.rs index bb1a42fed3..7a79de34d1 100644 --- a/executor/evm/tests/executor.rs +++ b/executor/evm/tests/executor.rs @@ -104,8 +104,8 @@ fn block() -> BlockContext { number: 1, timestamp: 1_714_000_000, beneficiary: evm::Address::ZERO, - gas_limit: None, - base_fee: None, + block_gas_limit: None, + base_fee_wei: None, } } @@ -1793,7 +1793,7 @@ fn checked_calls_enforce_transaction_validation() { let (mut tracking_copy, _tempdir) = tracking_copy(); let mut request = checked_call_request(from, Some(recipient), Vec::new(), CasperU256::zero()); - request.block.base_fee = Some(1); + request.block.base_fee_wei = Some(1); assert!(matches!( executor.execute(&mut tracking_copy, request), Err(Error::Revm(_)) diff --git a/node/Cargo.toml b/node/Cargo.toml index 9733087807..6a97b360e3 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -39,7 +39,7 @@ hex_fmt = "0.3.0" hostname = "0.3.0" http = "0.2.1" humantime = "2.1.0" -hyper = "0.14.27" +hyper = "0.14.32" itertools = "0.10.3" libc = "0.2.66" linked-hash-map = "0.5.3" @@ -112,8 +112,8 @@ proptest-derive = "0.5.1" rand_core = "0.6.2" reqwest = { version = "0.11.27", features = ["stream"] } tokio = { version = "1", features = ["test-util"] } -alloy-consensus = { version = "=1.0.41", default-features = false, features = ["k256"] } -alloy-eips = { version = "=1.4.2", default-features = false, features = ["k256"] } +alloy-consensus = { version = "1.0.41", default-features = false, features = ["k256"] } +alloy-eips = { version = "1.4.2", default-features = false, features = ["k256"] } alloy-primitives = { version = "=1.5.7", default-features = false, features = ["rlp", "sha3-keccak", "k256"] } k256 = { version = "0.13.4", default-features = false, features = ["ecdsa"] } revm = { version = "38", features = ["dev"] } diff --git a/node/src/components/contract_runtime.rs b/node/src/components/contract_runtime.rs index ecc1c4bd3c..f1524cc95e 100644 --- a/node/src/components/contract_runtime.rs +++ b/node/src/components/contract_runtime.rs @@ -11,6 +11,7 @@ mod rewards; mod tests; mod types; mod utils; +mod utils_evm; use std::{ cmp::Ordering, @@ -75,7 +76,6 @@ use exec_queue::{ExecQueue, QueueItem}; use metrics::Metrics; #[cfg(test)] pub(crate) use operations::compute_execution_results_checksum; -pub use operations::execute_finalized_block; use operations::speculatively_execute; pub(crate) use types::{ BlockAndExecutionArtifacts, ExecutionArtifact, ExecutionPreState, SpeculativeExecutionResult, diff --git a/node/src/components/contract_runtime/error.rs b/node/src/components/contract_runtime/error.rs index 09af7a8736..9fbe4467fa 100644 --- a/node/src/components/contract_runtime/error.rs +++ b/node/src/components/contract_runtime/error.rs @@ -21,7 +21,7 @@ use crate::{ }; /// Common state result errors. -#[derive(Debug, Error)] +#[derive(Debug, Error, Clone)] pub(crate) enum StateResultError { /// Invalid state root hash. #[error("invalid state root hash")] @@ -62,10 +62,6 @@ pub(crate) enum ContractRuntimeError { /// An error during block execution. #[derive(Debug, Error, Serialize)] pub enum BlockExecutionError { - /// Currently the contract runtime can only execute one commit at a time, so we cannot handle - /// more than one execution result. - #[error("more than one execution result")] - MoreThanOneExecutionResult, /// Both the block to be executed and the execution pre-state specify the height of the next /// block. These must agree and this error will be thrown if they do not. #[error( @@ -162,17 +158,8 @@ pub enum BlockExecutionError { // Error attempting to set block global data. #[error("Error while attempting to store block global data: {0}")] BlockGlobal(String), - #[error("No switch block header available for era: {0}")] - /// No switch block available - NoSwitchBlockHash(u64), - #[error("Unsupported execution kind: {0}")] - /// Unsupported execution kind - UnsupportedTransactionKind(u8), #[error("Error while converting transaction to internal representation: {0}")] TransactionConversion(String), - /// Invalid gas limit amount. - #[error("Invalid gas limit amount: {0}")] - InvalidGasLimit(U512), /// Invalid transaction variant. #[error("Invalid transaction variant")] InvalidTransactionVariant, diff --git a/node/src/components/contract_runtime/metrics.rs b/node/src/components/contract_runtime/metrics.rs index 0216626c43..9418550cbb 100644 --- a/node/src/components/contract_runtime/metrics.rs +++ b/node/src/components/contract_runtime/metrics.rs @@ -17,6 +17,12 @@ const EXPONENTIAL_BUCKET_COUNT: usize = 10; const EXEC_WASM_V1_NAME: &str = "contract_runtime_exec_wasm_v1"; const EXEC_WASM_V1_HELP: &str = "time in seconds to execute wasm using the v1 exec engine"; +const EXEC_WASM_V2_NAME: &str = "contract_runtime_exec_wasm_v2"; +const EXEC_WASM_V2_HELP: &str = "time in seconds to execute wasm using the v2 exec engine"; + +const EXEC_EVM_V1_NAME: &str = "contract_runtime_exec_evm_v1"; +const EXEC_EVM_V1_HELP: &str = "time in seconds to execute wasm using the evm v1 exec engine"; + const EXEC_BLOCK_PRE_PROCESSING_NAME: &str = "contract_runtime_exec_block_pre_proc"; const EXEC_BLOCK_PRE_PROCESSING_HELP: &str = "processing time in seconds before any transactions have processed"; @@ -121,6 +127,10 @@ pub struct Metrics { pub(super) exec_block_tnx_processing: Histogram, // tnx processing elapsed pub(super) exec_wasm_v1: Histogram, + // tnx processing elapsed + pub(super) exec_wasm_v2: Histogram, + // tnx processing elapsed + pub(super) exec_evm_v1: Histogram, // ee_v1 execution elapsed pub(super) exec_block_step_processing: Histogram, // step processing elapsed @@ -208,6 +218,18 @@ impl Metrics { EXEC_WASM_V1_HELP, common_buckets.clone(), )?, + exec_wasm_v2: utils::register_histogram_metric( + registry, + EXEC_WASM_V2_NAME, + EXEC_WASM_V2_HELP, + common_buckets.clone(), + )?, + exec_evm_v1: utils::register_histogram_metric( + registry, + EXEC_EVM_V1_NAME, + EXEC_EVM_V1_HELP, + common_buckets.clone(), + )?, exec_block_post_processing: utils::register_histogram_metric( registry, EXEC_BLOCK_POST_PROCESSING_NAME, @@ -365,6 +387,8 @@ impl Drop for Metrics { unregister_metric!(self.registry, self.exec_block_pre_processing); unregister_metric!(self.registry, self.exec_block_tnx_processing); unregister_metric!(self.registry, self.exec_wasm_v1); + unregister_metric!(self.registry, self.exec_wasm_v2); + unregister_metric!(self.registry, self.exec_evm_v1); unregister_metric!(self.registry, self.exec_block_post_processing); unregister_metric!(self.registry, self.exec_block_step_processing); unregister_metric!(self.registry, self.exec_block_total); diff --git a/node/src/components/contract_runtime/operations.rs b/node/src/components/contract_runtime/operations.rs index 9a607ea052..299d56aa36 100644 --- a/node/src/components/contract_runtime/operations.rs +++ b/node/src/components/contract_runtime/operations.rs @@ -1,33 +1,21 @@ -pub(crate) mod wasm_v2_request; - +use crate::types::transaction::WasmV2Result; use casper_executor_wasm::ExecutorV2; -use itertools::Itertools; -use std::{collections::BTreeMap, convert::TryInto, sync::Arc, time::Instant}; +use std::{collections::BTreeMap, convert::TryInto, sync::Arc}; use tracing::{debug, error, info, trace, warn}; -use wasm_v2_request::{WasmV2Request, WasmV2Result}; use casper_execution_engine::engine_state::{ BlockInfo, ExecutionEngineV1, WasmV1Request, WasmV1Result, }; use casper_executor_evm::{ - BlockContext as EvmBlockContext, BlockHashProvider as EvmBlockHashProvider, - BlockHashProviderResult as EvmBlockHashProviderResult, CallRequest as EvmExecutorCallRequest, - CallValidation as EvmCallValidation, EvmExecutor, ExecuteKind as EvmExecuteKind, - ExecuteRequest as EvmExecuteRequest, ExecutionStatus as EvmExecutionStatus, + BlockContext as EvmV1Request, BlockHashProvider as EvmBlockHashProvider, + CallRequest as EvmExecutorCallRequest, CallValidation as EvmCallValidation, EvmExecutor, + ExecuteKind as EvmExecuteKind, ExecuteRequest as EvmExecuteRequest, }; use casper_storage::{ - block_store::types::ApprovalsHashes, data_access_layer::{ - balance::BalanceHandling, - mint::{BalanceIdentifierTransferArgs, BurnRequest}, - AuctionMethod, BalanceHoldKind, BalanceHoldRequest, BalanceIdentifier, - BalanceIdentifierPurseRequest, BalanceIdentifierPurseResult, BalanceRequest, - BiddingRequest, BlockGlobalRequest, BlockGlobalResult, BlockRewardsRequest, + AuctionMethod, BalanceHoldKind, BalanceIdentifier, BalanceResult, BlockGlobalResult, BlockRewardsResult, DataAccessLayer, EntryPointRequest, EntryPointResult, - EraValidatorsRequest, EraValidatorsResult, EvictItem, FeeRequest, FeeResult, FlushRequest, - HandleFeeMode, HandleFeeRequest, HandleRefundMode, HandleRefundRequest, - InsufficientBalanceHandling, ProofHandling, PruneRequest, PruneResult, StepRequest, - StepResult, TransferRequest, + EraValidatorsResult, FeeResult, FlushRequest, PruneResult, StepResult, TransferRequest, }, global_state::state::{ lmdb::LmdbGlobalState, scratch::ScratchGlobalState, CommitProvider, ScratchProvider, @@ -38,19 +26,13 @@ use casper_storage::{ TrackingCopy, }; use casper_types::{ - account::{Account, AccountHash}, + account::AccountHash, bytesrepr::{self, Bytes, ToBytes, U32_SERIALIZED_LENGTH}, - contracts::NamedKeys, - evm::{ - Address as EvmAddress, HaltReason as EvmHaltReason, Receipt as EvmReceipt, - ReceiptStatus as EvmReceiptStatus, - }, + evm::{Address as EvmAddress, IdentityInstruction as EvmIdentityInstruction}, execution::{Effects, ExecutionResult, TransformKindV2, TransformV2}, - system::handle_payment::ARG_AMOUNT, - BlockHash, BlockHeader, BlockTime, BlockV2, CLValue, Chainspec, ChecksumRegistry, Digest, - EntityAddr, EraEndV2, EraId, EvmSpec, FeeHandling, Gas, InvalidTransaction, - InvalidTransactionV1, Key, ProtocolVersion, PublicKey, RefundHandling, StoredValue, TimeDiff, - Transaction, TransactionEntryPoint, AUCTION_LANE_ID, MINT_LANE_ID, U512, + BlockHash, BlockHeader, CLValue, Chainspec, ChecksumRegistry, Digest, EntityAddr, + EvmTransactionError, Gas, HashAddr, InvalidTransaction, InvalidTransactionV1, Key, + ProtocolVersion, PublicKey, StoredValue, TimeDiff, Transaction, TransactionEntryPoint, U512, }; use super::{ @@ -60,1575 +42,543 @@ use super::{ APPROVALS_CHECKSUM_NAME, EXECUTION_RESULTS_CHECKSUM_NAME, }; use crate::{ - components::fetcher::FetchItem, - contract_runtime::types::ExecutionArtifactBuilder, - types::{self, Chunkable, ExecutableBlock, InternalEraReport, MetaTransaction}, + contract_runtime::types::{ + BalanceIdentifierResolution, EvmOriginResolution, ExecuteBlockContext, + ExecuteBlockContextError, ExecuteBlockOutcome, InitialBalanceIdentifierResult, + ProcessRequest, StaticEvmBlockHashProvider, TransactionProcessContext, + }, + types::{Chunkable, ExecutableBlock, MetaTransaction}, }; -#[derive(Default)] -struct StaticEvmBlockHashProvider { - block_hashes: BTreeMap, -} - -impl EvmBlockHashProvider for StaticEvmBlockHashProvider { - fn block_hash(&self, block_height: u64) -> EvmBlockHashProviderResult> { - Ok(self.block_hashes.get(&block_height).copied()) - } -} - -fn evm_block_context( - chainspec: &Chainspec, - block_height: u64, - block_time: BlockTime, - proposer: &PublicKey, -) -> EvmBlockContext { - EvmBlockContext { - number: block_height, - timestamp: block_time.value() / 1000, - beneficiary: EvmAddress::from_block_proposer_public_key(proposer), - gas_limit: Some(chainspec.evm_config.block_gas_limit), - base_fee: Some(chainspec.evm_config.base_fee_wei()), - } -} - -fn write_eip4788_beacon_roots( - scratch_state: &ScratchGlobalState, - state_root_hash: Digest, +/// Executes a finalized block. +#[allow(clippy::too_many_arguments)] +pub(super) fn execute_finalized_block( + data_access_layer: &DataAccessLayer, + execution_engine_v1: &ExecutionEngineV1, + execution_engine_v2: ExecutorV2, chainspec: &Chainspec, - protocol_version: ProtocolVersion, - block_context: EvmBlockContext, - parent_hash: BlockHash, -) -> Result { - if !chainspec.evm_config.enabled || chainspec.evm_config.spec < EvmSpec::Prague { - return Ok(state_root_hash); - } - - if block_context.number == 0 { - return Ok(state_root_hash); - } + metrics: Option>, + execution_pre_state: ExecutionPreState, + _evm_block_hash_provider: &dyn EvmBlockHashProvider, + executable_block: ExecutableBlock, + key_block_height_for_activation_point: u64, + current_gas_price: u8, + next_era_gas_price: Option, + last_switch_block_hash: Option, +) -> Result { + let mut exec_ctx = match ExecuteBlockContext::try_new( + &executable_block, + &execution_pre_state, + chainspec, + current_gas_price, + next_era_gas_price, + last_switch_block_hash, + data_access_layer.enable_addressable_entity, + ) { + Ok(exec_ctx) => exec_ctx, + Err(eb_err) => { + return match eb_err { + ExecuteBlockContextError::InvalidAESetting(chainspec_setting) => { + Err(BlockExecutionError::InvalidAESetting(chainspec_setting)) + } + ExecuteBlockContextError::WrongBlockHeight { + executable_block, + execution_pre_state, + } => Err(BlockExecutionError::WrongBlockHeight { + executable_block, + execution_pre_state, + }), + ExecuteBlockContextError::FailedToGetNewEraGasPrice { era_id } => { + Err(BlockExecutionError::FailedToGetNewEraGasPrice { era_id }) + } + ExecuteBlockContextError::FailedToComputeApprovalsChecksum(bytesrepr_err) => Err( + BlockExecutionError::FailedToComputeApprovalsChecksum(bytesrepr_err), + ), + } + } + }; - match scratch_state.block_global(BlockGlobalRequest::set_eip4788_parent_hash( - state_root_hash, - protocol_version, - block_context.timestamp, - parent_hash, - )) { - BlockGlobalResult::RootNotFound => Err(BlockExecutionError::RootNotFound(state_root_hash)), + // NOTE this must occur prior to any block processing as subsequent logic + // will refer to the values being written to GS. + match data_access_layer.block_global(exec_ctx.block_global_request()) { + BlockGlobalResult::RootNotFound => { + return Err(BlockExecutionError::RootNotFound( + exec_ctx.state_root_hash(), + )); + } BlockGlobalResult::Failure(err) => { - Err(BlockExecutionError::BlockGlobal(format!("{err:?}"))) + return Err(BlockExecutionError::BlockGlobal(format!("{:?}", err))); } BlockGlobalResult::Success { post_state_hash, .. - } => Ok(post_state_hash), + } => { + exec_ctx.with_state_root_hash(post_state_hash); + } } -} -fn evm_precondition_receipt(effective_gas_price: u128) -> EvmReceipt { - EvmReceipt { - status: EvmReceiptStatus::Halt(EvmHaltReason::Unknown), - gas_used: 0, - effective_gas_price, - contract_address: None, - logs: Vec::new(), + // pre-processing is finished + if let Some(metrics) = metrics.as_ref() { + metrics + .exec_block_pre_processing + .observe(exec_ctx.pre_process_elapsed()); } -} -fn execution_min_cost( - is_evm: bool, - gas_limit: Gas, - cost: U512, - baseline_motes_amount: U512, -) -> U512 { - let min_cost = gas_limit.value().min(baseline_motes_amount); - // EVM cost is already converted to motes. Do not let the raw EVM gas - // limit raise the minimum above the maximum converted fee. - if is_evm { - min_cost.min(cost) - } else { - min_cost - } -} + // scratch_state must be used for all processing and post-processing data + // from here on out, until the effects are applied at the end. + let scratch_state = data_access_layer.get_scratch_global_state(); -#[derive(Clone, Debug)] -struct EvmOriginResolution { - // Concrete payer selected before payment checks. This is deliberately a - // data-access balance identifier, not an EVM-specific balance mode, so - // the rest of block execution can use the normal hold/refund/fee - // machinery. - balance_identifier: BalanceIdentifier, - // State mutation to perform later, inside the same tracking copy as EVM - // execution. Origin resolution itself is read-only so a rejected - // transaction does not create accounts or links as a side effect. - identity_plan: EvmIdentityPlan, -} + exec_ctx.process_starting(); -impl EvmOriginResolution { - fn new(balance_identifier: BalanceIdentifier, identity_plan: EvmIdentityPlan) -> Self { - Self { - balance_identifier, - identity_plan, - } - } -} + for txn in executable_block.transactions { + let mut txn_process_ctx = + TransactionProcessContext::try_new(&txn, chainspec, current_gas_price) + .map_err(|err| BlockExecutionError::TransactionConversion(err.to_string()))?; -/// Deferred write needed to make an EVM sender's identity explicit in global state. -/// -/// The runtime makes this decision because it has both pieces of context the -/// executor should not need: the recovered transaction signer and the Casper -/// account view at the current state root. -#[derive(Clone, Copy, Debug)] -enum EvmIdentityPlan { - /// No identity write is needed. Either the identity already exists, or the - /// address must remain EVM-native. - None, - /// The EVM address has no identity pointer yet, but the recovered signer - /// already has a Casper account. Link the address to that account hash. - LinkExisting { - address: EvmAddress, - account_hash: AccountHash, - }, - /// Neither an identity pointer nor a Casper account exists for the - /// recovered signer. Create the Casper account and then link the EVM - /// address to it. - CreateAccount { - address: EvmAddress, - account_hash: AccountHash, - main_purse: casper_types::URef, - }, -} + let transaction_hash = txn_process_ctx.transaction_hash(); -/// Resolves the payer and any deferred identity write for a signed EVM transaction. -/// -/// This function only reads state. That matters because it runs before payment -/// preconditions are known to pass. If execution is later allowed, the returned -/// [`EvmIdentityPlan`] is applied in the tracking copy used for EVM execution. -fn resolve_evm_origin( - scratch_state: &ScratchGlobalState, - state_root_hash: Digest, - protocol_version: ProtocolVersion, - transaction: &casper_types::EvmTransaction, -) -> Result { - let address = transaction.from(); - // The signer gives us a Casper `AccountHash` preimage from the secp256k1 - // public key. That account hash is not derivable from the 20-byte EVM - // address alone, so identity linking must happen while the signed - // transaction is available. - let signer = transaction - .signer() - .map_err(|error| BlockExecutionError::TransactionConversion(error.to_string()))?; - let account_hash = signer.to_account_hash(); - // Native EVM identities use a deterministic purse derived from the EVM - // address. Linked Casper accounts use the account's existing main purse - // instead, so the same key pair can spend the same funds from Casper and - // Ethereum-style transaction paths. - let deterministic_purse = casper_types::evm::deterministic_purse(address); - let mut tracking_copy = scratch_state - .tracking_copy(state_root_hash)? - .ok_or(BlockExecutionError::RootNotFound(state_root_hash))?; + let lane_id = txn_process_ctx.transaction_lane(); + if !chainspec.is_supported(lane_id) { + // this transaction should not have been allowed in the block + error!( + %transaction_hash, + %lane_id, + "lane_id is currently not supported" + ); + // record it and move on. + exec_ctx.with_artifact(txn_process_ctx.into_execution_artifact()); + continue; + } - // `EvmAddr::Account` is now only an identity pointer. It is either - // `Key::Account` for a linked Casper account or `Key::URef` for an - // EVM-native purse identity. - let identity_key = Key::Evm(casper_types::EvmAddr::Account(address)); - match tracking_copy - .read(&identity_key) - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? - { - Some(StoredValue::CLValue(cl_value)) => { - let key = cl_value - .into_t::() - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; - match key { - // Existing bridge records are authoritative. Once an EVM - // address is linked, the payer is the linked Casper account's - // main purse. - Key::Account(account_hash) => Ok(EvmOriginResolution::new( - BalanceIdentifier::Account(account_hash), - EvmIdentityPlan::None, - )), - // Existing EVM-native identities keep paying from their stored - // purse. We may still plan an upgrade to a Casper link, but - // only when doing so cannot steal a contract identity or move - // balances between distinct purses. - Key::URef(purse) => { - let identity_plan = resolve_evm_native_identity_plan( - &mut tracking_copy, - protocol_version, - address, - account_hash, - purse, - deterministic_purse, - )?; - Ok(EvmOriginResolution::new( - BalanceIdentifier::Purse(purse), - identity_plan, - )) - } - other => Err(BlockExecutionError::PaymentError(format!( - "invalid EVM account identity key: {other}" - ))), + let balance_identifier = { + let ret = txn_initial_balance_identifier(&scratch_state, &exec_ctx, &txn_process_ctx) + .map_err(|err| { + error!( + %transaction_hash, + ?err, + "failed to determine balance_identifier" + ); + err + })?; + if let Some(state_err) = ret.state_result_error() { + txn_process_ctx + .with_state_result_error(state_err.clone()) + .map_err(|_| exec_ctx.root_not_found())?; } - } - Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( - "unexpected stored value for {identity_key}: expected StoredValue::CLValue(Key), found {}", - stored_value.type_name() - ))), - None => { - // No identity pointer plus non-empty EVM code means this address is - // already a contract/runtime-created EVM account. Contracts do not - // have a signing key, so they must remain EVM-native. - if evm_account_has_code(&mut tracking_copy, address)? { - return Ok(EvmOriginResolution::new( - BalanceIdentifier::Purse(deterministic_purse), - EvmIdentityPlan::None, - )); + if let Some(evm_err) = ret.evm_transaction_error() { + txn_process_ctx.with_evm_error(evm_err.clone()); } - match account_main_purse(&mut tracking_copy, protocol_version, account_hash)? { - // A Casper account exists for the recovered signer, but the EVM - // address has not been seen before. Use the account for payment - // immediately and write the bridge only if execution proceeds. - Some(_) => Ok(EvmOriginResolution::new( - BalanceIdentifier::Account(account_hash), - EvmIdentityPlan::LinkExisting { - address, - account_hash, - }, - )), - // First use of this signing pair on both sides. Runtime will - // create a Casper account whose main purse is the deterministic - // EVM purse, then write the bridge record. - None => Ok(EvmOriginResolution::new( - BalanceIdentifier::Purse(deterministic_purse), - EvmIdentityPlan::CreateAccount { - address, - account_hash, - main_purse: deterministic_purse, - }, - )), + if let Some(evm_resolution) = ret.evm_origin_resolution() { + txn_process_ctx.with_evm_origin_resolution(Some(evm_resolution.clone())); } - } - } -} -fn resolve_evm_native_identity_plan( - tracking_copy: &mut TrackingCopy, - protocol_version: ProtocolVersion, - address: EvmAddress, - account_hash: AccountHash, - purse: casper_types::URef, - deterministic_purse: casper_types::URef, -) -> Result -where - R: StateReader, -{ - // Do not overwrite contract identities, and do not turn an arbitrary purse - // identity into a Casper account link. The only EVM-native identity that is - // safe to link is the deterministic purse for this address. - if evm_account_has_code(tracking_copy, address)? || purse.addr() != deterministic_purse.addr() { - return Ok(EvmIdentityPlan::None); - } + if let Some(bi) = ret.balance_identifier() { + bi.clone() + } else { + // record it and move on. + info!( + %transaction_hash, + "unknown initial balance identifier" + ); + exec_ctx.with_artifact(txn_process_ctx.into_execution_artifact()); + continue; + } + }; - match account_main_purse(tracking_copy, protocol_version, account_hash)? { - // If the recovered Casper account already uses the same deterministic - // purse, replacing the pointer with `Key::Account` preserves the balance - // location and lets Casper-native flows see the account identity. - Some(main_purse) if main_purse.addr() == purse.addr() => { - Ok(EvmIdentityPlan::LinkExisting { - address, - account_hash, - }) - } - // A Casper account exists, but its main purse differs from the existing - // EVM-native purse. Keep the EVM-native identity to avoid moving funds - // or changing ownership semantics behind the user's back. - Some(_) => Ok(EvmIdentityPlan::None), - // No Casper account exists yet, so creating one backed by the existing - // deterministic purse preserves balances while giving the signer a - // Casper account identity. - None => Ok(EvmIdentityPlan::CreateAccount { - address, - account_hash, - main_purse: purse, - }), - } -} + txn_process_ctx.with_initial_balance_identifier(balance_identifier.clone()); -fn evm_account_has_code( - tracking_copy: &mut TrackingCopy, - address: EvmAddress, -) -> Result -where - R: StateReader, -{ - // Code hash is the cheap contract/EOA discriminator for an EVM address. A - // non-empty code hash means the address is not a user-controlled signing - // identity, so runtime must not create or link a Casper account for it. - let key = Key::Evm(casper_types::EvmAddr::CodeHash(address)); - match tracking_copy - .read(&key) - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? - { - Some(StoredValue::CLValue(cl_value)) => { - let code_hash = cl_value - .into_t::() - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; - Ok(code_hash != casper_types::evm::EMPTY_CODE_HASH) - } - Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( - "unexpected stored value for {key}: expected StoredValue::CLValue(evm::Hash), found {}", - stored_value.type_name() - ))), - None => Ok(false), - } -} + // INITIAL BALANCE + { + let initial_balance_result = + scratch_state.balance(exec_ctx.balance_request(balance_identifier.clone())); -fn evm_account_has_nonce( - tracking_copy: &mut TrackingCopy, - address: EvmAddress, -) -> Result -where - R: StateReader, -{ - let key = Key::Evm(casper_types::EvmAddr::Nonce(address)); - match tracking_copy - .read(&key) - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? - { - Some(StoredValue::CLValue(cl_value)) => { - let _nonce = cl_value - .into_t::() - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; - Ok(true) + if let BalanceResult::RootNotFound = initial_balance_result { + return Err(BlockExecutionError::RootNotFound( + exec_ctx.state_root_hash(), + )); + } + txn_process_ctx.with_initial_balance_result(initial_balance_result); } - Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( - "unexpected stored value for {key}: expected StoredValue::CLValue(u64), found {}", - stored_value.type_name() - ))), - None => Ok(false), - } -} -fn account_main_purse( - tracking_copy: &mut TrackingCopy, - protocol_version: ProtocolVersion, - account_hash: AccountHash, -) -> Result, BlockExecutionError> -where - R: StateReader, -{ - // Runtime footprints cover both legacy `StoredValue::Account` accounts and - // addressable-entity-backed accounts, so this is the authoritative account - // existence check for identity linking. - match tracking_copy.runtime_footprint_by_account_hash(protocol_version, account_hash) { - Ok((_, entity)) => entity - .main_purse() - .map(Some) - .ok_or_else(|| BlockExecutionError::PaymentError("missing account main purse".into())), - Err(TrackingCopyError::KeyNotFound(_)) => Ok(None), - Err(error) => Err(BlockExecutionError::PaymentError(error.to_string())), - } -} - -fn apply_evm_proposer_identity( - tracking_copy: &mut TrackingCopy, - protocol_version: ProtocolVersion, - proposer: &PublicKey, -) -> Result<(), BlockExecutionError> -where - R: StateReader, -{ - let address = EvmAddress::from_block_proposer_public_key(proposer); - let account_hash = proposer.to_account_hash(); - if account_main_purse(tracking_copy, protocol_version, account_hash)?.is_none() { - return Ok(()); - } - - let identity_key = Key::Evm(casper_types::EvmAddr::Account(address)); - match tracking_copy - .read(&identity_key) - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? - { - Some(StoredValue::CLValue(cl_value)) => { - let identity = cl_value - .into_t::() - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; - match identity { - Key::Account(_) | Key::URef(_) => Ok(()), - other => Err(BlockExecutionError::PaymentError(format!( - "invalid EVM account identity key: {other}" - ))), - } - } - Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( - "unexpected stored value for {identity_key}: expected StoredValue::CLValue(Key), found {}", - stored_value.type_name() - ))), - None => { - if evm_account_has_code(tracking_copy, address)? - || evm_account_has_nonce(tracking_copy, address)? - { - return Ok(()); - } - write_evm_identity(tracking_copy, address, Key::Account(account_hash)) - } - } -} - -fn apply_evm_identity_plan( - tracking_copy: &mut TrackingCopy, - protocol_version: ProtocolVersion, - plan: EvmIdentityPlan, -) -> Result<(), BlockExecutionError> -where - R: StateReader, -{ - // Identity writes are intentionally delayed until after payment - // preconditions pass. They are applied to the same tracking copy as EVM - // execution so the identity record and nonce/code/storage updates commit or - // discard together. - match plan { - EvmIdentityPlan::None => Ok(()), - EvmIdentityPlan::LinkExisting { - address, - account_hash, - } => write_evm_identity(tracking_copy, address, Key::Account(account_hash)), - EvmIdentityPlan::CreateAccount { - address, - account_hash, - main_purse, - } => { - // Another transaction in the same block may have already created - // the account through this scratch state. Avoid recreating it, but - // still write the EVM identity pointer below. - if account_main_purse(tracking_copy, protocol_version, account_hash)?.is_none() { - let account = Account::create(account_hash, NamedKeys::new(), main_purse); - tracking_copy - .create_addressable_entity_from_account(account, protocol_version) - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; - } - write_evm_identity(tracking_copy, address, Key::Account(account_hash)) + // CHECK FOR MINIMUM BALANCE + if !txn_process_ctx.has_sufficient_minimum() { + // the purse does not have enough to cover the minimum, just record it and move on. + info!( + %transaction_hash, + "Has less than minimum balance" + ); + exec_ctx.with_artifact(txn_process_ctx.into_execution_artifact()); + continue; } - } -} - -fn write_evm_identity( - tracking_copy: &mut TrackingCopy, - address: EvmAddress, - identity: Key, -) -> Result<(), BlockExecutionError> -where - R: StateReader, -{ - // Keep the bridge record minimal: a CLValue containing the identity `Key`. - // Nonce, code hash, bytecode, and storage live under their own EVM keys. - let key = Key::Evm(casper_types::EvmAddr::Account(address)); - let cl_value = CLValue::from_t(identity) - .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; - tracking_copy.write(key, StoredValue::CLValue(cl_value)); - Ok(()) -} - -/// Executes a finalized block. -#[allow(clippy::too_many_arguments)] -pub fn execute_finalized_block( - data_access_layer: &DataAccessLayer, - execution_engine_v1: &ExecutionEngineV1, - execution_engine_v2: ExecutorV2, - chainspec: &Chainspec, - metrics: Option>, - execution_pre_state: ExecutionPreState, - evm_block_hash_provider: &dyn EvmBlockHashProvider, - executable_block: ExecutableBlock, - key_block_height_for_activation_point: u64, - current_gas_price: u8, - next_era_gas_price: Option, - last_switch_block_hash: Option, -) -> Result { - let block_height = executable_block.height; - if block_height != execution_pre_state.next_block_height() { - return Err(BlockExecutionError::WrongBlockHeight { - executable_block: Box::new(executable_block), - execution_pre_state: Box::new(execution_pre_state), - }); - } - if executable_block.era_report.is_some() && next_era_gas_price.is_none() { - return Err(BlockExecutionError::FailedToGetNewEraGasPrice { - era_id: executable_block.era_id.successor(), - }); - } - let start = Instant::now(); - let protocol_version = chainspec.protocol_version(); - let activation_point_era_id = chainspec.protocol_config.activation_point.era_id(); - let prune_batch_size = chainspec.core_config.prune_batch_size; - let native_runtime_config = NativeRuntimeConfig::from_chainspec(chainspec); - let addressable_entity_enabled = chainspec.core_config.enable_addressable_entity(); - - if addressable_entity_enabled != data_access_layer.enable_addressable_entity { - return Err(BlockExecutionError::InvalidAESetting( - data_access_layer.enable_addressable_entity, - )); - } - - // scrape variables from execution pre state - let parent_hash = execution_pre_state.parent_hash(); - let parent_seed = execution_pre_state.parent_seed(); - let parent_block_hash = execution_pre_state.parent_hash(); - let pre_state_root_hash = execution_pre_state.pre_state_root_hash(); - let mut state_root_hash = pre_state_root_hash; // initial state root is parent's state root - - let payment_balance_addr = - match data_access_layer.balance_purse(BalanceIdentifierPurseRequest::new( - state_root_hash, - protocol_version, - BalanceIdentifier::Payment, - )) { - BalanceIdentifierPurseResult::RootNotFound => { - return Err(BlockExecutionError::RootNotFound(state_root_hash)) - } - BalanceIdentifierPurseResult::Failure(tce) => { - return Err(BlockExecutionError::BlockGlobal(format!("{:?}", tce))); - } - BalanceIdentifierPurseResult::Success { purse_addr } => purse_addr, - }; - - // scrape variables from executable block - let block_time = BlockTime::new(executable_block.timestamp.millis()); - - let proposer = executable_block.proposer.clone(); - let era_id = executable_block.era_id; - let mut artifacts = Vec::with_capacity(executable_block.transactions.len()); - - // set up accounting variables / settings - let insufficient_balance_handling = InsufficientBalanceHandling::HoldRemaining; - let refund_handling = chainspec.core_config.refund_handling; - let fee_handling = chainspec.core_config.fee_handling; - let baseline_motes_amount = chainspec.core_config.baseline_motes_amount_u512(); - let balance_handling = BalanceHandling::Available; - - // get scratch state, which must be used for all processing and post-processing data - // requirements. - let scratch_state = data_access_layer.get_scratch_global_state(); - - // pre-processing is finished - if let Some(metrics) = metrics.as_ref() { - metrics - .exec_block_pre_processing - .observe(start.elapsed().as_secs_f64()); - } - // grabbing transaction id's now to avoid cloning transactions - let transaction_ids = executable_block - .transactions - .iter() - .map(Transaction::fetch_id) - .collect_vec(); + // REGISTER EXEC ATTEMPT (from this point onward we charge for success and failure) + txn_process_ctx.with_exec_attempt(); - // transaction processing starts now - let txn_processing_start = Instant::now(); + // PROCESS TRANSACTION + //let process_request = process_request(&txn_process_ctx); + let process_request = txn_process_ctx.process_request(); + trace!(%transaction_hash, %process_request, "process_request created"); - // put block_time to global state - // NOTE this must occur prior to any block processing as subsequent logic - // will refer to the block time value being written to GS now. - match scratch_state.block_global(BlockGlobalRequest::block_time( - state_root_hash, - protocol_version, - block_time, - )) { - BlockGlobalResult::RootNotFound => { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); - } - BlockGlobalResult::Failure(err) => { - return Err(BlockExecutionError::BlockGlobal(format!("{:?}", err))); - } - BlockGlobalResult::Success { - post_state_hash, .. - } => { - state_root_hash = post_state_hash; - } - } + // PLACE PROCESSING HOLD TO PREVENT DOUBLE SPEND, IF REQUIRED + let requires_hold = process_request.requires_processing_hold(); + if requires_hold { + let hold_amount = txn_process_ctx.cost_to_use(); + let hold_result = scratch_state.balance_hold( + exec_ctx.balance_hold_request(balance_identifier.clone(), hold_amount), + ); - // put protocol version to global state - match scratch_state.block_global(BlockGlobalRequest::set_protocol_version( - state_root_hash, - protocol_version, - )) { - BlockGlobalResult::RootNotFound => { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); - } - BlockGlobalResult::Failure(err) => { - return Err(BlockExecutionError::BlockGlobal(format!("{:?}", err))); - } - BlockGlobalResult::Success { - post_state_hash, .. - } => { - state_root_hash = post_state_hash; - } - } + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects(exec_ctx.state_root_hash(), hold_result.effects().clone()) + .map_err(BlockExecutionError::Lmdb)?, + ); - // put enable addressable entity flag to global state - match scratch_state.block_global(BlockGlobalRequest::set_addressable_entity( - state_root_hash, - protocol_version, - addressable_entity_enabled, - )) { - BlockGlobalResult::RootNotFound => { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); - } - BlockGlobalResult::Failure(err) => { - return Err(BlockExecutionError::BlockGlobal(format!("{:?}", err))); - } - BlockGlobalResult::Success { - post_state_hash, .. - } => { - state_root_hash = post_state_hash; + txn_process_ctx + .with_balance_hold_result(&hold_result) + .map_err(|_| exec_ctx.root_not_found())?; } - } - - state_root_hash = write_eip4788_beacon_roots( - &scratch_state, - state_root_hash, - chainspec, - protocol_version, - evm_block_context(chainspec, block_height, block_time, &proposer), - parent_hash, - )?; - - let transaction_config = &chainspec.transaction_config; - - for stored_transaction in executable_block.transactions { - let evm_transaction = stored_transaction.as_evm(); - let is_evm = evm_transaction.is_some(); - let transaction = MetaTransaction::from_transaction( - &stored_transaction, - chainspec.core_config.pricing_handling, - transaction_config, - ) - .map_err(|err| BlockExecutionError::TransactionConversion(err.to_string()))?; - - let transaction_hash = stored_transaction.hash(); - let authorization_keys = stored_transaction.authorization_keys(); - - /* - we solve for halting state using a `gas limit` which is the maximum amount of - computation we will allow a given transaction to consume. the transaction itself - provides a function to determine this if provided with the current cost tables - gas_limit is ALWAYS calculated with price == 1. - - next there is the actual cost, i.e. how much we charge for that computation - this is calculated by multiplying the gas limit by the current `gas_price` - gas price has a floor of 1, and the ceiling is configured in the chainspec - NOTE: when the gas price is 1, the gas limit and the cost are coincidentally - equal because x == x * 1; thus it is recommended to run tests with - price >1 to avoid being confused by this. - - the third important value is the amount of computation consumed by executing a - transaction for native transactions there is no wasm and the consumed always - equals the limit for bytecode / wasm based transactions the consumed is based on - what opcodes were executed and can range from >=0 to <=gas_limit. - consumed is determined after execution and is used for refund & fee post-processing. - - we check these top level concerns early so that we can skip if there is an error - */ - - let lane_id = transaction.transaction_lane(); - - let mut artifact_builder = { - // NOTE: this is the allowed computation limit (gas limit) - let gas_limit = if let Some(evm_transaction) = evm_transaction { - Gas::new(evm_transaction.gas_limit()) - } else { - match transaction.gas_limit(chainspec) { - Ok(gas) => gas, - Err(ite) => { - debug!(%transaction_hash, %ite, "invalid transaction (gas limit)"); - artifacts.push( - ExecutionArtifactBuilder::pre_condition_failure( - &stored_transaction, - current_gas_price, - ite, - ) - .build(), - ); - continue; - } - } - }; - - // NOTE: this is the actual adjusted cost that we charge for. - // Native transactions use gas limit * Casper gas price. EVM - // transactions convert gas limit * EVM gas price from wei to motes. - // For accepted EIP-1559 transactions, config compliance has already required - // `max_priority_fee_per_gas == 0`, so the effective EVM gas price is the - // configured base fee capped by `max_fee_per_gas`; Casper does not charge an - // Ethereum-style priority premium while transaction priority is not based on - // gas parameters. - let cost = if let Some(evm_transaction) = evm_transaction { - evm_transaction - .max_fee_amount(&chainspec.evm_config) - .ok_or_else(|| { - BlockExecutionError::PaymentError( - "EVM fee amount overflowed U512".to_string(), - ) - })? - } else { - match stored_transaction.gas_cost(chainspec, lane_id, current_gas_price) { - Ok(motes) => motes.value(), - Err(ite) => { - debug!(%transaction_hash, "invalid transaction (motes conversion)"); - artifacts.push( - ExecutionArtifactBuilder::pre_condition_failure( - &stored_transaction, - current_gas_price, - ite, - ) - .build(), - ); - continue; - } - } - }; - - // this is the minimum we will charge, even if 0 is consumed - let min_cost = execution_min_cost(is_evm, gas_limit, cost, baseline_motes_amount); - ExecutionArtifactBuilder::new( - &stored_transaction, - gas_limit, - current_gas_price, - cost, - min_cost, - ) - }; - let is_standard_payment = transaction.is_standard_payment(); - let is_custom_payment = !is_standard_payment && transaction.is_custom_payment(); - let is_v1_wasm = transaction.is_v1_wasm(); - let is_v2_wasm = transaction.is_v2_wasm(); - let initiator_addr = stored_transaction.initiator_addr(); - let evm_origin_resolution = if let Some(evm_transaction) = evm_transaction { - Some(resolve_evm_origin( - &scratch_state, - state_root_hash, - protocol_version, - evm_transaction, - )?) - } else { - None - }; - let payer_balance_identifier = if let Some(resolution) = &evm_origin_resolution { - resolution.balance_identifier.clone() - } else { - initiator_addr - .clone() - .try_into() - .map_err(|_| BlockExecutionError::InvalidTransactionVariant)? - }; - - let refund_purse_active = is_custom_payment; - if refund_purse_active { - // if custom payment before doing any processing, initialize the initiator's main purse - // to be the refund purse for this transaction. - // NOTE: when executed, custom payment logic has the option to call set_refund_purse - // on the handle payment contract to set up a different refund purse, if desired. - let handle_refund_request = HandleRefundRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - HandleRefundMode::SetRefundPurse { - target: Box::new(payer_balance_identifier.clone()), - }, - ); - let handle_refund_result = scratch_state.handle_refund(handle_refund_request); - if let Err(root_not_found) = - artifact_builder.with_set_refund_purse_result(&handle_refund_result) - { - if root_not_found { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); - } - artifacts.push(artifact_builder.build()); - continue; // don't commit effects, move on + // PROCESS TRANSACTION + match process_request { + ProcessRequest::NoExec => { + // noop + debug!(%transaction_hash, "no exec"); } - state_root_hash = scratch_state - .commit_effects(state_root_hash, handle_refund_result.effects().clone())?; - } - - { - // Ensure the initiator's main purse can cover the penalty payment before proceeding. - let initial_balance_result = scratch_state.balance(BalanceRequest::new( - state_root_hash, - protocol_version, - payer_balance_identifier.clone(), - balance_handling, - ProofHandling::NoProofs, - )); + ProcessRequest::NoExecEvm { + effective_gas_price, + } => { + txn_process_ctx.with_zero_cost(); + let outcome = casper_executor_evm::ExecutionOutcome::no_exec(); + txn_process_ctx.with_evm_execution_outcome( + outcome, + effective_gas_price, + Effects::new(), + ); - if let Err(root_not_found) = artifact_builder - .with_initial_balance_result(initial_balance_result.clone(), baseline_motes_amount) - { - if root_not_found { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); - } - trace!(%transaction_hash, "insufficient initial balance"); - debug!(%transaction_hash, ?initial_balance_result, %baseline_motes_amount, "insufficient initial balance"); - if let Some(evm_transaction) = evm_transaction { - artifact_builder.with_zero_cost().with_evm_receipt( - evm_precondition_receipt( - evm_transaction - .effective_gas_price(chainspec.evm_config.base_fee_wei()), - ), - U512::zero(), - Effects::new(), - ); - } - artifacts.push(artifact_builder.build()); - // only reads have happened so far, and we can't charge due - // to insufficient balance, so move on with no effects committed - continue; + debug!(%transaction_hash, "no exec evm"); } - } - - let mut balance_identifier = { - if is_evm { - // EVM transactions intentionally do not participate in Casper custom payment - // or refund-purse setup. Ethereum payloads carry a gas limit and gas price fields, - // but this chain still owns the fee/refund policy through the same chainspec - // settings used by Deploy and native Transaction::V1 payloads. The EVM sender's - // main purse is therefore the payer for the processing hold, refund - // calculation, and final fee handling, while revm runs with gas fee - // charging disabled and only mutates EVM nonce, code, storage, - // logs, creates, and value transfers. - payer_balance_identifier.clone() - } else if is_standard_payment { - let contract_might_pay = - addressable_entity_enabled && transaction.is_contract_by_hash_invocation(); - - if contract_might_pay { - match invoked_contract_will_pay(&scratch_state, state_root_hash, &transaction) { - Ok(Some(entity_addr)) => BalanceIdentifier::Entity(entity_addr), - Ok(None) => { - // the initiating account pays using its main purse - trace!(%transaction_hash, "direct invocation with account payment"); - payer_balance_identifier.clone() - } - Err(err) => { - trace!(%transaction_hash, "failed to resolve contract self payment"); - artifact_builder - .with_state_result_error(err) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - BalanceIdentifier::PenalizedAccount( - initiator_addr - .account_hash() - .ok_or(BlockExecutionError::InvalidTransactionVariant)?, + ProcessRequest::NativeMint { + session_args, + entry_point, + } => { + let runtime_args = session_args + .as_named() + .ok_or(BlockExecutionError::InvalidTransactionArgs)?; + if let TransactionEntryPoint::Transfer = entry_point { + let transfer_request = + exec_ctx.transfer_request(&txn_process_ctx, runtime_args.clone()); + let transfer_result = scratch_state.transfer(transfer_request); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects( + exec_ctx.state_root_hash(), + transfer_result.effects().clone(), ) - } - } - } else { - // the initiating account pays using its main purse - trace!(%transaction_hash, "account session with standard payment"); - payer_balance_identifier.clone() - } - } else if is_v2_wasm { - // vm2 does not support custom payment, so it MUST be standard payment - // if transaction runtime is v2 then the initiating account will pay using - // the refund purse - payer_balance_identifier.clone() - } else if is_custom_payment { - // this is the custom payment flow - // the initiating account will pay, but wants to do so with a different purse or - // in a custom way. If anything goes wrong, penalize the sender, do not execute - let custom_payment_gas_limit = - Gas::new(chainspec.transaction_config.native_transfer_minimum_motes * 5); - let pay_result = match WasmV1Request::new_custom_payment( - BlockInfo::new( - state_root_hash, - block_time, - parent_block_hash, - block_height, - protocol_version, - ), - custom_payment_gas_limit, - &transaction.to_payment_input_data(), - ) { - Ok(mut pay_request) => { - pay_request - .args - .insert(ARG_AMOUNT, artifact_builder.cost_to_use()) - .map_err(|e| BlockExecutionError::PaymentError(e.to_string()))?; - execution_engine_v1.execute(&scratch_state, pay_request) - } - Err(error) => { - WasmV1Result::invalid_executable_item(custom_payment_gas_limit, error) - } - }; - - let insufficient_payment_deposited = !pay_result.balance_increased_by_amount( - payment_balance_addr, - artifact_builder.cost_to_use(), - ); - - if insufficient_payment_deposited || pay_result.error().is_some() { - // Charge initiator for the penalty payment amount - // the most expedient way to do this that aligns with later code - // is to transfer from the initiator's main purse to the payment purse - let transfer_result = scratch_state.transfer(TransferRequest::new_indirect( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - initiator_addr.clone(), - authorization_keys.clone(), - BalanceIdentifierTransferArgs::new( - None, - payer_balance_identifier.clone(), - BalanceIdentifier::Payment, - baseline_motes_amount, - None, - ), - )); - - let msg = match pay_result.error() { - Some(err) => format!("{}", err), - None => { - if insufficient_payment_deposited { - "Insufficient custom payment".to_string() - } else { - // this should be unreachable due to guard condition above - let unk = "Unknown custom payment issue"; - warn!(%transaction_hash, unk); - debug_assert!(false, "{}", unk); - unk.to_string() - } - } - }; - // commit penalty payment effects - state_root_hash = scratch_state - .commit_effects(state_root_hash, transfer_result.effects().clone())?; - artifact_builder - .with_error_message(msg) + .map_err(BlockExecutionError::Lmdb)?, + ); + txn_process_ctx + .with_gas_limit_consumed() .with_transfer_result(transfer_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - trace!(%transaction_hash, balance_identifier=?BalanceIdentifier::PenalizedPayment, "account session with custom payment failed"); - BalanceIdentifier::PenalizedPayment + .map_err(|_| exec_ctx.root_not_found())?; + } else if let TransactionEntryPoint::Burn = entry_point { + let burn_request = + exec_ctx.burn_request(&txn_process_ctx, runtime_args.clone()); + let burn_result = scratch_state.burn(burn_request); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects( + exec_ctx.state_root_hash(), + burn_result.effects().clone(), + ) + .map_err(BlockExecutionError::Lmdb)?, + ); + txn_process_ctx + .with_gas_limit_consumed() + .with_burn_result(burn_result) + .map_err(|_| exec_ctx.root_not_found())?; } else { - // commit successful effects - state_root_hash = scratch_state - .commit_effects(state_root_hash, pay_result.effects().clone())?; - artifact_builder - .with_wasm_v1_result(pay_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - trace!(%transaction_hash, balance_identifier=?BalanceIdentifier::Payment, "account session with custom payment success"); - BalanceIdentifier::Payment + txn_process_ctx.with_error_message(format!( + "Attempt to call unsupported native mint entrypoint: {}", + entry_point + )); } - } else { - BalanceIdentifier::PenalizedAccount( - initiator_addr - .account_hash() - .ok_or(BlockExecutionError::InvalidTransactionVariant)?, - ) } - }; - - let post_payment_balance_result = scratch_state.balance(BalanceRequest::new( - state_root_hash, - protocol_version, - balance_identifier.clone(), - balance_handling, - ProofHandling::NoProofs, - )); - - artifact_builder.with_available(post_payment_balance_result.available_balance().copied()); - let allow_execution = { - let is_not_penalized = !balance_identifier.is_penalty(); - // in the case of custom payment, we do all payment processing up front after checking - // if the initiator can cover the penalty payment, and then either charge the full - // amount in the happy path or the penalty amount in the sad path...in whichever case - // the sad path is handled by is_penalty and the balance in the payment purse is - // the penalty payment or the full amount but is 'sufficient' either way - let actual_cost = artifact_builder.actual_cost(); // use actual cost here - let required_balance = if let Some(evm_transaction) = evm_transaction { - evm_transaction - .required_balance(actual_cost) - .ok_or_else(|| { - BlockExecutionError::PaymentError( - "EVM value plus fee amount overflowed U512".to_string(), - ) - })? - } else { - actual_cost - }; - let is_sufficient_balance = - is_custom_payment || post_payment_balance_result.is_sufficient(required_balance); - let is_allowed_by_chainspec = chainspec.is_supported(lane_id); - let allow = is_not_penalized && is_sufficient_balance && is_allowed_by_chainspec; - if !allow { - let err_msg = { - if !is_sufficient_balance { - "Insufficient funds".to_string() - } else { - format!( - "penalized: {}, sufficient balance: {}, allowed by chainspec: {}", - !is_not_penalized, is_sufficient_balance, is_allowed_by_chainspec - ) + ProcessRequest::NativeAuction { + session_args, + entry_point, + } => { + let runtime_args = session_args + .as_named() + .ok_or(BlockExecutionError::InvalidTransactionArgs)?; + match AuctionMethod::from_parts(entry_point.clone(), runtime_args, chainspec) { + Ok(auction_method) => { + let bidding_result = scratch_state + .bidding(exec_ctx.bidding_request(&txn_process_ctx, auction_method)); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects( + exec_ctx.state_root_hash(), + bidding_result.effects().clone(), + ) + .map_err(BlockExecutionError::Lmdb)?, + ); + txn_process_ctx + .with_gas_limit_consumed() + .with_bidding_result(bidding_result) + .map_err(|_| exec_ctx.root_not_found())?; + } + Err(ame) => { + error!( + %transaction_hash, + ?ame, + "failed to determine auction method" + ); + txn_process_ctx.with_auction_method_error(&ame); } }; - if artifact_builder.error_message().is_none() { - artifact_builder.with_error_message(err_msg); - } - info!(%transaction_hash, ?balance_identifier, ?is_sufficient_balance, ?is_not_penalized, ?is_allowed_by_chainspec, "payment preprocessing unsuccessful"); - } else { - debug!(%transaction_hash, ?balance_identifier, ?is_sufficient_balance, ?is_not_penalized, ?is_allowed_by_chainspec, "payment preprocessing successful"); - } - allow - }; - - if allow_execution { - debug!(%transaction_hash, ?allow_execution, "execution allowed"); - if is_standard_payment { - // place a processing hold on the paying account to prevent double spend. - let hold_amount = artifact_builder.cost_to_use(); - let hold_request = BalanceHoldRequest::new_processing_hold( - state_root_hash, - protocol_version, - balance_identifier.clone(), - hold_amount, - insufficient_balance_handling, - ); - let hold_result = scratch_state.balance_hold(hold_request); - state_root_hash = - scratch_state.commit_effects(state_root_hash, hold_result.effects().clone())?; - artifact_builder - .with_balance_hold_result(&hold_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; } - - trace!(%transaction_hash, ?lane_id, "eligible for execution"); - match lane_id { - lane_id if lane_id == MINT_LANE_ID => { - let transaction_args = transaction.session_args(); - let runtime_args = transaction_args - .as_named() - .ok_or(BlockExecutionError::InvalidTransactionArgs)?; - let entry_point = transaction.entry_point(); - if let TransactionEntryPoint::Transfer = entry_point { - let transfer_result = - scratch_state.transfer(TransferRequest::with_runtime_args( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - initiator_addr.clone(), - authorization_keys, - runtime_args.clone(), - )); - state_root_hash = scratch_state - .commit_effects(state_root_hash, transfer_result.effects().clone())?; - artifact_builder - .consume_limit() - .with_transfer_result(transfer_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - } else if let TransactionEntryPoint::Burn = entry_point { - let burn_result = scratch_state.burn(BurnRequest::with_runtime_args( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - initiator_addr.clone(), - authorization_keys, - runtime_args.clone(), - )); - state_root_hash = scratch_state - .commit_effects(state_root_hash, burn_result.effects().clone())?; - artifact_builder - .consume_limit() - .with_burn_result(burn_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - } else { - artifact_builder.with_error_message(format!( - "Attempt to call unsupported native mint entrypoint: {}", - entry_point - )); - } - } - lane_id if lane_id == AUCTION_LANE_ID => { - let transaction_args = transaction.session_args(); - let runtime_args = transaction_args - .as_named() - .ok_or(BlockExecutionError::InvalidTransactionArgs)?; - let entry_point = transaction.entry_point(); - match AuctionMethod::from_parts(entry_point, runtime_args, chainspec) { - Ok(auction_method) => { - let bidding_result = scratch_state.bidding(BiddingRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - initiator_addr.clone(), - authorization_keys, - auction_method, - )); - state_root_hash = scratch_state.commit_effects( - state_root_hash, - bidding_result.effects().clone(), - )?; - artifact_builder - .consume_limit() - .with_bidding_result(bidding_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - } - Err(ame) => { - error!( - %transaction_hash, - ?ame, - "failed to determine auction method" - ); - artifact_builder.with_auction_method_error(&ame); - } - }; - } - _ if is_evm => { - let evm_transaction = evm_transaction.expect("EVM transaction should exist"); - let base_fee_wei = chainspec.evm_config.base_fee_wei(); - let block_context = - evm_block_context(chainspec, block_height, block_time, &proposer); - let request = EvmExecuteRequest { - block: block_context, - kind: EvmExecuteKind::Transaction(Box::new(evm_transaction.clone())), - }; - let mut tracking_copy = scratch_state - .tracking_copy(state_root_hash)? - .ok_or(BlockExecutionError::RootNotFound(state_root_hash))?; - if let Some(resolution) = &evm_origin_resolution { - // Apply the deferred bridge/account creation only now, - // after balance preconditions have allowed execution. - // This keeps rejected EVM transactions from mutating - // identity state and makes the identity write atomic - // with the revm state transition below. - apply_evm_identity_plan( - &mut tracking_copy, - protocol_version, - resolution.identity_plan, - )?; - } - apply_evm_proposer_identity(&mut tracking_copy, protocol_version, &proposer)?; - let outcome = EvmExecutor::new(chainspec.evm_config) - .execute_with_block_hash_provider( - &mut tracking_copy, - request, - evm_block_hash_provider, - ) - .map_err(|error| { - BlockExecutionError::TransactionConversion(error.to_string()) - })?; - let execution_effects = tracking_copy.effects(); - state_root_hash = - scratch_state.commit_effects(state_root_hash, execution_effects.clone())?; - let effective_gas_price = evm_transaction.effective_gas_price(base_fee_wei); - let consumed = if matches!(outcome.status, EvmExecutionStatus::Success) { - evm_transaction - .fee_amount(outcome.gas_used, &chainspec.evm_config) - .ok_or_else(|| { - BlockExecutionError::PaymentError( - "EVM fee amount overflowed U512".to_string(), + ProcessRequest::WasmV1 { session_input_data } => { + exec_ctx.wasm_v1_starting(); + match exec_ctx.wasm_v1_session_request(&txn_process_ctx, &session_input_data) { + Ok(wasm_v1_request) => { + trace!(%transaction_hash, ?lane_id, ?wasm_v1_request, "able to get wasm v1 request"); + let wasm_v1_result = + execution_engine_v1.execute(&scratch_state, wasm_v1_request); + trace!(%transaction_hash, ?lane_id, ?wasm_v1_result, "able to get wasm v1 result"); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects( + exec_ctx.state_root_hash(), + wasm_v1_result.effects().clone(), ) - })? - } else { - artifact_builder.cost_to_use() - }; - artifact_builder.with_evm_receipt( - outcome.to_receipt(effective_gas_price), - consumed, - execution_effects, - ); - } - _ if is_v1_wasm => { - let wasm_v1_start = Instant::now(); - let session_input_data = transaction.to_session_input_data(); - match WasmV1Request::new_session( - BlockInfo::new( - state_root_hash, - block_time, - parent_block_hash, - block_height, - protocol_version, - ), - artifact_builder.gas_limit(), - &session_input_data, - ) { - Ok(wasm_v1_request) => { - trace!(%transaction_hash, ?lane_id, ?wasm_v1_request, "able to get wasm v1 request"); - let wasm_v1_result = - execution_engine_v1.execute(&scratch_state, wasm_v1_request); - trace!(%transaction_hash, ?lane_id, ?wasm_v1_result, "able to get wasm v1 result"); - state_root_hash = scratch_state.commit_effects( - state_root_hash, - wasm_v1_result.effects().clone(), - )?; - // note: consumed is scraped from wasm_v1_result along w/ other fields - artifact_builder - .with_wasm_v1_result(wasm_v1_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - } - Err(ire) => { - debug!(%transaction_hash, ?lane_id, ?ire, "unable to get wasm v1 request"); - artifact_builder.with_invalid_wasm_v1_request(&ire); - } - }; - if let Some(metrics) = metrics.as_ref() { - metrics - .exec_wasm_v1 - .observe(wasm_v1_start.elapsed().as_secs_f64()); + .map_err(BlockExecutionError::Lmdb)?, + ); + // note: consumed is scraped from wasm_v1_result along w/ other fields + txn_process_ctx + .with_wasm_v1_result(wasm_v1_result) + .map_err(|_| exec_ctx.root_not_found())?; + } + Err(ire) => { + debug!(%transaction_hash, ?lane_id, ?ire, "unable to get wasm v1 request"); + txn_process_ctx.with_invalid_wasm_v1_request(&ire); } + }; + if let Some(metrics) = metrics.as_ref() { + metrics.exec_wasm_v1.observe(exec_ctx.wasm_v1_elapsed()); } - _ if is_v2_wasm => match WasmV2Request::new( - artifact_builder.gas_limit(), - chainspec.network_config.name.clone(), - state_root_hash, - parent_block_hash, - block_height, - &transaction, - ) { + } + ProcessRequest::WasmV2 { transaction_input } => { + exec_ctx.wasm_v2_starting(); + match exec_ctx.wasm_v2_request(&txn_process_ctx, transaction_input.clone()) { Ok(wasm_v2_request) => { + let pre_root = exec_ctx.state_root_hash(); match wasm_v2_request.execute( &execution_engine_v2, - state_root_hash, + pre_root, &scratch_state, ) { Ok(wasm_v2_result) => { match &wasm_v2_result { WasmV2Result::Install(install_result) => { info!( - contract_hash=base16::encode_lower(&install_result.smart_contract_addr()), - pre_state_root_hash=%state_root_hash, - post_state_root_hash=%install_result.post_state_hash(), - "install contract result"); + contract_hash=base16::encode_lower(&install_result.smart_contract_addr()), + pre_state_root_hash=%pre_root, + post_state_root_hash=%install_result.post_state_hash(), + "install contract result"); } WasmV2Result::Execute(execute_result) => { info!( - pre_state_root_hash=%state_root_hash, - post_state_root_hash=%execute_result.post_state_hash(), - host_error=?execute_result.host_error.as_ref(), - "execute contract result"); + pre_state_root_hash=%pre_root, + post_state_root_hash=%execute_result.post_state_hash(), + host_error=?execute_result.host_error.as_ref(), + "execute contract result"); } } - state_root_hash = wasm_v2_result.post_state_hash(); - artifact_builder.with_wasm_v2_result(wasm_v2_result); + exec_ctx.with_state_root_hash(wasm_v2_result.post_state_hash()); + txn_process_ctx.with_wasm_v2_result(wasm_v2_result); } Err(wasm_v2_error) => { - artifact_builder.with_wasm_v2_error(wasm_v2_error); + txn_process_ctx.with_wasm_v2_error(wasm_v2_error); } } } Err(ire) => { debug!(%transaction_hash, ?lane_id, ?ire, "unable to get wasm v2 request"); - artifact_builder.with_invalid_wasm_v2_request(ire); + txn_process_ctx.with_invalid_wasm_v2_request(ire); } - }, - _ => { - // it is currently not possible to specify a vm other than v1 or v2 on the - // transaction itself, so this should be unreachable - unreachable!("Unknown VM target") + } + if let Some(metrics) = metrics.as_ref() { + metrics.exec_wasm_v2.observe(exec_ctx.wasm_v2_elapsed()); } } - } + ProcessRequest::EvmV1 { + evm_txn, + base_fee_wei, + effective_gas_price, + block_gas_limit, + } => { + exec_ctx.evm_v1_starting(); + + let pre_hash = exec_ctx.state_root_hash(); + let mut tracking_copy = scratch_state + .tracking_copy(pre_hash) + .map_err(BlockExecutionError::Lmdb)? + .ok_or(BlockExecutionError::RootNotFound(pre_hash))?; + + // TODO: move origin resolution to before txn process request + // if let Some(resolution) = &evm_origin_resolution { + // // Apply the deferred bridge/account creation only now, + // // after balance preconditions have allowed execution. + // // This keeps rejected EVM transactions from mutating + // // identity state and makes the identity write atomic + // // with the revm state transition below. + // apply_evm_identity_plan( + // &mut tracking_copy, + // protocol_version, + // resolution.identity_plan(), + // )?; + // } + // apply_evm_proposer_identity(&mut tracking_copy, protocol_version, + // &proposer)?; + + let evm_v1_request = + exec_ctx.evm_v1_request(evm_txn.clone(), block_gas_limit, base_fee_wei); + let outcome = EvmExecutor::new(chainspec.evm_config) + .execute(&mut tracking_copy, evm_v1_request) + .map_err(|error| { + BlockExecutionError::TransactionConversion(error.to_string()) + })?; + let execution_effects = tracking_copy.effects(); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects(pre_hash, execution_effects.clone()) + .map_err(BlockExecutionError::Lmdb)?, + ); - if is_evm && !allow_execution { - let effective_gas_price = evm_transaction - .expect("EVM transaction should exist") - .effective_gas_price(chainspec.evm_config.base_fee_wei()); - artifact_builder.with_zero_cost().with_evm_receipt( - evm_precondition_receipt(effective_gas_price), - U512::zero(), - Effects::new(), - ); - artifacts.push(artifact_builder.build()); - continue; + txn_process_ctx.with_evm_execution_outcome( + outcome, + effective_gas_price, + execution_effects, + ); + if let Some(metrics) = metrics.as_ref() { + metrics.exec_evm_v1.observe(exec_ctx.evm_v1_elapsed()); + } + } + ProcessRequest::Unknown => { + // this should be unreachable + unreachable!("Unknown transaction execution target"); + } } - // clear all holds on the balance_identifier purse before payment processing + // CLEAR ALL EXPIRED BALANCE HOLDS { - let hold_request = BalanceHoldRequest::new_clear( - state_root_hash, - protocol_version, - BalanceHoldKind::All, - balance_identifier.clone(), - ); + let hold_request = exec_ctx + .clear_balance_hold_request(BalanceHoldKind::All, balance_identifier.clone()); let hold_result = scratch_state.balance_hold(hold_request); - state_root_hash = - scratch_state.commit_effects(state_root_hash, hold_result.effects().clone())?; - artifact_builder + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects(exec_ctx.state_root_hash(), hold_result.effects().clone()) + .map_err(BlockExecutionError::Lmdb)?, + ); + txn_process_ctx .with_balance_hold_result(&hold_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; + .map_err(|_| exec_ctx.root_not_found())?; } - // handle refunds per the chainspec determined setting. - let refund_amount = { - let consumed = - if balance_identifier.is_penalty() || artifact_builder.error_message().is_some() { - artifact_builder.cost_to_use() // no refund for penalty - } else { - artifact_builder.consumed() - }; - - let available = artifact_builder.available().unwrap_or(U512::zero()); - - let refund_mode = match refund_handling { - RefundHandling::NoRefund => { - if fee_handling.is_no_fee() && is_custom_payment { - // in no fee mode, we need to return the motes to the refund purse, - // and then point the balance_identifier to the refund purse - // this will result in the downstream no fee handling logic - // placing a hold on the correct purse. - balance_identifier = BalanceIdentifier::Refund; - Some(HandleRefundMode::RefundNoFeeCustomPayment { - initiator_addr: Box::new(initiator_addr.clone()), - limit: artifact_builder.limit(), - gas_price: current_gas_price, - cost: artifact_builder.cost_to_use(), - }) - } else { - None - } - } - RefundHandling::Burn { refund_ratio } => { - let (limit, gas_price) = if is_evm { - (artifact_builder.cost_to_use(), 1) - } else { - (artifact_builder.limit(), current_gas_price) - }; - Some(HandleRefundMode::Burn { - limit, - gas_price, - cost: artifact_builder.cost_to_use(), - consumed, - source: Box::new(balance_identifier.clone()), - ratio: refund_ratio, - available, - }) - } - RefundHandling::Refund { refund_ratio } => { - let source = Box::new(balance_identifier.clone()); - if is_custom_payment { - // in custom payment we have to do all payment handling up front. - // therefore, if refunds are turned on we have to transfer the refunded - // amount back to the specified refund purse. - - // the refund purse for a given transaction is set to the initiator's main - // purse by default, but the custom payment provided by the initiator can - // set a different purse when executed. thus, the handle payment system - // contract tracks a refund purse and is handled internally at processing - // time. Outer logic should never assume or refer to a specific purse for - // purposes of refund. instead, `BalanceIdentifier::Refund` is used by outer - // logic, which is interpreted by inner logic to use the currently set - // refund purse. - Some(HandleRefundMode::Refund { - initiator_addr: Box::new(initiator_addr.clone()), - limit: artifact_builder.limit(), - gas_price: current_gas_price, - consumed, - cost: artifact_builder.cost_to_use(), - ratio: refund_ratio, - source, - target: Box::new(BalanceIdentifier::Refund), - available, - }) - } else { - // in normal payment handling we put a temporary processing hold - // on the paying purse rather than take the token up front. - // thus, here we only want to determine the refund amount rather than - // attempt to process a refund on something we haven't actually taken yet. - // later in the flow when the processing hold is released and payment is - // finalized we reduce the amount taken by the refunded amount. This avoids - // the churn of taking the token up front via transfer (which writes - // multiple permanent records) and then transfer some of it back (which - // writes more permanent records). - let (limit, gas_price) = if is_evm { - (artifact_builder.cost_to_use(), 1) - } else { - (artifact_builder.limit(), current_gas_price) - }; - Some(HandleRefundMode::CalculateAmount { - limit, - gas_price, - consumed, - cost: artifact_builder.cost_to_use(), - ratio: refund_ratio, - available, - }) - } - } - }; - match refund_mode { - Some(refund_mode) => { - let handle_refund_request = HandleRefundRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - refund_mode, - ); - let handle_refund_result = scratch_state.handle_refund(handle_refund_request); - let refunded_amount = handle_refund_result.refund_amount(); - state_root_hash = scratch_state - .commit_effects(state_root_hash, handle_refund_result.effects().clone())?; - artifact_builder - .with_handle_refund_result(&handle_refund_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - - refunded_amount - } - None => U512::zero(), + // HANDLE REFUND (IF ANY) + match exec_ctx.refund_mode(&txn_process_ctx) { + None => { + txn_process_ctx.with_refund_amount(U512::zero()); } - }; - artifact_builder.with_refund_amount(refund_amount); - - // take the lower of the difference between cost - refund OR available - let fee_amount = artifact_builder - .cost_to_use() - .saturating_sub(refund_amount) - .min(artifact_builder.available().unwrap_or(U512::zero())); - - // handle fees per the chainspec determined setting. - let handle_fee_result = match fee_handling { - FeeHandling::NoFee => { - // in this mode, a gas hold is placed on the payer's purse. - let hold_request = BalanceHoldRequest::new_gas_hold( - state_root_hash, - protocol_version, - balance_identifier, - fee_amount, - insufficient_balance_handling, - ); - let hold_result = scratch_state.balance_hold(hold_request); - state_root_hash = - scratch_state.commit_effects(state_root_hash, hold_result.effects().clone())?; - artifact_builder - .with_balance_hold_result(&hold_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; - let handle_fee_request = HandleFeeRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - HandleFeeMode::credit(proposer.clone(), fee_amount, era_id), - ); - scratch_state.handle_fee(handle_fee_request) - } - FeeHandling::Burn => { - // in this mode, the fee portion is burned. - let handle_fee_request = HandleFeeRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - HandleFeeMode::burn(balance_identifier, Some(fee_amount)), - ); - scratch_state.handle_fee(handle_fee_request) - } - FeeHandling::PayToProposer => { - // in this mode, the consumed gas is paid as a fee to the block proposer - let handle_fee_request = HandleFeeRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - HandleFeeMode::pay( - initiator_addr - .account_hash() - .map(|_| Box::new(initiator_addr.clone())), - balance_identifier, - BalanceIdentifier::Public(*(proposer.clone())), - fee_amount, - ), - ); - scratch_state.handle_fee(handle_fee_request) - } - FeeHandling::Accumulate => { - // in this mode, consumed gas is accumulated into a single purse - // for later distribution - let handle_fee_request = HandleFeeRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - HandleFeeMode::pay( - initiator_addr - .account_hash() - .map(|_| Box::new(initiator_addr.clone())), - balance_identifier, - BalanceIdentifier::Accumulate, - fee_amount, - ), + Some(refund_mode) => { + let handle_refund_request = + exec_ctx.handle_refund_request(&txn_process_ctx, refund_mode); + let handle_refund_result = scratch_state.handle_refund(handle_refund_request); + let refunded_amount = handle_refund_result.refund_amount(); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects( + exec_ctx.state_root_hash(), + handle_refund_result.effects().clone(), + ) + .map_err(BlockExecutionError::Lmdb)?, ); - scratch_state.handle_fee(handle_fee_request) - } - }; + txn_process_ctx + .with_handle_refund_result(&handle_refund_result) + .map_err(|_| exec_ctx.root_not_found())?; - state_root_hash = - scratch_state.commit_effects(state_root_hash, handle_fee_result.effects().clone())?; - - artifact_builder - .with_handle_fee_result(&handle_fee_result) - .map_err(|_| BlockExecutionError::RootNotFound(state_root_hash))?; + txn_process_ctx.with_refund_amount(refunded_amount); + } + } - // clear refund purse if it was set - if refund_purse_active { - // if refunds are turned on we initialize the refund purse to the initiator's main - // purse before doing any processing. NOTE: when executed, custom payment logic - // has the option to call set_refund_purse on the handle payment contract to set - // up a different refund purse, if desired. - let handle_refund_request = HandleRefundRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - transaction_hash, - HandleRefundMode::ClearRefundPurse, - ); - let handle_refund_result = scratch_state.handle_refund(handle_refund_request); - if let Err(root_not_found) = - artifact_builder.with_clear_refund_purse_result(&handle_refund_result) - { - if root_not_found { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); + // HANDLE FEE (IF ANY) + match exec_ctx.fee_mode(&txn_process_ctx) { + None => {} + Some(fee_mode) => { + if fee_mode.requires_hold() { + let hold_request = + exec_ctx.gas_hold_request(balance_identifier, txn_process_ctx.fee_amount()); + let hold_result = scratch_state.balance_hold(hold_request); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects( + exec_ctx.state_root_hash(), + hold_result.effects().clone(), + ) + .map_err(BlockExecutionError::Lmdb)?, + ); + txn_process_ctx + .with_balance_hold_result(&hold_result) + .map_err(|_| exec_ctx.root_not_found())?; } - warn!( - "{}", - artifact_builder.error_message().unwrap_or( - "unknown error encountered when attempting to clear refund purse" - .to_string() - ) + let handle_fee_request = exec_ctx.handle_fee_request(&txn_process_ctx, fee_mode); + let handle_fee_result = scratch_state.handle_fee(handle_fee_request); + exec_ctx.with_state_root_hash( + scratch_state + .commit_effects( + exec_ctx.state_root_hash(), + handle_fee_result.effects().clone(), + ) + .map_err(BlockExecutionError::Lmdb)?, ); + txn_process_ctx + .with_handle_fee_result(&handle_fee_result) + .map_err(|_| exec_ctx.root_not_found())?; } - state_root_hash = scratch_state - .commit_effects(state_root_hash, handle_refund_result.effects().clone())?; + }; + + if let Some(err_msg) = txn_process_ctx.error_message() { + debug!(%transaction_hash, ?err_msg, "transaction error"); } - artifacts.push(artifact_builder.build()); + exec_ctx.with_artifact(txn_process_ctx.into_execution_artifact()); } // transaction processing is finished if let Some(metrics) = metrics.as_ref() { metrics .exec_block_tnx_processing - .observe(txn_processing_start.elapsed().as_secs_f64()); + .observe(exec_ctx.process_elapsed()); } - // post-processing starts now - let post_processing_start = Instant::now(); + exec_ctx.post_process_starting(); - // calculate and store checksums for approvals and execution effects across the transactions in - // the block we do this so that the full set of approvals and the full set of effect metadata - // can be verified if necessary for a given block. the block synchronizer in particular - // depends on the existence of such checksums. - let transaction_approvals_hashes = { - let approvals_checksum = types::compute_approvals_checksum(transaction_ids.clone()) - .map_err(BlockExecutionError::FailedToComputeApprovalsChecksum)?; + // REGISTER CHECKSUMS + { + // the canonical full set of approvals and metadata must be historically verifiable. + // to allow this, we must calculate and store checksums for approvals and execution effects + // across all transactions in the block. + // block synchronization uses these checksums to ensure correct complete block data. + // TODO: shift this to an iterator if possible + let artifacts = exec_ctx.execution_artifacts(); let execution_results_checksum = compute_execution_results_checksum( artifacts.iter().map(|artifact| &artifact.execution_result), )?; + let transaction_ids_checksum = exec_ctx.transaction_ids_checksum(); + let mut checksum_registry = ChecksumRegistry::new(); - checksum_registry.insert(APPROVALS_CHECKSUM_NAME, approvals_checksum); + checksum_registry.insert(APPROVALS_CHECKSUM_NAME, transaction_ids_checksum); checksum_registry.insert(EXECUTION_RESULTS_CHECKSUM_NAME, execution_results_checksum); let mut effects = Effects::new(); @@ -1640,17 +590,15 @@ pub fn execute_finalized_block( .into(), ), )); - scratch_state.commit_effects(state_root_hash, effects)?; - transaction_ids - .into_iter() - .map(|id| id.approvals_hash()) - .collect() + scratch_state + .commit_effects(exec_ctx.state_root_hash(), effects) + .map_err(BlockExecutionError::Lmdb)?; }; if let Some(metrics) = metrics.as_ref() { metrics .txn_approvals_hashes_calculation - .observe(post_processing_start.elapsed().as_secs_f64()); + .observe(exec_ctx.post_process_elapsed()); } // Pay out ̶b̶l̶o̶c̶k̶ e͇r͇a͇ rewards @@ -1660,78 +608,69 @@ pub fn execute_finalized_block( // thus if in future the calling logic passes rewards per block it should just work as is. // This auto-commits. if let Some(rewards) = &executable_block.rewards { - let block_rewards_payout_start = Instant::now(); + exec_ctx.block_rewards_starting(); + // Pay out block fees, if relevant. This auto-commits { - let fee_req = FeeRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - block_time, - ); - debug!(?fee_req, "distributing fees"); - match scratch_state.distribute_fees(fee_req) { + let fee_request = exec_ctx.block_fee_request(); + debug!(?fee_request, "distributing block fees"); + match scratch_state.distribute_fees(fee_request) { FeeResult::RootNotFound => { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); + return Err(exec_ctx.root_not_found()); } FeeResult::Failure(fer) => return Err(BlockExecutionError::DistributeFees(fer)), FeeResult::Success { post_state_hash, .. } => { debug!("fee distribution success"); - state_root_hash = post_state_hash; + exec_ctx.with_state_root_hash(post_state_hash); } } } - let rewards_req = BlockRewardsRequest::new( - native_runtime_config.clone(), - state_root_hash, - protocol_version, - block_time, - rewards.clone(), - ); - debug!(?rewards_req, "distributing rewards"); - match scratch_state.distribute_block_rewards(rewards_req) { - BlockRewardsResult::RootNotFound => { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); - } - BlockRewardsResult::Failure(bre) => { - return Err(BlockExecutionError::DistributeBlockRewards(bre)); - } - BlockRewardsResult::Success { - post_state_hash, .. - } => { - debug!("rewards distribution success"); - state_root_hash = post_state_hash; + // Pay out block rewards, if relevant. This auto-commits + { + let rewards_request = exec_ctx.block_rewards_request(rewards.clone()); + debug!(?rewards_request, "distributing block rewards"); + match scratch_state.distribute_block_rewards(rewards_request) { + BlockRewardsResult::RootNotFound => { + return Err(exec_ctx.root_not_found()); + } + BlockRewardsResult::Failure(bre) => { + return Err(BlockExecutionError::DistributeBlockRewards(bre)); + } + BlockRewardsResult::Success { + post_state_hash, .. + } => { + debug!("rewards distribution success"); + exec_ctx.with_state_root_hash(post_state_hash); + } } } + if let Some(metrics) = metrics.as_ref() { metrics .block_rewards_payout - .observe(block_rewards_payout_start.elapsed().as_secs_f64()); + .observe(exec_ctx.block_rewards_elapsed()); } } // if era report is some, this is a switch block. a series of end-of-era extra processing must // transpire before this block is entirely finished. - let step_outcome = if let Some(era_report) = &executable_block.era_report { + if let Some(era_report) = &executable_block.era_report { // step processing starts now - let step_processing_start = Instant::now(); + exec_ctx.step_starting(); + + let step_request = exec_ctx.step_request(era_report); debug!("committing step"); - let step_effects = match commit_step( - native_runtime_config, - &scratch_state, - metrics.clone(), - protocol_version, - state_root_hash, - era_report.clone(), - block_time.value(), - executable_block.era_id.successor(), - ) { + let step_result = scratch_state.step(step_request); + debug_assert!(step_result.is_success(), "{:?}", step_result); + trace!(?step_result, "step response"); + + let step_effects = match step_result { StepResult::RootNotFound => { - return Err(BlockExecutionError::RootNotFound(state_root_hash)); + return Err(exec_ctx.root_not_found()); } StepResult::Failure(err) => return Err(BlockExecutionError::Step(err)), StepResult::Success { @@ -1739,16 +678,22 @@ pub fn execute_finalized_block( post_state_hash, .. } => { - state_root_hash = post_state_hash; + exec_ctx.with_state_root_hash(post_state_hash); effects } }; debug!("step committed"); - let era_validators_req = EraValidatorsRequest::new(state_root_hash); - let era_validators_result = data_access_layer.era_validators(era_validators_req); + if let Some(metrics) = metrics.as_ref() { + let elapsed = exec_ctx.step_elapsed(); + metrics.commit_step.observe(elapsed); + metrics.latest_commit_step.set(elapsed); + } - let upcoming_era_validators = match era_validators_result { + let era_validators_request = exec_ctx.era_validators_request(); + + let upcoming_era_validators = match data_access_layer.era_validators(era_validators_request) + { EraValidatorsResult::RootNotFound => { panic!("root not found"); } @@ -1768,54 +713,58 @@ pub fn execute_finalized_block( if let Some(metrics) = metrics.as_ref() { metrics .exec_block_step_processing - .observe(step_processing_start.elapsed().as_secs_f64()); + .observe(exec_ctx.step_elapsed()); } - Some(StepOutcome { + + exec_ctx.with_step_outcome(StepOutcome { step_effects, upcoming_era_validators, - }) - } else { - None - }; + }); + } // Pruning -- this is orthogonal to the contents of the block, but we deliberately do it // at the end to avoid a read ordering issue during block execution. - if let Some(previous_block_height) = block_height.checked_sub(1) { + if let Some(previous_block_height) = exec_ctx.prev_block_height() { + let activation_point_era_id = exec_ctx.activation_point_era_id(); + let prune_batch_size = exec_ctx.prune_batch_size(); + if let Some(keys_to_prune) = calculate_prune_eras( activation_point_era_id, key_block_height_for_activation_point, previous_block_height, prune_batch_size, ) { - let pruning_start = Instant::now(); + exec_ctx.prune_starting(); let first_key = keys_to_prune.first().copied(); let last_key = keys_to_prune.last().copied(); + let prune_root = exec_ctx.state_root_hash(); info!( previous_block_height, %key_block_height_for_activation_point, - %state_root_hash, + %prune_root, first_key=?first_key, last_key=?last_key, "commit prune: preparing prune config" ); - let request = PruneRequest::new(state_root_hash, keys_to_prune); - match scratch_state.prune(request) { + let prune_request = exec_ctx.prune_request(keys_to_prune); + match scratch_state.prune(prune_request) { PruneResult::RootNotFound => { error!( previous_block_height, - %state_root_hash, + %prune_root, "commit prune: root not found" ); - panic!( - "Root {} not found while performing a prune.", - state_root_hash - ); + return Err(exec_ctx.root_not_found()); + } + PruneResult::Failure(tce) => { + error!(?tce, "commit prune: failure"); + return Err(tce.into()); } PruneResult::MissingKey => { warn!( previous_block_height, - %state_root_hash, + %prune_root, "commit prune: key does not exist" ); } @@ -1825,42 +774,39 @@ pub fn execute_finalized_block( info!( previous_block_height, %key_block_height_for_activation_point, - %state_root_hash, + %prune_root, %post_state_hash, first_key=?first_key, last_key=?last_key, "commit prune: success" ); - state_root_hash = post_state_hash; - } - PruneResult::Failure(tce) => { - error!(?tce, "commit prune: failure"); - return Err(tce.into()); + exec_ctx.with_state_root_hash(post_state_hash); } } if let Some(metrics) = metrics.as_ref() { - metrics - .pruning_time - .observe(pruning_start.elapsed().as_secs_f64()); + metrics.pruning_time.observe(exec_ctx.prune_elapsed()); } } } { - let database_write_start = Instant::now(); // Finally, the new state-root-hash from the cumulative changes to global state is // returned when they are written to LMDB. - state_root_hash = data_access_layer.write_scratch_to_db(state_root_hash, scratch_state)?; + exec_ctx.db_write_starting(); + exec_ctx.with_state_root_hash( + data_access_layer + .write_scratch_to_db(exec_ctx.state_root_hash(), scratch_state) + .map_err(BlockExecutionError::Lmdb)?, + ); if let Some(metrics) = metrics.as_ref() { metrics .scratch_lmdb_write_time - .observe(database_write_start.elapsed().as_secs_f64()); + .observe(exec_ctx.db_write_elapsed()); } // Flush once, after all data mutation. - let database_flush_start = Instant::now(); - let flush_req = FlushRequest::new(); - let flush_result = data_access_layer.flush(flush_req); + exec_ctx.db_flush_starting(); + let flush_result = data_access_layer.flush(FlushRequest::new()); if let Err(gse) = flush_result.as_error() { error!("failed to flush lmdb"); return Err(BlockExecutionError::Lmdb(gse)); @@ -1868,112 +814,52 @@ pub fn execute_finalized_block( if let Some(metrics) = metrics.as_ref() { metrics .database_flush_time - .observe(database_flush_start.elapsed().as_secs_f64()); + .observe(exec_ctx.db_flush_elapsed()); } } - // the rest of this is post process, picking out data bits to return to caller - let next_era_id = executable_block.era_id.successor(); - let maybe_next_era_validator_weights: Option<(BTreeMap, u8)> = - match step_outcome.as_ref() { - None => None, - Some(effects_and_validators) => { - match effects_and_validators - .upcoming_era_validators - .get(&next_era_id) - .cloned() - { - Some(validators) => next_era_gas_price.map(|gas_price| (validators, gas_price)), - None => None, - } - } - }; - - let era_end = match ( - executable_block.era_report, - maybe_next_era_validator_weights, - ) { - (None, None) => None, - ( - Some(InternalEraReport { - equivocators, - inactive_validators, - }), - Some((next_era_validator_weights, next_era_gas_price)), - ) => Some(EraEndV2::new( - equivocators, - inactive_validators, - next_era_validator_weights, - executable_block.rewards.unwrap_or_default(), - next_era_gas_price, - )), - (maybe_era_report, maybe_next_era_validator_weights) => { - if maybe_era_report.is_none() { - error!( - "era_end {}: maybe_era_report is none", - executable_block.era_id - ); - } - if maybe_next_era_validator_weights.is_none() { - error!( - "era_end {}: maybe_next_era_validator_weights is none", - executable_block.era_id - ); - } - return Err(BlockExecutionError::FailedToCreateEraEnd { - maybe_era_report, - maybe_next_era_validator_weights, - }); - } - }; - - let block = Arc::new(BlockV2::new( - parent_hash, - parent_seed, - state_root_hash, - executable_block.random_bit, - era_end, - executable_block.timestamp, - executable_block.era_id, - block_height, - protocol_version, - (*proposer).clone(), - executable_block.transaction_map, - executable_block.rewarded_signatures, - current_gas_price, - last_switch_block_hash, - )); - - let proof_of_checksum_registry = match data_access_layer.tracking_copy(state_root_hash)? { - Some(tc) => match tc.reader().read_with_proof(&Key::ChecksumRegistry)? { + let merkle_proof = match data_access_layer + .tracking_copy(exec_ctx.state_root_hash()) + .map_err(BlockExecutionError::Lmdb)? + { + Some(tc) => match tc + .reader() + .read_with_proof(&Key::ChecksumRegistry) + .map_err(BlockExecutionError::Lmdb)? + { Some(proof) => proof, None => return Err(BlockExecutionError::MissingChecksumRegistry), }, - None => return Err(BlockExecutionError::RootNotFound(state_root_hash)), + None => return Err(exec_ctx.root_not_found()), }; - let approvals_hashes = Box::new(ApprovalsHashes::new( - *block.hash(), - transaction_approvals_hashes, - proof_of_checksum_registry, - )); - - // processing is finished now if let Some(metrics) = metrics.as_ref() { metrics .exec_block_post_processing - .observe(post_processing_start.elapsed().as_secs_f64()); - metrics - .exec_block_total - .observe(start.elapsed().as_secs_f64()); + .observe(exec_ctx.post_process_elapsed()); } - Ok(BlockAndExecutionArtifacts { - block, - approvals_hashes, - execution_artifacts: artifacts, - step_outcome, - }) + let outcome = exec_ctx.into_outcome(merkle_proof); + + if let Some(metrics) = metrics.as_ref() { + metrics.exec_block_total.observe(outcome.total_elapsed()); + } + + match outcome { + ExecuteBlockOutcome::FailedToCreateEraEnd { + err_msg, + maybe_era_report, + maybe_next_era_validator_weights, + .. + } => { + error!("{}", err_msg); + Err(BlockExecutionError::FailedToCreateEraEnd { + maybe_era_report, + maybe_next_era_validator_weights, + }) + } + ExecuteBlockOutcome::Success { ret, .. } => Ok(ret), + } } /// Execute the transaction without committing the effects. @@ -1991,12 +877,9 @@ pub(super) fn speculatively_execute( where S: StateProvider, { - let transaction_config = &chainspec.transaction_config; - let maybe_transaction = MetaTransaction::from_transaction( - &input_transaction, - chainspec.core_config.pricing_handling, - transaction_config, - ); + let gas_price = 1; // run speculative with flat cost + let maybe_transaction = + MetaTransaction::new_from_txn_with_price(&input_transaction, chainspec, gas_price); if let Err(error) = maybe_transaction { return SpeculativeExecutionResult::invalid_transaction(error); } @@ -2129,12 +1012,12 @@ where { if !chainspec.evm_config.enabled { return SpeculativeExecutionResult::invalid_transaction(InvalidTransaction::Evm( - casper_types::EvmTransactionError::Disabled, + EvmTransactionError::Disabled, )); } if evm_transaction.gas_limit() > chainspec.evm_config.block_gas_limit { return SpeculativeExecutionResult::invalid_transaction(InvalidTransaction::Evm( - casper_types::EvmTransactionError::GasLimitExceedsBlockGasLimit { + EvmTransactionError::GasLimitExceedsBlockGasLimit { gas_limit: evm_transaction.gas_limit(), block_gas_limit: chainspec.evm_config.block_gas_limit, }, @@ -2146,14 +1029,12 @@ where Ok(Some(tracking_copy)) => tracking_copy, Ok(None) => { return SpeculativeExecutionResult::invalid_transaction(InvalidTransaction::Evm( - casper_types::EvmTransactionError::Decode(format!( - "state root {state_root_hash} not found" - )), + EvmTransactionError::Decode(format!("state root {state_root_hash} not found")), )) } Err(error) => { return SpeculativeExecutionResult::invalid_transaction(InvalidTransaction::Evm( - casper_types::EvmTransactionError::Decode(format!( + EvmTransactionError::Decode(format!( "failed to check out EVM speculative execution state: {error}" )), )) @@ -2162,13 +1043,16 @@ where let block_time = block_header .timestamp() .saturating_add(chainspec.core_config.minimum_block_time); - let base_fee_wei = chainspec.evm_config.base_fee_wei(); - let block_context = EvmBlockContext { + + let base_fee = u128::from(chainspec.evm_config.base_fee); + let wei_per_mote = u128::from(chainspec.evm_config.wei_per_mote); + let base_fee_wei = base_fee * wei_per_mote; + let block_context = EvmV1Request { number: block_header.height(), timestamp: block_time.millis() / 1000, beneficiary: EvmAddress::ZERO, - gas_limit: Some(chainspec.evm_config.block_gas_limit), - base_fee: Some(base_fee_wei), + block_gas_limit: chainspec.evm_config.block_gas_limit, + base_fee_wei, }; let kind = if evm_transaction.is_unsigned_call() { EvmExecuteKind::Call(EvmExecutorCallRequest { @@ -2188,7 +1072,7 @@ where block: block_context, kind, }; - let block_hash_provider = StaticEvmBlockHashProvider { block_hashes }; + let block_hash_provider = StaticEvmBlockHashProvider::new(block_hashes); let outcome = match EvmExecutor::new(chainspec.evm_config).execute_with_block_hash_provider( &mut tracking_copy, execute_request, @@ -2197,7 +1081,7 @@ where Ok(outcome) => outcome, Err(error) => { return SpeculativeExecutionResult::invalid_transaction(InvalidTransaction::Evm( - casper_types::EvmTransactionError::Decode(error.to_string()), + EvmTransactionError::Decode(error.to_string()), )) } }; @@ -2205,95 +1089,21 @@ where let effective_gas_price = if evm_transaction.is_unsigned_call() { base_fee_wei } else { - evm_transaction.effective_gas_price(base_fee_wei) - }; - let receipt = outcome.to_receipt(effective_gas_price); - let error = receipt.status.message().map(str::to_string); - SpeculativeExecutionResult::Evm(Box::new( - casper_binary_port::EvmSpeculativeExecutionResult::new( - block_header.block_hash(), - Gas::new(evm_transaction.gas_limit()), - Gas::new(outcome.gas_used), - effects, - error, - receipt, - Bytes::from(outcome.output), - ), - )) -} - -fn invoked_contract_will_pay( - state_provider: &ScratchGlobalState, - state_root_hash: Digest, - transaction: &MetaTransaction, -) -> Result, StateResultError> { - let (hash_addr, entry_point_name) = match transaction.contract_direct_address() { - None => { - return Err(StateResultError::ValueNotFound( - "contract direct address not found".to_string(), - )) - } - Some((hash_addr, entry_point_name)) => (hash_addr, entry_point_name), - }; - let entity_addr = EntityAddr::new_smart_contract(hash_addr); - let entry_point_request = EntryPointRequest::new(state_root_hash, entry_point_name, hash_addr); - let entry_point_response = state_provider.entry_point(entry_point_request); - match entry_point_response { - EntryPointResult::RootNotFound => Err(StateResultError::RootNotFound), - EntryPointResult::ValueNotFound(msg) => Err(StateResultError::ValueNotFound(msg)), - EntryPointResult::Failure(tce) => Err(StateResultError::Failure(tce)), - EntryPointResult::Success { entry_point } => { - if entry_point.will_pay_direct_invocation() { - Ok(Some(entity_addr)) - } else { - Ok(None) - } - } - } -} - -#[allow(clippy::too_many_arguments)] -fn commit_step( - native_runtime_config: NativeRuntimeConfig, - scratch_state: &ScratchGlobalState, - maybe_metrics: Option>, - protocol_version: ProtocolVersion, - state_hash: Digest, - InternalEraReport { - equivocators, - inactive_validators, - }: InternalEraReport, - era_end_timestamp_millis: u64, - next_era_id: EraId, -) -> StepResult { - // Both inactive validators and equivocators are evicted - let evict_items = inactive_validators - .into_iter() - .chain(equivocators) - .map(EvictItem::new) - .collect(); - - let step_request = StepRequest::new( - native_runtime_config, - state_hash, - protocol_version, - vec![], // <-- casper mainnet currently does not slash - evict_items, - next_era_id, - era_end_timestamp_millis, - ); - - // Commit the step. - let start = Instant::now(); - let result = scratch_state.step(step_request); - debug_assert!(result.is_success(), "{:?}", result); - if let Some(metrics) = maybe_metrics { - let elapsed = start.elapsed().as_secs_f64(); - metrics.commit_step.observe(elapsed); - metrics.latest_commit_step.set(elapsed); - } - trace!(?result, "step response"); - result + evm_transaction.effective_gas_price(base_fee_wei) + }; + let receipt = outcome.to_receipt(effective_gas_price); + let error = receipt.status.message().map(str::to_string); + SpeculativeExecutionResult::Evm(Box::new( + casper_binary_port::EvmSpeculativeExecutionResult::new( + block_header.block_hash(), + Gas::new(evm_transaction.gas_limit()), + Gas::new(outcome.gas_used), + effects, + error, + receipt, + Bytes::from(outcome.output), + ), + )) } /// Computes the checksum of the given set of execution results. @@ -2341,73 +1151,486 @@ pub(crate) fn compute_execution_results_checksum<'a>( }) } -#[cfg(test)] -mod tests { - use super::*; - use casper_storage::{global_state::state, tracking_copy::TrackingCopyExt}; - use casper_types::{EvmConfig, DEFAULT_WEI_PER_MOTE}; - - #[test] - fn should_not_raise_evm_min_cost_above_converted_fee() { - let gas_limit = Gas::new(21_000); - let cost = U512::from(1); - let baseline_motes_amount = U512::from(1_000_000); - - assert_eq!( - execution_min_cost(true, gas_limit, cost, baseline_motes_amount), - cost - ); +// *************** EVM RELATED ***************** + +fn account_main_purse( + tracking_copy: &mut TrackingCopy, + protocol_version: ProtocolVersion, + account_hash: AccountHash, +) -> Result, BlockExecutionError> +where + R: StateReader, +{ + // Runtime footprints cover both legacy `StoredValue::Account` accounts and + // addressable-entity-backed accounts, so this is the authoritative account + // existence check for identity linking. + match tracking_copy.runtime_footprint_by_account_hash(protocol_version, account_hash) { + Ok((_, entity)) => entity + .main_purse() + .map(Some) + .ok_or_else(|| BlockExecutionError::PaymentError("missing account main purse".into())), + Err(TrackingCopyError::KeyNotFound(_)) => Ok(None), + Err(error) => Err(BlockExecutionError::PaymentError(error.to_string())), } +} - #[test] - fn should_keep_native_min_cost_based_on_gas_limit() { - let gas_limit = Gas::new(21_000); - let cost = U512::from(1); - let baseline_motes_amount = U512::from(1_000_000); +fn invoked_contract_will_pay( + state_provider: &ScratchGlobalState, + state_root_hash: Digest, + contract_address: Option<(HashAddr, String)>, +) -> Result, StateResultError> { + if let Some((hash_addr, entry_point_name)) = contract_address { + let entity_addr = EntityAddr::new_smart_contract(hash_addr); + let entry_point_request = + EntryPointRequest::new(state_root_hash, entry_point_name, hash_addr); + let entry_point_response = state_provider.entry_point(entry_point_request); + match entry_point_response { + EntryPointResult::RootNotFound => Err(StateResultError::RootNotFound), + EntryPointResult::ValueNotFound(msg) => Err(StateResultError::ValueNotFound(msg)), + EntryPointResult::Failure(tce) => Err(StateResultError::Failure(tce)), + EntryPointResult::Success { entry_point } => { + if entry_point.will_pay_direct_invocation() { + Ok(Some(entity_addr)) + } else { + Ok(None) + } + } + } + } else { + Ok(None) + } +} - assert_eq!( - execution_min_cost(false, gas_limit, cost, baseline_motes_amount), - U512::from(21_000) - ); +fn txn_initial_balance_identifier( + state_provider: &ScratchGlobalState, + exec_ctx: &ExecuteBlockContext, + txn_ctx: &TransactionProcessContext, +) -> Result { + let state_root_hash = exec_ctx.state_root_hash(); + let protocol_version = exec_ctx.protocol_version(); + let addressable_entity_enabled = exec_ctx.addressable_entity_enabled(); + + let transaction_hash = txn_ctx.transaction_hash(); + let initiator_addr = txn_ctx.initiator_addr().clone(); + + match txn_ctx.balance_identifier_resolution() { + Ok(resolution) => { + match resolution { + BalanceIdentifierResolution::Identifier(bi) => { + Ok(InitialBalanceIdentifierResult::Identifier(bi)) + } + BalanceIdentifierResolution::CheckContractPay(default_bi) => { + if !addressable_entity_enabled { + // the initiating account pays using its main purse + trace!(%transaction_hash, "account session"); + Ok(InitialBalanceIdentifierResult::Identifier(default_bi)) + } else { + match invoked_contract_will_pay( + state_provider, + state_root_hash, + txn_ctx.contract_direct_address(), + ) { + Ok(Some(entity_addr)) => { + let entity_bi = BalanceIdentifier::Entity(entity_addr); + Ok(InitialBalanceIdentifierResult::Identifier(entity_bi)) + } + Ok(None) => { + // the initiating account pays using its main purse + trace!(%transaction_hash, "direct invocation with account payment"); + Ok(InitialBalanceIdentifierResult::Identifier(default_bi)) + } + Err(state_err) => { + trace!(%transaction_hash, "failed to resolve contract self payment"); + let penalized_bi = BalanceIdentifier::PenalizedAccount( + initiator_addr + .account_hash() + .ok_or(BlockExecutionError::InvalidTransactionVariant)?, + ); + Ok(InitialBalanceIdentifierResult::IdentifierAndStateError( + penalized_bi, + state_err, + )) + } + } + } + } + BalanceIdentifierResolution::CheckEvmAccount(_initiator_bi) => { + let signer = match txn_ctx.evm_signer() { + Some(Ok(signer)) => signer, + Some(Err(evm_err)) => { + trace!(%transaction_hash, "failed to resolve evm transaction identifier"); + let penalized_bi = BalanceIdentifier::PenalizedAccount( + initiator_addr + .account_hash() + .ok_or(BlockExecutionError::InvalidTransactionVariant)?, + ); + return Ok(InitialBalanceIdentifierResult::IdentifierAndEvmError( + penalized_bi, + evm_err, + )); + } + None => { + trace!(%transaction_hash, "failed to resolve evm transaction identifier"); + return Err(BlockExecutionError::TransactionConversion( + "non evm transaction flagged as evm".to_string(), + )); + } + }; + + let evm_address = match txn_ctx.evm_address() { + Some(addr) => addr, + None => { + trace!(%transaction_hash, "failed to resolve evm transaction identifier"); + return Err(BlockExecutionError::TransactionConversion( + "evm transaction missing its address".to_string(), + )); + } + }; + + match resolve_evm_origin( + state_provider, + state_root_hash, + protocol_version, + signer, + evm_address, + ) { + Ok(resolution) => { + let bi = resolution.balance_identifier(); + Ok(InitialBalanceIdentifierResult::IdentifierAndEvmResolution( + bi, resolution, + )) + } + Err(err) => { + trace!(%transaction_hash, "failed to resolve evm origin"); + Err(err) + } + } + } + } + } + Err(err) => { + trace!(%transaction_hash, "failed to determine payer_balance_identifier"); + error!( + %transaction_hash, + ?err, + "failed to determine payer_balance_identifier" + ); + Ok(InitialBalanceIdentifierResult::Unknown) + } } +} - #[test] - fn eip4788_hook_writes_beacon_roots_without_transactions() { - let chainspec = Chainspec { - evm_config: EvmConfig { - enabled: true, - chain_id: 7, - spec: EvmSpec::Prague, - block_gas_limit: 30_000_000, - base_fee: 0, - wei_per_mote: DEFAULT_WEI_PER_MOTE, - }, - ..Default::default() - }; - let (global_state, state_root_hash, _tempdir) = - state::lmdb::make_temporary_global_state([]); - let scratch_state = global_state.create_scratch(); - let block_time = BlockTime::new(2_000); - let block_context = evm_block_context(&chainspec, 1, block_time, &PublicKey::System); - let parent_hash = BlockHash::new(Digest::from_raw([0x44; 32])); - - let updated_state_root_hash = write_eip4788_beacon_roots( - &scratch_state, - state_root_hash, - &chainspec, - ProtocolVersion::V1_0_0, - block_context.clone(), - parent_hash, - ) - .expect("EIP-4788 hook should succeed"); - let tracking_copy = scratch_state - .tracking_copy(updated_state_root_hash) - .expect("tracking copy should not fail") - .expect("state root should exist"); - let entry = tracking_copy - .get_eip4788_parent_hash(block_context.timestamp) - .expect("EIP-4788 beacon root should be readable"); - - assert_eq!(entry, Some((block_context.timestamp, parent_hash))); +/// Resolves the payer and any deferred identity write for a signed EVM transaction. +/// +/// This function only reads state. That matters because it runs before payment +/// preconditions are known to pass. If execution is later allowed, the returned +/// [`EvmIdentityInstruction`] is applied in the tracking copy used for EVM execution. +fn resolve_evm_origin( + scratch_state: &ScratchGlobalState, + state_root_hash: Digest, + protocol_version: ProtocolVersion, + signer: &PublicKey, + address: EvmAddress, +) -> Result { + // The signer gives us a Casper `AccountHash` preimage from the secp256k1 + // public key. That account hash is not derivable from the 20-byte EVM + // address alone, so identity linking must happen while the signed + // transaction is available. + // let signer = transaction + // .signer() + // .map_err(|error| BlockExecutionError::TransactionConversion(error.to_string()))?; + let account_hash = signer.to_account_hash(); + // Native EVM identities use a deterministic purse derived from the EVM + // address. Linked Casper accounts use the account's existing main purse + // instead, so the same key pair can spend the same funds from Casper and + // Ethereum-style transaction paths. + //let address = transaction.from(); + let deterministic_purse = casper_types::evm::deterministic_purse(address); + let mut tracking_copy = match scratch_state.tracking_copy(state_root_hash) { + Ok(Some(tc)) => tc, + Ok(None) => return Err(BlockExecutionError::RootNotFound(state_root_hash)), + Err(gse) => return Err(BlockExecutionError::Lmdb(gse)), + }; + + // `EvmAddr::Account` is now only an identity pointer. It is either + // `Key::Account` for a linked Casper account or `Key::URef` for an + // EVM-native purse identity. + let identity_key = Key::Evm(casper_types::EvmAddr::Account(address)); + match tracking_copy + .read(&identity_key) + .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? + { + Some(StoredValue::CLValue(cl_value)) => { + let key = cl_value + .into_t::() + .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; + match key { + // Existing bridge records are authoritative. Once an EVM + // address is linked, the payer is the linked Casper account's + // main purse. + Key::Account(account_hash) => Ok(EvmOriginResolution::new( + BalanceIdentifier::Account(account_hash), + EvmIdentityInstruction::None, + )), + // Existing EVM-native identities keep paying from their stored + // purse. We may still plan an upgrade to a Casper link, but + // only when doing so cannot steal a contract identity or move + // balances between distinct purses. + Key::URef(purse) => { + let eve_identity_instruction = resolve_evm_native_identity_plan( + &mut tracking_copy, + protocol_version, + address, + account_hash, + purse, + deterministic_purse, + )?; + Ok(EvmOriginResolution::new( + BalanceIdentifier::Purse(purse), + eve_identity_instruction, + )) + } + other => Err(BlockExecutionError::PaymentError(format!( + "invalid EVM account identity key: {other}" + ))), + } + } + Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( + "unexpected stored value for {identity_key}: expected StoredValue::CLValue(Key), found {}", + stored_value.type_name() + ))), + None => { + // No identity pointer plus non-empty EVM code means this address is + // already a contract/runtime-created EVM account. Contracts do not + // have a signing key, so they must remain EVM-native. + if evm_account_has_code(&mut tracking_copy, address)? { + return Ok(EvmOriginResolution::new( + BalanceIdentifier::Purse(deterministic_purse), + EvmIdentityInstruction::None, + )); + } + match account_main_purse(&mut tracking_copy, protocol_version, account_hash)? { + // A Casper account exists for the recovered signer, but the EVM + // address has not been seen before. Use the account for payment + // immediately and write the bridge only if execution proceeds. + Some(_) => Ok(EvmOriginResolution::new( + BalanceIdentifier::Account(account_hash), + EvmIdentityInstruction::LinkExisting { + address, + account_hash, + }, + )), + // First use of this signing pair on both sides. Runtime will + // create a Casper account whose main purse is the deterministic + // EVM purse, then write the bridge record. + None => Ok(EvmOriginResolution::new( + BalanceIdentifier::Purse(deterministic_purse), + EvmIdentityInstruction::CreateAccount { + address, + account_hash, + main_purse: deterministic_purse, + }, + )), + } + } + } +} + +fn resolve_evm_native_identity_plan( + tracking_copy: &mut TrackingCopy, + protocol_version: ProtocolVersion, + address: EvmAddress, + account_hash: AccountHash, + purse: casper_types::URef, + deterministic_purse: casper_types::URef, +) -> Result +where + R: StateReader, +{ + // Do not overwrite contract identities, and do not turn an arbitrary purse + // identity into a Casper account link. The only EVM-native identity that is + // safe to link is the deterministic purse for this address. + if evm_account_has_code(tracking_copy, address)? || purse.addr() != deterministic_purse.addr() { + return Ok(EvmIdentityInstruction::None); + } + + match account_main_purse(tracking_copy, protocol_version, account_hash)? { + // If the recovered Casper account already uses the same deterministic + // purse, replacing the pointer with `Key::Account` preserves the balance + // location and lets Casper-native flows see the account identity. + Some(main_purse) if main_purse.addr() == purse.addr() => { + Ok(EvmIdentityInstruction::LinkExisting { + address, + account_hash, + }) + } + // A Casper account exists, but its main purse differs from the existing + // EVM-native purse. Keep the EVM-native identity to avoid moving funds + // or changing ownership semantics behind the user's back. + Some(_) => Ok(EvmIdentityInstruction::None), + // No Casper account exists yet, so creating one backed by the existing + // deterministic purse preserves balances while giving the signer a + // Casper account identity. + None => Ok(EvmIdentityInstruction::CreateAccount { + address, + account_hash, + main_purse: purse, + }), + } +} + +fn evm_account_has_code( + tracking_copy: &mut TrackingCopy, + address: EvmAddress, +) -> Result +where + R: StateReader, +{ + // Code hash is the cheap contract/EOA discriminator for an EVM address. A + // non-empty code hash means the address is not a user-controlled signing + // identity, so runtime must not create or link a Casper account for it. + let key = Key::Evm(casper_types::EvmAddr::CodeHash(address)); + match tracking_copy + .read(&key) + .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? + { + Some(StoredValue::CLValue(cl_value)) => { + let code_hash = cl_value + .into_t::() + .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; + Ok(code_hash != casper_types::evm::EMPTY_CODE_HASH) + } + Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( + "unexpected stored value for {key}: expected StoredValue::CLValue(evm::Hash), found {}", + stored_value.type_name() + ))), + None => Ok(false), } } + +// +// fn evm_account_has_nonce( +// tracking_copy: &mut TrackingCopy, +// address: EvmAddress, +// ) -> Result +// where +// R: StateReader, +// { +// let key = Key::Evm(casper_types::EvmAddr::Nonce(address)); +// match tracking_copy +// .read(&key) +// .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? +// { +// Some(StoredValue::CLValue(cl_value)) => { +// let _nonce = cl_value +// .into_t::() +// .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; +// Ok(true) +// } +// Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( +// "unexpected stored value for {key}: expected StoredValue::CLValue(u64), found {}", +// stored_value.type_name() +// ))), +// None => Ok(false), +// } +// } + +// +// fn apply_evm_proposer_identity( +// tracking_copy: &mut TrackingCopy, +// protocol_version: ProtocolVersion, +// proposer: &PublicKey, +// ) -> Result<(), BlockExecutionError> +// where +// R: StateReader, +// { +// let address = EvmAddress::from_block_proposer_public_key(proposer); +// let account_hash = proposer.to_account_hash(); +// if account_main_purse(tracking_copy, protocol_version, account_hash)?.is_none() { +// return Ok(()); +// } +// +// let identity_key = Key::Evm(casper_types::EvmAddr::Account(address)); +// match tracking_copy +// .read(&identity_key) +// .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))? +// { +// Some(StoredValue::CLValue(cl_value)) => { +// let identity = cl_value +// .into_t::() +// .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; +// match identity { +// Key::Account(_) | Key::URef(_) => Ok(()), +// other => Err(BlockExecutionError::PaymentError(format!( +// "invalid EVM account identity key: {other}" +// ))), +// } +// } +// Some(stored_value) => Err(BlockExecutionError::PaymentError(format!( +// "unexpected stored value for {identity_key}: expected StoredValue::CLValue(Key), +// found {}", stored_value.type_name() +// ))), +// None => { +// if evm_account_has_code(tracking_copy, address)? +// || evm_account_has_nonce(tracking_copy, address)? +// { +// return Ok(()); +// } +// write_evm_identity(tracking_copy, address, Key::Account(account_hash)) +// } +// } +// } +// +// fn apply_evm_identity_plan( +// tracking_copy: &mut TrackingCopy, +// protocol_version: ProtocolVersion, +// plan: EvmIdentityInstruction, +// ) -> Result<(), BlockExecutionError> +// where +// R: StateReader, +// { +// // Identity writes are intentionally delayed until after payment +// // preconditions pass. They are applied to the same tracking copy as EVM +// // execution so the identity record and nonce/code/storage updates commit or +// // discard together. +// match plan { +// EvmIdentityInstruction::None => Ok(()), +// EvmIdentityInstruction::LinkExisting { +// address, +// account_hash, +// } => write_evm_identity(tracking_copy, address, Key::Account(account_hash)), +// EvmIdentityInstruction::CreateAccount { +// address, +// account_hash, +// main_purse, +// } => { +// // Another transaction in the same block may have already created +// // the account through this scratch state. Avoid recreating it, but +// // still write the EVM identity pointer below. +// if account_main_purse(tracking_copy, protocol_version, account_hash)?.is_none() { +// let account = Account::create(account_hash, NamedKeys::new(), main_purse); +// tracking_copy +// .create_addressable_entity_from_account(account, protocol_version) +// .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; +// } +// write_evm_identity(tracking_copy, address, Key::Account(account_hash)) +// } +// } +// } +// +// fn write_evm_identity( +// tracking_copy: &mut TrackingCopy, +// address: EvmAddress, +// identity: Key, +// ) -> Result<(), BlockExecutionError> +// where +// R: StateReader, +// { +// // Keep the bridge record minimal: a CLValue containing the identity `Key`. +// // Nonce, code hash, bytecode, and storage live under their own EVM keys. +// let key = Key::Evm(casper_types::EvmAddr::Account(address)); +// let cl_value = CLValue::from_t(identity) +// .map_err(|error| BlockExecutionError::PaymentError(error.to_string()))?; +// tracking_copy.write(key, StoredValue::CLValue(cl_value)); +// Ok(()) +// } diff --git a/node/src/components/contract_runtime/types.rs b/node/src/components/contract_runtime/types.rs index b53d0c9d40..7271526667 100644 --- a/node/src/components/contract_runtime/types.rs +++ b/node/src/components/contract_runtime/types.rs @@ -1,716 +1,30 @@ -use std::{collections::BTreeMap, sync::Arc}; - -use crate::{contract_runtime::StateResultError, types::TransactionHeader}; -use casper_types::{evm, EvmTransactionError, InitiatorAddr, Transfer}; -use datasize::DataSize; -use serde::Serialize; - -use casper_execution_engine::engine_state::{ - Error, InvalidRequest as InvalidWasmV1Request, WasmV1Result, -}; -use casper_storage::{ - block_store::types::ApprovalsHashes, - data_access_layer::{ - auction::AuctionMethodError, mint::BurnResult, BalanceHoldResult, BalanceResult, - BiddingResult, EraValidatorsRequest, HandleFeeResult, HandleRefundResult, TransferResult, +mod block_and_execution_artifacts; +mod era_price; +mod evm_origin_resolution; +mod execute_block_context; +mod execution_artifact; +mod execution_pre_state; +mod limits_and_costs; +mod process_request; +mod speculative_execution_result; +mod static_evm_block_hash_provider; +mod step_outcome; +mod transaction_process_context; +mod validator_weights_by_era_id_request; + +pub(crate) use crate::components::contract_runtime::types::{ + block_and_execution_artifacts::BlockAndExecutionArtifacts, + era_price::EraPrice, + evm_origin_resolution::EvmOriginResolution, + execute_block_context::{ExecuteBlockContext, ExecuteBlockContextError, ExecuteBlockOutcome}, + execution_artifact::ExecutionArtifact, + execution_pre_state::ExecutionPreState, + limits_and_costs::LimitsAndCosts, + process_request::ProcessRequest, + speculative_execution_result::SpeculativeExecutionResult, + static_evm_block_hash_provider::StaticEvmBlockHashProvider, + step_outcome::StepOutcome, + transaction_process_context::{ + BalanceIdentifierResolution, InitialBalanceIdentifierResult, TransactionProcessContext, }, }; -use casper_types::{ - contract_messages::Messages, - execution::{Effects, EvmExecutionResult, ExecutionResult, ExecutionResultV2}, - BlockHash, BlockHeaderV2, BlockV2, Digest, EraId, Gas, InvalidDeploy, InvalidTransaction, - InvalidTransactionV1, ProtocolVersion, PublicKey, Transaction, TransactionHash, U512, -}; - -use self::wasm_v2_request::{WasmV2Error, WasmV2Result}; - -use super::operations::wasm_v2_request; - -/// Request for validator weights for a specific era. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ValidatorWeightsByEraIdRequest { - state_hash: Digest, - era_id: EraId, - protocol_version: ProtocolVersion, -} - -impl ValidatorWeightsByEraIdRequest { - /// Constructs a new ValidatorWeightsByEraIdRequest. - pub fn new(state_hash: Digest, era_id: EraId, protocol_version: ProtocolVersion) -> Self { - ValidatorWeightsByEraIdRequest { - state_hash, - era_id, - protocol_version, - } - } - - /// Get the state hash. - pub fn state_hash(&self) -> Digest { - self.state_hash - } - - /// Get the era id. - pub fn era_id(&self) -> EraId { - self.era_id - } - - /// Get the protocol version. - pub fn protocol_version(&self) -> ProtocolVersion { - self.protocol_version - } -} - -impl From for EraValidatorsRequest { - fn from(input: ValidatorWeightsByEraIdRequest) -> Self { - EraValidatorsRequest::new(input.state_hash) - } -} - -#[derive(Clone, Debug)] -pub(crate) struct ExecutionArtifactBuilder { - effects: Effects, - hash: TransactionHash, - header: TransactionHeader, - error_message: Option, - messages: Messages, - transfers: Vec, - initiator: InitiatorAddr, - current_price: u8, - cost: U512, - limit: Gas, - consumed: Gas, - refund: U512, - size_estimate: u64, - min_cost: U512, - available: Option, - evm_receipt: Option, -} - -impl ExecutionArtifactBuilder { - pub fn new( - transaction: &Transaction, - limit: Gas, - current_price: u8, - initial_cost: U512, - min_cost: U512, - ) -> Self { - ExecutionArtifactBuilder { - effects: Effects::new(), - hash: transaction.hash(), - header: transaction.into(), - error_message: None, - transfers: vec![], - messages: Default::default(), - initiator: transaction.initiator_addr(), - current_price, - cost: initial_cost, - limit, - consumed: Gas::zero(), - refund: U512::zero(), - size_estimate: transaction.size_estimate() as u64, - min_cost, - available: None, - evm_receipt: None, - } - } - - pub fn pre_condition_failure( - transaction: &Transaction, - current_price: u8, - invalid_transaction: InvalidTransaction, - ) -> Self { - ExecutionArtifactBuilder { - effects: Effects::new(), - hash: transaction.hash(), - header: transaction.into(), - error_message: Some(format!("{}", invalid_transaction)), - transfers: vec![], - messages: Default::default(), - initiator: transaction.initiator_addr(), - current_price, - cost: U512::zero(), - limit: Gas::zero(), - consumed: Gas::zero(), - refund: U512::zero(), - size_estimate: transaction.size_estimate() as u64, - min_cost: U512::zero(), - available: None, - evm_receipt: None, - } - } - - pub fn error_message(&self) -> Option { - self.error_message.clone() - } - - pub fn gas_limit(&self) -> Gas { - self.limit - } - - pub fn limit(&self) -> U512 { - self.limit.value() - } - - pub fn consumed(&self) -> U512 { - self.consumed.value() - } - - pub fn available(&self) -> Option { - self.available - } - - pub fn actual_cost(&self) -> U512 { - self.cost - } - - pub fn cost_to_use(&self) -> U512 { - // to prevent do-nothing exhaustion and other 0 cost scenarios, - // we raise cost to min_cost if less than that - - let cost = { - let cost = self.cost; - if cost < self.min_cost { - self.min_cost - } else { - cost - } - }; - - match self.available { - Some(available) => { - if available < self.cost { - available - } else { - cost - } - } - None => cost, - } - } - - pub fn consume_limit(&mut self) -> &mut Self { - self.consumed = self.consumed.saturating_add(self.limit); - self - } - - pub fn with_added_consumed(&mut self, consumed: Gas) -> &mut Self { - self.consumed = self.consumed.saturating_add(consumed); - self - } - - pub fn with_appended_transfers(&mut self, transfers: &mut Vec) -> &mut Self { - self.transfers.append(transfers); - self - } - - pub fn with_appended_effects(&mut self, effects: Effects) -> &mut Self { - self.effects.append(effects); - self - } - - pub fn with_appended_messages(&mut self, messages: &mut Messages) -> &mut Self { - self.messages.append(messages); - self - } - - pub fn with_state_result_error(&mut self, error: StateResultError) -> Result<&mut Self, ()> { - if let StateResultError::RootNotFound = error { - return Err(()); - } - if self.error_message.is_none() { - self.error_message = Some(format!("{:?}", error)); - } - Ok(self) - } - - pub fn with_initial_balance_result( - &mut self, - balance_result: BalanceResult, - minimum_amount: U512, - ) -> Result<&mut Self, bool> { - if let BalanceResult::RootNotFound = balance_result { - return Err(true); - } - if let (None, Some(err)) = (&self.error_message, balance_result.error()) { - self.error_message = Some(format!("{}", err)); - return Err(false); - } - if let Some(purse) = balance_result.purse_addr() { - let is_sufficient = balance_result.is_sufficient(minimum_amount); - if !is_sufficient { - self.error_message = Some(format!( - "Purse {} has less than {}", - base16::encode_lower(&purse), - minimum_amount - )); - return Ok(self); - } - } - Ok(self) - } - - pub fn with_wasm_v1_result(&mut self, wasm_v1_result: WasmV1Result) -> Result<&mut Self, ()> { - if let Some(Error::RootNotFound(_)) = wasm_v1_result.error() { - return Err(()); - } - self.with_added_consumed(wasm_v1_result.consumed()); - - if let Some(err) = wasm_v1_result.error() { - self.error_message = Some(format!("{}", err)); - } else if wasm_v1_result.consumed() == Gas::zero() { - self.error_message = Some("Wasm consumed 0 gas".to_string()); - } - - if self.error_message.is_some() { - return Ok(self); - } - - self.with_appended_transfers(&mut wasm_v1_result.transfers().clone()) - .with_appended_messages(&mut wasm_v1_result.messages().clone()) - .with_appended_effects(wasm_v1_result.effects().clone()); - Ok(self) - } - - pub fn with_error_message(&mut self, error_message: String) -> &mut Self { - self.error_message = Some(error_message); - self - } - - pub fn with_set_refund_purse_result( - &mut self, - handle_refund_result: &HandleRefundResult, - ) -> Result<&mut Self, bool> { - if let HandleRefundResult::RootNotFound = handle_refund_result { - return Err(true); - } - if let HandleRefundResult::Success { - effects, transfers, .. - } = handle_refund_result - { - self.with_appended_transfers(&mut transfers.clone()) - .with_appended_effects(effects.clone()); - } - if let (None, HandleRefundResult::Failure(_)) = (&self.error_message, handle_refund_result) - { - self.error_message = handle_refund_result.error_message(); - return Err(false); - } - Ok(self) - } - - pub fn with_clear_refund_purse_result( - &mut self, - handle_refund_result: &HandleRefundResult, - ) -> Result<&mut Self, bool> { - if let HandleRefundResult::RootNotFound = handle_refund_result { - return Err(true); - } - if let HandleRefundResult::Success { - effects, transfers, .. - } = handle_refund_result - { - self.with_appended_transfers(&mut transfers.clone()) - .with_appended_effects(effects.clone()); - } - if let (None, HandleRefundResult::Failure(_)) = (&self.error_message, handle_refund_result) - { - self.error_message = handle_refund_result.error_message(); - return Err(false); - } - Ok(self) - } - - pub fn with_handle_refund_result( - &mut self, - handle_refund_result: &HandleRefundResult, - ) -> Result<&mut Self, ()> { - if let HandleRefundResult::RootNotFound = handle_refund_result { - return Err(()); - } - if let HandleRefundResult::Success { - effects, transfers, .. - } = handle_refund_result - { - self.with_appended_transfers(&mut transfers.clone()) - .with_appended_effects(effects.clone()); - } - if let (None, HandleRefundResult::Failure(_)) = (&self.error_message, handle_refund_result) - { - self.error_message = handle_refund_result.error_message(); - return Ok(self); - } - Ok(self) - } - - pub fn with_handle_fee_result( - &mut self, - handle_fee_result: &HandleFeeResult, - ) -> Result<&mut Self, ()> { - if let HandleFeeResult::RootNotFound = handle_fee_result { - return Err(()); - } - if let HandleFeeResult::Success { - effects, transfers, .. - } = handle_fee_result - { - self.with_appended_transfers(&mut transfers.clone()) - .with_appended_effects(effects.clone()); - } - if let (None, HandleFeeResult::Failure(_)) = (&self.error_message, handle_fee_result) { - self.error_message = handle_fee_result.error_message(); - return Ok(self); - } - Ok(self) - } - - pub fn with_balance_hold_result( - &mut self, - hold_result: &BalanceHoldResult, - ) -> Result<&mut Self, ()> { - if let BalanceHoldResult::RootNotFound = hold_result { - return Err(()); - } - if let BalanceHoldResult::Success { effects, .. } = hold_result { - self.with_appended_effects(*effects.clone()); - } - if let (None, BalanceHoldResult::Failure(_)) = (&self.error_message, hold_result) { - self.error_message = hold_result.error_message(); - return Ok(self); - } - Ok(self) - } - - pub fn with_refund_amount(&mut self, refund: U512) -> &mut Self { - self.refund = refund; - self - } - - pub fn with_zero_cost(&mut self) -> &mut Self { - self.cost = U512::zero(); - self.min_cost = U512::zero(); - self - } - - pub fn with_evm_receipt( - &mut self, - receipt: evm::Receipt, - consumed: U512, - effects: Effects, - ) -> &mut Self { - self.evm_receipt = Some(receipt); - self.consumed = Gas::new(consumed); - self.with_appended_effects(effects); - self - } - - pub fn with_invalid_wasm_v1_request( - &mut self, - invalid_request: &InvalidWasmV1Request, - ) -> &mut Self { - if self.error_message.is_none() { - self.error_message = Some(format!("{}", invalid_request)); - } - self - } - - pub fn with_auction_method_error( - &mut self, - auction_method_error: &AuctionMethodError, - ) -> &mut Self { - if self.error_message.is_none() { - self.error_message = Some(format!("{}", auction_method_error)); - } - self - } - - pub fn with_transfer_result( - &mut self, - transfer_result: TransferResult, - ) -> Result<&mut Self, ()> { - if let TransferResult::RootNotFound = transfer_result { - return Err(()); - } - if let (None, TransferResult::Failure(err)) = (&self.error_message, &transfer_result) { - self.error_message = Some(format!("{}", err)); - } - if let TransferResult::Success { - effects, - transfers, - cache: _, - } = transfer_result - { - self.with_appended_transfers(&mut transfers.clone()) - .with_appended_effects(effects); - } - Ok(self) - } - - pub fn with_burn_result(&mut self, burn_result: BurnResult) -> Result<&mut Self, ()> { - if let BurnResult::RootNotFound = burn_result { - return Err(()); - } - if let (None, BurnResult::Failure(err)) = (&self.error_message, &burn_result) { - self.error_message = Some(format!("{}", err)); - } - if let BurnResult::Success { effects, cache: _ } = burn_result { - self.with_appended_effects(effects); - } - Ok(self) - } - - pub fn with_bidding_result(&mut self, bidding_result: BiddingResult) -> Result<&mut Self, ()> { - if let BiddingResult::RootNotFound = bidding_result { - return Err(()); - } - if let (None, BiddingResult::Failure(err)) = (&self.error_message, &bidding_result) { - self.error_message = Some(format!("{}", err)); - } - if let BiddingResult::Success { - effects, transfers, .. - } = bidding_result - { - self.with_appended_transfers(&mut transfers.clone()) - .with_appended_effects(effects); - } - Ok(self) - } - - #[allow(unused)] - pub fn with_initiator_addr(&mut self, initiator_addr: InitiatorAddr) -> &mut Self { - self.initiator = initiator_addr; - self - } - - pub fn with_available(&mut self, available: Option) -> &mut Self { - self.available = available; - self - } - - pub(crate) fn build(self) -> ExecutionArtifact { - let actual_cost = self.cost_to_use(); - let execution_result = if let Some(receipt) = self.evm_receipt { - let result = EvmExecutionResult { - initiator: self - .initiator - .evm_address() - .expect("EVM execution result requires an EVM initiator"), - current_price: self.current_price, - limit: self.limit, - cost: actual_cost, - refund: self.refund, - size_estimate: self.size_estimate, - effects: self.effects, - receipt, - }; - ExecutionResult::from(result) - } else { - let result = ExecutionResultV2 { - effects: self.effects, - transfers: self.transfers, - initiator: self.initiator, - refund: self.refund, - limit: self.limit, - consumed: self.consumed, - cost: actual_cost, - current_price: self.current_price, - size_estimate: self.size_estimate, - error_message: self.error_message, - }; - ExecutionResult::V2(Box::new(result)) - }; - ExecutionArtifact::new(self.hash, self.header, execution_result, self.messages) - } - - /// Adds the error message from a `InvalidRequest` to the artifact. - pub(crate) fn with_invalid_wasm_v2_request( - &mut self, - ire: wasm_v2_request::InvalidRequest, - ) -> &mut Self { - if self.error_message.is_none() { - self.error_message = Some(format!("{}", ire)); - } - self - } - - /// Adds the result from a `WasmV2Result` to the artifact. - pub(crate) fn with_wasm_v2_result(&mut self, result: WasmV2Result) -> &mut Self { - self.with_added_consumed(Gas::from(result.gas_usage().gas_spent())); - - // TODO: Use system message to notify about contract hash - - self.with_appended_effects(result.effects().clone()); - - self - } - - /// Adds the error message from a `WasmV2Error` to the artifact. - #[inline] - pub(crate) fn with_wasm_v2_error(&mut self, error: WasmV2Error) -> &mut Self { - self.with_error_message(error.to_string()); - self - } -} - -/// Effects from running step and the next era validators that are gathered when an era ends. -#[derive(Clone, Debug, DataSize)] -pub(crate) struct StepOutcome { - /// Validator sets for all upcoming eras that have already been determined. - pub(crate) upcoming_era_validators: BTreeMap>, - /// An [`Effects`] created by an era ending. - pub(crate) step_effects: Effects, -} - -#[derive(Clone, Debug, DataSize, PartialEq, Eq, Serialize)] -pub(crate) struct ExecutionArtifact { - pub(crate) transaction_hash: TransactionHash, - pub(crate) transaction_header: TransactionHeader, - pub(crate) execution_result: ExecutionResult, - pub(crate) messages: Messages, -} - -impl ExecutionArtifact { - pub(crate) fn new( - transaction_hash: TransactionHash, - transaction_header: TransactionHeader, - execution_result: ExecutionResult, - messages: Messages, - ) -> Self { - Self { - transaction_hash, - transaction_header, - execution_result, - messages, - } - } -} - -#[doc(hidden)] -/// A [`Block`] that was the result of execution in the `ContractRuntime` along with any execution -/// effects it may have. -#[derive(Clone, Debug, DataSize)] -pub struct BlockAndExecutionArtifacts { - /// The [`Block`] the contract runtime executed. - pub(crate) block: Arc, - /// The [`ApprovalsHashes`] for the transactions in this block. - pub(crate) approvals_hashes: Box, - /// The results from executing the transactions in the block. - pub(crate) execution_artifacts: Vec, - /// The [`Effects`] and the upcoming validator sets determined by the `step` - pub(crate) step_outcome: Option, -} - -/// Type representing results of the speculative execution. -#[derive(Debug)] -pub enum SpeculativeExecutionResult { - InvalidTransaction(InvalidTransaction), - WasmV1(Box), - Evm(Box), -} - -impl SpeculativeExecutionResult { - pub fn invalid_gas_limit(transaction: Transaction) -> Self { - match transaction { - Transaction::Deploy(_) => SpeculativeExecutionResult::InvalidTransaction( - InvalidTransaction::Deploy(InvalidDeploy::UnableToCalculateGasLimit), - ), - Transaction::V1(_) => SpeculativeExecutionResult::InvalidTransaction( - InvalidTransaction::V1(InvalidTransactionV1::UnableToCalculateGasLimit), - ), - Transaction::Evm(_) => SpeculativeExecutionResult::InvalidTransaction( - InvalidTransaction::Evm(EvmTransactionError::Decode( - "EVM transactions are not routed through contract runtime".to_string(), - )), - ), - } - } - - pub fn invalid_transaction(error: InvalidTransaction) -> Self { - SpeculativeExecutionResult::InvalidTransaction(error) - } -} - -/// State to use to construct the next block in the blockchain. Includes the state root hash for the -/// execution engine as well as certain values the next header will be based on. -#[derive(DataSize, Default, Debug, Clone, Serialize)] -pub struct ExecutionPreState { - /// The height of the next `Block` to be constructed. Note that this must match the height of - /// the `FinalizedBlock` used to generate the block. - next_block_height: u64, - /// The state root to use when executing deploys. - pre_state_root_hash: Digest, - /// The parent hash of the next `Block`. - parent_hash: BlockHash, - /// The accumulated seed for the pseudo-random number generator to be incorporated into the - /// next `Block`, where additional entropy will be introduced. - parent_seed: Digest, -} - -impl ExecutionPreState { - pub(crate) fn new( - next_block_height: u64, - pre_state_root_hash: Digest, - parent_hash: BlockHash, - parent_seed: Digest, - ) -> Self { - ExecutionPreState { - next_block_height, - pre_state_root_hash, - parent_hash, - parent_seed, - } - } - - /// Creates instance of `ExecutionPreState` from given block header nad Merkle tree hash - /// activation point. - pub fn from_block_header(block_header: &BlockHeaderV2) -> Self { - ExecutionPreState { - pre_state_root_hash: *block_header.state_root_hash(), - next_block_height: block_header.height() + 1, - parent_hash: block_header.block_hash(), - parent_seed: *block_header.accumulated_seed(), - } - } - - // The height of the next `Block` to be constructed. Note that this must match the height of - /// the `FinalizedBlock` used to generate the block. - pub fn next_block_height(&self) -> u64 { - self.next_block_height - } - /// The state root to use when executing deploys. - pub fn pre_state_root_hash(&self) -> Digest { - self.pre_state_root_hash - } - /// The parent hash of the next `Block`. - pub fn parent_hash(&self) -> BlockHash { - self.parent_hash - } - /// The accumulated seed for the pseudo-random number generator to be incorporated into the - /// next `Block`, where additional entropy will be introduced. - pub fn parent_seed(&self) -> Digest { - self.parent_seed - } -} - -#[derive(Clone, Copy, Ord, Eq, PartialOrd, PartialEq, DataSize, Debug)] -pub(crate) struct EraPrice { - era_id: EraId, - gas_price: u8, -} - -impl EraPrice { - pub(crate) fn new(era_id: EraId, gas_price: u8) -> Self { - Self { era_id, gas_price } - } - - pub(crate) fn gas_price(&self) -> u8 { - self.gas_price - } - - pub(crate) fn maybe_gas_price_for_era_id(&self, era_id: EraId) -> Option { - if self.era_id == era_id { - return Some(self.gas_price); - } - - None - } -} diff --git a/node/src/components/contract_runtime/types/block_and_execution_artifacts.rs b/node/src/components/contract_runtime/types/block_and_execution_artifacts.rs new file mode 100644 index 0000000000..f7efb8fb9d --- /dev/null +++ b/node/src/components/contract_runtime/types/block_and_execution_artifacts.rs @@ -0,0 +1,22 @@ +use crate::{ + contract_runtime::{ExecutionArtifact, StepOutcome}, + types::DataSize, +}; +use casper_storage::block_store::types::ApprovalsHashes; +use casper_types::BlockV2; +use std::sync::Arc; + +#[doc(hidden)] +/// A [`Block`] that was the result of execution in the `ContractRuntime` along with any execution +/// effects it may have. +#[derive(Clone, Debug, DataSize)] +pub struct BlockAndExecutionArtifacts { + /// The [`Block`] the contract runtime executed. + pub(crate) block: Arc, + /// The [`ApprovalsHashes`] for the transactions in this block. + pub(crate) approvals_hashes: Box, + /// The results from executing the transactions in the block. + pub(crate) execution_artifacts: Vec, + /// The [`Effects`] and the upcoming validator sets determined by the `step` + pub(crate) step_outcome: Option, +} diff --git a/node/src/components/contract_runtime/types/era_price.rs b/node/src/components/contract_runtime/types/era_price.rs new file mode 100644 index 0000000000..ad09c47543 --- /dev/null +++ b/node/src/components/contract_runtime/types/era_price.rs @@ -0,0 +1,26 @@ +use crate::types::DataSize; +use casper_types::EraId; + +#[derive(Clone, Copy, Ord, Eq, PartialOrd, PartialEq, DataSize, Debug)] +pub(crate) struct EraPrice { + era_id: EraId, + gas_price: u8, +} + +impl EraPrice { + pub(crate) fn new(era_id: EraId, gas_price: u8) -> Self { + Self { era_id, gas_price } + } + + pub(crate) fn gas_price(&self) -> u8 { + self.gas_price + } + + pub(crate) fn maybe_gas_price_for_era_id(&self, era_id: EraId) -> Option { + if self.era_id == era_id { + return Some(self.gas_price); + } + + None + } +} diff --git a/node/src/components/contract_runtime/types/evm_origin_resolution.rs b/node/src/components/contract_runtime/types/evm_origin_resolution.rs new file mode 100644 index 0000000000..66d45071d3 --- /dev/null +++ b/node/src/components/contract_runtime/types/evm_origin_resolution.rs @@ -0,0 +1,37 @@ +use casper_storage::data_access_layer::BalanceIdentifier; +use casper_types::evm::IdentityInstruction as EvmIdentityInstruction; + +#[derive(Clone, Debug)] +pub(crate) struct EvmOriginResolution { + // Concrete payer selected before payment checks. This is deliberately a + // data-access balance identifier, not an EVM-specific balance mode, so + // the rest of block execution can use the normal hold/refund/fee + // machinery. + balance_identifier: BalanceIdentifier, + // State mutation to perform later, inside the same tracking copy as EVM + // execution. Origin resolution itself is read-only so a rejected + // transaction does not create accounts or links as a side effect. + #[allow(unused)] // TODO remove this when ready + identity_plan: EvmIdentityInstruction, +} + +impl EvmOriginResolution { + pub(crate) fn new( + balance_identifier: BalanceIdentifier, + identity_plan: EvmIdentityInstruction, + ) -> Self { + Self { + balance_identifier, + identity_plan, + } + } + + pub(crate) fn balance_identifier(&self) -> BalanceIdentifier { + self.balance_identifier.clone() + } + + #[allow(unused)] // TODO remove this when ready + pub(crate) fn identity_plan(&self) -> EvmIdentityInstruction { + self.identity_plan + } +} diff --git a/node/src/components/contract_runtime/types/execute_block_context.rs b/node/src/components/contract_runtime/types/execute_block_context.rs new file mode 100644 index 0000000000..60111d7dc3 --- /dev/null +++ b/node/src/components/contract_runtime/types/execute_block_context.rs @@ -0,0 +1,1048 @@ +use crate::{ + contract_runtime::{ + types::TransactionProcessContext, BlockAndExecutionArtifacts, BlockExecutionError, + ExecutionArtifact, ExecutionPreState, StepOutcome, + }, + types::{ + transaction::{WasmV2InvalidRequest, WasmV2Request}, + ExecutableBlock, InternalEraReport, + }, +}; +use casper_execution_engine::engine_state::{ + BlockInfo, InvalidRequest, SessionInputData, WasmV1Request, +}; +use casper_executor_evm::{ + BlockContext as EvmV1BlockContext, ExecuteKind as EvmExecuteKind, + ExecuteRequest as EvmV1ExecuteRequest, +}; +use casper_storage::{ + block_store::types::ApprovalsHashes, + data_access_layer::{ + balance::BalanceHandling, mint::BurnRequest, AuctionMethod, BalanceHoldKind, + BalanceHoldRequest, BalanceIdentifier, BalanceRequest, BiddingRequest, BlockGlobalRequest, + BlockRewardsRequest, EraValidatorsRequest, EvictItem, FeeRequest, HandleFeeMode, + HandleFeeRequest, HandleRefundMode, HandleRefundRequest, InsufficientBalanceHandling, + ProofHandling, PruneRequest, StepRequest, TransferRequest, + }, + system::runtime_native::Config as NativeRuntimeConfig, +}; +use casper_types::{ + bytesrepr, bytesrepr::ToBytes, evm::Address as EvmAddress, global_state::TrieMerkleProof, + ApprovalsHash, BlockHash, BlockTime, BlockV2, Chainspec, Digest, EraEndV2, EraId, + EvmTransaction, FeeHandling, Key, ProtocolVersion, PublicKey, RefundHandling, RuntimeArgs, + StoredValue, Transaction, U512, +}; +use itertools::Itertools; +use serde::Serialize; +use std::{collections::BTreeMap, sync::Arc, time::Instant}; +use thiserror::Error; +use tracing::error; + +pub(crate) enum ExecuteBlockOutcome { + FailedToCreateEraEnd { + elapsed: f64, + err_msg: String, + /// An optional `EraReport` we tried to use to construct an `EraEnd`. + maybe_era_report: Option, + /// An optional map of the next era validator weights used to construct an `EraEnd`. + maybe_next_era_validator_weights: Option<(BTreeMap, u8)>, + }, + Success { + elapsed: f64, + ret: BlockAndExecutionArtifacts, + }, +} + +impl ExecuteBlockOutcome { + pub(crate) fn total_elapsed(&self) -> f64 { + match self { + ExecuteBlockOutcome::FailedToCreateEraEnd { elapsed, .. } => *elapsed, + ExecuteBlockOutcome::Success { elapsed, .. } => *elapsed, + } + } +} + +/// An error during block execution. +#[derive(Debug, Error, Serialize)] +pub(crate) enum ExecuteBlockContextError { + /// Both the block to be executed and the execution pre-state specify the height of the next + /// block. These must agree and this error will be thrown if they do not. + #[error( + "block's height does not agree with execution pre-state. \ + block: {executable_block:?}, \ + execution pre-state: {execution_pre_state:?}" + )] + WrongBlockHeight { + /// The finalized block the system attempted to execute. + executable_block: Box, + /// The state of the blockchain prior to block execution that was to be used. + execution_pre_state: Box, + }, + #[error("Failed to get new era gas price when executing switch block")] + FailedToGetNewEraGasPrice { era_id: EraId }, + #[error("Chainspec addressable entity setting misaligned with block setting")] + InvalidAESetting(bool), + + #[error("Unable to generate transaction ids checksum")] + FailedToComputeApprovalsChecksum(bytesrepr::Error), +} + +pub(crate) struct HandlingSettings { + network_name: String, + insufficient_balance_handling: InsufficientBalanceHandling, + balance_handling: BalanceHandling, + refund_handling: RefundHandling, + fee_handling: FeeHandling, +} + +impl HandlingSettings { + pub(crate) fn new( + network_name: String, + insufficient_balance_handling: InsufficientBalanceHandling, + balance_handling: BalanceHandling, + refund_handling: RefundHandling, + fee_handling: FeeHandling, + ) -> Self { + HandlingSettings { + network_name, + insufficient_balance_handling, + balance_handling, + refund_handling, + fee_handling, + } + } + + pub(crate) fn network_name(&self) -> String { + self.network_name.clone() + } + + pub(crate) fn insufficient_balance_handling(&self) -> InsufficientBalanceHandling { + self.insufficient_balance_handling + } + + pub(crate) fn balance_handling(&self) -> BalanceHandling { + self.balance_handling + } + + pub(crate) fn refund_handling(&self) -> RefundHandling { + self.refund_handling + } + + pub(crate) fn fee_handling(&self) -> FeeHandling { + self.fee_handling + } +} + +pub(crate) struct ExecuteBlockTiming { + pre_process: Instant, + process: Option, + post_process: Option, + wasm_v1: Option, + wasm_v2: Option, + evm_v1: Option, + block_rewards: Option, + step: Option, + prune: Option, + db_write: Option, + db_flush: Option, +} + +impl ExecuteBlockTiming { + pub(crate) fn new(pre_process: Instant) -> Self { + Self { + pre_process, + process: None, + post_process: None, + wasm_v1: None, + wasm_v2: None, + evm_v1: None, + block_rewards: None, + step: None, + prune: None, + db_write: None, + db_flush: None, + } + } + + pub(crate) fn pre_process(&self) -> Instant { + self.pre_process + } + + pub(crate) fn process(&self) -> Option { + self.process + } + + pub(crate) fn post_process(&self) -> Option { + self.post_process + } + + pub(crate) fn wasm_v1(&self) -> Option { + self.wasm_v1 + } + + pub(crate) fn wasm_v2(&self) -> Option { + self.wasm_v2 + } + + pub(crate) fn evm_v1(&self) -> Option { + self.evm_v1 + } + + pub(crate) fn block_rewards(&self) -> Option { + self.block_rewards + } + + pub(crate) fn step(&self) -> Option { + self.step + } + + pub(crate) fn prune(&self) -> Option { + self.prune + } + + pub(crate) fn db_write(&self) -> Option { + self.db_write + } + + pub(crate) fn db_flush(&self) -> Option { + self.db_flush + } + + pub(crate) fn with_process(&mut self, process: Instant) -> &mut Self { + self.process = Some(process); + self + } + + pub(crate) fn with_post_process(&mut self, post_process: Instant) -> &mut Self { + self.post_process = Some(post_process); + self + } + + pub(crate) fn with_wasm_v1_starting(&mut self, wasm_v1_starting: Instant) -> &mut Self { + self.wasm_v1 = Some(wasm_v1_starting); + self + } + + pub(crate) fn with_wasm_v2_starting(&mut self, wasm_v2_starting: Instant) -> &mut Self { + self.wasm_v2 = Some(wasm_v2_starting); + self + } + + pub(crate) fn with_evm_v1_starting(&mut self, evm_v1_starting: Instant) -> &mut Self { + self.evm_v1 = Some(evm_v1_starting); + self + } + + pub(crate) fn with_block_rewards_starting( + &mut self, + block_rewards_starting: Instant, + ) -> &mut Self { + self.block_rewards = Some(block_rewards_starting); + self + } + + pub(crate) fn with_step_starting(&mut self, step_starting: Instant) -> &mut Self { + self.step = Some(step_starting); + self + } + + pub(crate) fn with_prune_starting(&mut self, prune_starting: Instant) -> &mut Self { + self.prune = Some(prune_starting); + self + } + + pub(crate) fn with_db_write_starting(&mut self, db_write_starting: Instant) -> &mut Self { + self.db_write = Some(db_write_starting); + self + } + + pub(crate) fn with_db_flush_starting(&mut self, db_flush_starting: Instant) -> &mut Self { + self.db_flush = Some(db_flush_starting); + self + } +} + +pub(crate) struct ExecuteBlockContext { + pre_state: ExecutionPreState, + executable_block: ExecutableBlock, + last_switch_block_hash: Option, + current_gas_price: u8, + next_era_gas_price: Option, + + handling_settings: HandlingSettings, + transaction_ids_checksum: Digest, + approval_hashes: Vec, + native_runtime_config: NativeRuntimeConfig, + protocol_version: ProtocolVersion, + activation_point_era_id: EraId, + prune_batch_size: u64, + addressable_entity_enabled: bool, + + // mutable variables below this line + timing: ExecuteBlockTiming, + state_root_hash: Digest, + execution_artifacts: Vec, + step_outcome: Option, +} + +impl ExecuteBlockContext { + pub(crate) fn try_new( + executable_block: &ExecutableBlock, + execution_pre_state: &ExecutionPreState, + chainspec: &Chainspec, + current_gas_price: u8, + next_era_gas_price: Option, + last_switch_block_hash: Option, + addressable_entity_enabled: bool, + ) -> Result { + let timing = ExecuteBlockTiming::new(Instant::now()); + let enable_addressable_entity = chainspec.core_config.enable_addressable_entity(); + if enable_addressable_entity != addressable_entity_enabled { + return Err(ExecuteBlockContextError::InvalidAESetting( + addressable_entity_enabled, + )); + } + let block_height = executable_block.height; + if block_height != execution_pre_state.next_block_height() { + return Err(ExecuteBlockContextError::WrongBlockHeight { + executable_block: Box::new(executable_block.clone()), + execution_pre_state: Box::new(execution_pre_state.clone()), + }); + } + if executable_block.era_report.is_some() && next_era_gas_price.is_none() { + return Err(ExecuteBlockContextError::FailedToGetNewEraGasPrice { + era_id: executable_block.era_id.successor(), + }); + } + + let transaction_ids = executable_block + .transactions + .iter() + .map(Transaction::compute_id) + .collect_vec(); + + let approval_hashes = transaction_ids + .clone() + .into_iter() + .map(|id| id.approvals_hash()) + .collect(); + + let transaction_ids_checksum = match transaction_ids.into_bytes() { + Ok(bytes) => Digest::hash(bytes), + Err(err) => { + return Err(ExecuteBlockContextError::FailedToComputeApprovalsChecksum( + err, + )) + } + }; + + let protocol_version = chainspec.protocol_version(); + let activation_point_era_id = chainspec.protocol_config.activation_point.era_id(); + let prune_batch_size = chainspec.core_config.prune_batch_size; + let addressable_entity_enabled = chainspec.core_config.enable_addressable_entity(); + + // set up network handling settings + let network_name = chainspec.network_config.name.clone(); + let insufficient_balance_handling = InsufficientBalanceHandling::HoldRemaining; + let balance_handling = BalanceHandling::Available; + let refund_handling = chainspec.core_config.refund_handling; + let fee_handling = chainspec.core_config.fee_handling; + let handling_settings = HandlingSettings::new( + network_name, + insufficient_balance_handling, + balance_handling, + refund_handling, + fee_handling, + ); + + let state_root_hash = execution_pre_state.pre_state_root_hash(); + + let native_runtime_config = NativeRuntimeConfig::from_chainspec(chainspec); + + let artifacts = Vec::with_capacity(executable_block.transactions.len()); + + Ok(ExecuteBlockContext { + pre_state: execution_pre_state.clone(), + executable_block: executable_block.clone(), + current_gas_price, + next_era_gas_price, + last_switch_block_hash, + transaction_ids_checksum, + approval_hashes, + protocol_version, + activation_point_era_id, + prune_batch_size, + addressable_entity_enabled, + native_runtime_config, + handling_settings, + timing, + state_root_hash, + execution_artifacts: artifacts, + step_outcome: None, + }) + } + + pub(crate) fn transaction_ids_checksum(&self) -> Digest { + self.transaction_ids_checksum + } + + pub(crate) fn execution_artifacts(&self) -> Vec { + self.execution_artifacts.clone() + } + + // pub(crate) fn execution_results_iter<'a>( + // &self, + // ) -> impl Iterator + Clone + use<'a, '_> { + // self.execution_artifacts + // .iter() + // .map(move |artifact| &artifact.execution_result) + // } + + pub(crate) fn block_height(&self) -> u64 { + self.executable_block.height + } + + pub(crate) fn prev_block_height(&self) -> Option { + self.executable_block.height.checked_sub(1) + } + + pub(crate) fn protocol_version(&self) -> ProtocolVersion { + self.protocol_version + } + + pub(crate) fn activation_point_era_id(&self) -> EraId { + self.activation_point_era_id + } + + pub(crate) fn prune_batch_size(&self) -> u64 { + self.prune_batch_size + } + + pub(crate) fn addressable_entity_enabled(&self) -> bool { + self.addressable_entity_enabled + } + + pub(crate) fn block_time(&self) -> BlockTime { + BlockTime::new(self.executable_block.timestamp.millis()) + } + + pub(crate) fn block_time_millis(&self) -> u64 { + self.executable_block.timestamp.millis() + } + + pub(crate) fn proposer(&self) -> Box { + self.executable_block.proposer.clone() + } + + pub(crate) fn era_id(&self) -> EraId { + self.executable_block.era_id + } + + pub(crate) fn state_root_hash(&self) -> Digest { + self.state_root_hash + } + + pub(crate) fn parent_block_hash(&self) -> BlockHash { + self.pre_state.parent_hash() + } + + pub(crate) fn network_name(&self) -> String { + self.handling_settings.network_name() + } + + pub(crate) fn refund_handling(&self) -> RefundHandling { + self.handling_settings.refund_handling() + } + + pub(crate) fn fee_handling(&self) -> FeeHandling { + self.handling_settings.fee_handling() + } + + pub(crate) fn pre_process_elapsed(&self) -> f64 { + self.timing.pre_process().elapsed().as_secs_f64() + } + + pub(crate) fn process_elapsed(&self) -> f64 { + match self.timing.process() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn post_process_elapsed(&self) -> f64 { + match self.timing.post_process() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn wasm_v1_elapsed(&self) -> f64 { + match self.timing.wasm_v1() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn wasm_v2_elapsed(&self) -> f64 { + match self.timing.wasm_v2() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn evm_v1_elapsed(&self) -> f64 { + match self.timing.evm_v1() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn block_rewards_elapsed(&self) -> f64 { + match self.timing.block_rewards() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn step_elapsed(&self) -> f64 { + match self.timing.step() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn prune_elapsed(&self) -> f64 { + match self.timing.prune() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn db_write_elapsed(&self) -> f64 { + match self.timing.db_write() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn db_flush_elapsed(&self) -> f64 { + match self.timing.db_flush() { + Some(instant) => instant.elapsed().as_secs_f64(), + None => f64::default(), + } + } + + pub(crate) fn process_starting(&mut self) -> &mut Self { + self.timing.with_process(Instant::now()); + self + } + + pub(crate) fn post_process_starting(&mut self) -> &mut Self { + self.timing.with_post_process(Instant::now()); + self + } + + pub(crate) fn wasm_v1_starting(&mut self) -> &mut Self { + self.timing.with_wasm_v1_starting(Instant::now()); + self + } + + pub(crate) fn wasm_v2_starting(&mut self) -> &mut Self { + self.timing.with_wasm_v2_starting(Instant::now()); + self + } + + pub(crate) fn evm_v1_starting(&mut self) -> &mut Self { + self.timing.with_evm_v1_starting(Instant::now()); + self + } + + pub(crate) fn block_rewards_starting(&mut self) -> &mut Self { + self.timing.with_block_rewards_starting(Instant::now()); + self + } + + pub(crate) fn step_starting(&mut self) -> &mut Self { + self.timing.with_step_starting(Instant::now()); + self + } + + pub(crate) fn prune_starting(&mut self) -> &mut Self { + self.timing.with_prune_starting(Instant::now()); + self + } + + pub(crate) fn db_write_starting(&mut self) -> &mut Self { + self.timing.with_db_write_starting(Instant::now()); + self + } + + pub(crate) fn db_flush_starting(&mut self) -> &mut Self { + self.timing.with_db_flush_starting(Instant::now()); + self + } + + pub(crate) fn with_state_root_hash(&mut self, state_root_hash: Digest) -> &mut Self { + self.state_root_hash = state_root_hash; + self + } + + pub(crate) fn with_artifact(&mut self, artifact: ExecutionArtifact) -> &mut Self { + self.execution_artifacts.push(artifact); + self + } + + pub(crate) fn with_step_outcome(&mut self, step_outcome: StepOutcome) -> &mut Self { + self.step_outcome = Some(step_outcome); + self + } +} + +impl ExecuteBlockContext { + pub(crate) fn root_not_found(&self) -> BlockExecutionError { + BlockExecutionError::RootNotFound(self.state_root_hash) + } + + pub(crate) fn block_global_request(&self) -> BlockGlobalRequest { + BlockGlobalRequest::set_block_info( + self.state_root_hash, + self.block_time(), + self.protocol_version, + self.addressable_entity_enabled, + ) + } + + pub(crate) fn balance_request(&self, balance_identifier: BalanceIdentifier) -> BalanceRequest { + BalanceRequest::new( + self.state_root_hash, + self.protocol_version, + balance_identifier, + self.handling_settings.balance_handling(), + ProofHandling::NoProofs, + ) + } + + pub(crate) fn balance_hold_request( + &self, + balance_identifier: BalanceIdentifier, + hold_amount: U512, + ) -> BalanceHoldRequest { + BalanceHoldRequest::new_processing_hold( + self.state_root_hash, + self.protocol_version, + balance_identifier, + hold_amount, + self.handling_settings.insufficient_balance_handling(), + ) + } + + pub(crate) fn gas_hold_request( + &self, + balance_identifier: BalanceIdentifier, + hold_amount: U512, + ) -> BalanceHoldRequest { + BalanceHoldRequest::new_gas_hold( + self.state_root_hash, + self.protocol_version, + balance_identifier, + hold_amount, + self.handling_settings.insufficient_balance_handling(), + ) + } + + pub(crate) fn clear_balance_hold_request( + &self, + hold_kind: BalanceHoldKind, + balance_identifier: BalanceIdentifier, + ) -> BalanceHoldRequest { + BalanceHoldRequest::new_clear( + self.state_root_hash, + self.protocol_version, + hold_kind, + balance_identifier, + ) + } + + pub(crate) fn transfer_request( + &self, + txn_ctx: &TransactionProcessContext, + runtime_args: RuntimeArgs, + ) -> TransferRequest { + TransferRequest::with_runtime_args( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + txn_ctx.transaction_hash(), + txn_ctx.initiator_addr(), + txn_ctx.authorization_keys(), + runtime_args, + ) + } + + pub(crate) fn burn_request( + &self, + txn_ctx: &TransactionProcessContext, + runtime_args: RuntimeArgs, + ) -> BurnRequest { + BurnRequest::with_runtime_args( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + txn_ctx.transaction_hash(), + txn_ctx.initiator_addr(), + txn_ctx.authorization_keys(), + runtime_args, + ) + } + + pub(crate) fn bidding_request( + &self, + txn_ctx: &TransactionProcessContext, + auction_method: AuctionMethod, + ) -> BiddingRequest { + BiddingRequest::new( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + txn_ctx.transaction_hash(), + txn_ctx.initiator_addr(), + txn_ctx.authorization_keys(), + auction_method, + ) + } + + pub(crate) fn wasm_v1_session_request( + &self, + txn_ctx: &TransactionProcessContext, + input: &SessionInputData, + ) -> Result { + WasmV1Request::new_session( + BlockInfo::new( + self.state_root_hash, + self.block_time(), + self.parent_block_hash(), + self.block_height(), + self.protocol_version, + ), + txn_ctx.gas_limit(), + input, + ) + } + + pub(crate) fn wasm_v2_request( + &self, + txn_ctx: &TransactionProcessContext, + input: crate::types::transaction::WasmV2TransactionInput, + ) -> Result { + WasmV2Request::new( + txn_ctx.gas_limit(), + self.network_name(), + self.state_root_hash, + self.parent_block_hash(), + self.block_height(), + input, + ) + } + + pub(crate) fn evm_v1_request( + &self, + evm_txn: EvmTransaction, + block_gas_limit: u64, + base_fee_wei: u128, + ) -> EvmV1ExecuteRequest { + let block_context = EvmV1BlockContext::new( + self.block_height(), + self.block_time(), + block_gas_limit, + base_fee_wei, + EvmAddress::from_block_proposer_public_key(&self.proposer()), + ); + EvmV1ExecuteRequest { + block: block_context, + kind: EvmExecuteKind::Transaction(Box::new(evm_txn)), + } + } + + pub(crate) fn refund_mode( + &self, + txn_ctx: &TransactionProcessContext, + ) -> Option { + let balance_identifier = match txn_ctx.initial_balance_identifier() { + Some(balance_identifier) => balance_identifier.clone(), + None => return None, + }; + + let limit = txn_ctx.gas_limit().value(); + let cost = txn_ctx.cost_to_use(); + let consumed = txn_ctx.consumed(); + let available = txn_ctx.available().unwrap_or(U512::zero()); + let gas_price = txn_ctx.gas_price(); + + match self.refund_handling() { + RefundHandling::NoRefund => None, + RefundHandling::Burn { refund_ratio } => Some(HandleRefundMode::Burn { + limit, + gas_price, + cost, + consumed, + source: Box::new(balance_identifier), + ratio: refund_ratio, + available, + }), + RefundHandling::Refund { refund_ratio } => { + // in normal payment handling we put a temporary processing hold + // on the paying purse rather than take the token up front. + // thus, here we only want to determine the refund amount rather than + // attempt to process a refund on something we haven't actually taken yet. + // later in the flow when the processing hold is released and payment is + // finalized we reduce the amount taken by the refunded amount. This avoids + // the churn of taking the token up front via transfer (which writes + // multiple permanent records) and then transfer some of it back (which + // writes more permanent records). + Some(HandleRefundMode::CalculateAmount { + limit, + gas_price, + consumed, + cost, + ratio: refund_ratio, + available, + }) + } + } + } + + pub(crate) fn handle_refund_request( + &self, + txn_ctx: &TransactionProcessContext, + refund_mode: HandleRefundMode, + ) -> HandleRefundRequest { + HandleRefundRequest::new( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + txn_ctx.transaction_hash(), + refund_mode, + ) + } + + pub(crate) fn fee_mode(&self, txn_ctx: &TransactionProcessContext) -> Option { + let fee_amount = txn_ctx.fee_amount(); + + let proposer = self.proposer().clone(); + let balance_identifier = match txn_ctx.initial_balance_identifier() { + Some(balance_identifier) => balance_identifier.clone(), + None => return None, + }; + + match self.fee_handling() { + FeeHandling::NoFee => Some(HandleFeeMode::credit(proposer, fee_amount, self.era_id())), + FeeHandling::Burn => { + let balance_identifier = match txn_ctx.initial_balance_identifier() { + Some(balance_identifier) => balance_identifier.clone(), + None => return None, + }; + Some(HandleFeeMode::burn(balance_identifier, Some(fee_amount))) + } + FeeHandling::PayToProposer => { + let initiator_addr = txn_ctx.initiator_addr(); + Some(HandleFeeMode::pay( + initiator_addr + .account_hash() + .map(|_| Box::new(initiator_addr.clone())), + balance_identifier, + BalanceIdentifier::Public(*(proposer)), + fee_amount, + )) + } + FeeHandling::Accumulate => { + let initiator_addr = txn_ctx.initiator_addr(); + Some(HandleFeeMode::pay( + initiator_addr + .account_hash() + .map(|_| Box::new(initiator_addr.clone())), + balance_identifier, + BalanceIdentifier::Accumulate, + fee_amount, + )) + } + } + } + + pub(crate) fn handle_fee_request( + &self, + txn_ctx: &TransactionProcessContext, + fee_mode: HandleFeeMode, + ) -> HandleFeeRequest { + HandleFeeRequest::new( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + txn_ctx.transaction_hash(), + fee_mode, + ) + } + + pub(crate) fn block_fee_request(&self) -> FeeRequest { + FeeRequest::new( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + self.block_time(), + ) + } + + pub(crate) fn block_rewards_request( + &self, + block_rewards: BTreeMap>, + ) -> BlockRewardsRequest { + BlockRewardsRequest::new( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + self.block_time(), + block_rewards, + ) + } + + pub(crate) fn step_request(&self, era_report: &InternalEraReport) -> StepRequest { + let equivocators = &era_report.equivocators; + let inactive_validators = &era_report.inactive_validators; + let evict_items = inactive_validators + .iter() + .chain(equivocators) + .map(|validator_id: &PublicKey| EvictItem::new(validator_id.clone())) + .collect(); + + let next_era_id = self.era_id().successor(); + let era_end_timestamp_millis = self.block_time_millis(); + + StepRequest::new( + self.native_runtime_config.clone(), + self.state_root_hash, + self.protocol_version, + vec![], + evict_items, + next_era_id, + era_end_timestamp_millis, + ) + } + + pub(crate) fn era_validators_request(&self) -> EraValidatorsRequest { + EraValidatorsRequest::new(self.state_root_hash) + } + + pub(crate) fn prune_request(&self, keys_to_prune: Vec) -> PruneRequest { + PruneRequest::new(self.state_root_hash, keys_to_prune) + } + + pub(crate) fn into_outcome( + self, + proof: TrieMerkleProof, + ) -> ExecuteBlockOutcome { + let proposer = *self.proposer().clone(); + let ExecuteBlockContext { + pre_state, + executable_block, + current_gas_price, + last_switch_block_hash, + approval_hashes, + protocol_version, + state_root_hash, + execution_artifacts, + step_outcome, + timing, + .. + } = self; + + let era_id = executable_block.era_id; + let next_era_id = era_id.successor(); + let maybe_next_era_validator_weights: Option<(BTreeMap, u8)> = + match step_outcome.as_ref() { + None => None, + Some(effects_and_validators) => { + match effects_and_validators + .upcoming_era_validators + .get(&next_era_id) + .cloned() + { + Some(validators) => self + .next_era_gas_price + .map(|gas_price| (validators, gas_price)), + None => None, + } + } + }; + + let era_end = match ( + executable_block.era_report, + maybe_next_era_validator_weights, + ) { + (None, None) => None, + ( + Some(InternalEraReport { + equivocators, + inactive_validators, + }), + Some((next_era_validator_weights, next_era_gas_price)), + ) => { + let rewards = executable_block.rewards.clone(); + Some(EraEndV2::new( + equivocators, + inactive_validators, + next_era_validator_weights, + rewards.unwrap_or_default(), + next_era_gas_price, + )) + } + (maybe_era_report, maybe_next_era_validator_weights) => { + let mut err_msg = "failed to create era_end".to_string(); + if maybe_era_report.is_none() { + err_msg = format!("era_end {}: maybe_era_report is none", era_id); + } + if maybe_next_era_validator_weights.is_none() { + err_msg = format!( + "era_end {}: maybe_next_era_validator_weights is none", + era_id + ); + } + let elapsed = timing.pre_process().elapsed().as_secs_f64(); + return ExecuteBlockOutcome::FailedToCreateEraEnd { + elapsed, + err_msg, + maybe_era_report, + maybe_next_era_validator_weights, + }; + } + }; + + let block = Arc::new(BlockV2::new( + pre_state.parent_hash(), + pre_state.parent_seed(), + state_root_hash, + executable_block.random_bit, + era_end, + executable_block.timestamp, + executable_block.era_id, + executable_block.height, + protocol_version, + proposer, + executable_block.transaction_map, + executable_block.rewarded_signatures, + current_gas_price, + last_switch_block_hash, + )); + + let approvals_hashes = + Box::new(ApprovalsHashes::new(*block.hash(), approval_hashes, proof)); + + let ret = BlockAndExecutionArtifacts { + block, + approvals_hashes, + execution_artifacts, + step_outcome, + }; + + let elapsed = timing.pre_process().elapsed().as_secs_f64(); + ExecuteBlockOutcome::Success { ret, elapsed } + } +} diff --git a/node/src/components/contract_runtime/types/execution_artifact.rs b/node/src/components/contract_runtime/types/execution_artifact.rs new file mode 100644 index 0000000000..72d1d530a7 --- /dev/null +++ b/node/src/components/contract_runtime/types/execution_artifact.rs @@ -0,0 +1,27 @@ +use crate::types::{DataSize, TransactionHeader}; +use casper_types::{contract_messages::Messages, execution::ExecutionResult, TransactionHash}; +use serde::Serialize; + +#[derive(Clone, Debug, DataSize, PartialEq, Eq, Serialize)] +pub(crate) struct ExecutionArtifact { + pub(crate) transaction_hash: TransactionHash, + pub(crate) transaction_header: TransactionHeader, + pub(crate) execution_result: ExecutionResult, + pub(crate) messages: Messages, +} + +impl ExecutionArtifact { + pub(crate) fn new( + transaction_hash: TransactionHash, + transaction_header: TransactionHeader, + execution_result: ExecutionResult, + messages: Messages, + ) -> Self { + Self { + transaction_hash, + transaction_header, + execution_result, + messages, + } + } +} diff --git a/node/src/components/contract_runtime/types/execution_pre_state.rs b/node/src/components/contract_runtime/types/execution_pre_state.rs new file mode 100644 index 0000000000..65ba72558b --- /dev/null +++ b/node/src/components/contract_runtime/types/execution_pre_state.rs @@ -0,0 +1,65 @@ +use crate::types::DataSize; +use casper_types::{BlockHash, BlockHeaderV2, Digest}; +use serde::Serialize; + +/// State to use to construct the next block in the blockchain. Includes the state root hash for the +/// execution engine as well as certain values the next header will be based on. +#[derive(DataSize, Default, Debug, Clone, Serialize)] +pub struct ExecutionPreState { + /// The height of the next `Block` to be constructed. Note that this must match the height of + /// the `FinalizedBlock` used to generate the block. + next_block_height: u64, + /// The root hash of the previous block. + pre_state_root_hash: Digest, + /// The parent hash of the next `Block`. + parent_hash: BlockHash, + /// The accumulated seed for the pseudo-random number generator to be incorporated into the + /// next `Block`, where additional entropy will be introduced. + parent_seed: Digest, +} + +impl ExecutionPreState { + pub(crate) fn new( + next_block_height: u64, + pre_state_root_hash: Digest, + parent_hash: BlockHash, + parent_seed: Digest, + ) -> Self { + ExecutionPreState { + next_block_height, + pre_state_root_hash, + parent_hash, + parent_seed, + } + } + + /// Creates instance of `ExecutionPreState` from given block header nad Merkle tree hash + /// activation point. + pub fn from_block_header(block_header: &BlockHeaderV2) -> Self { + ExecutionPreState { + pre_state_root_hash: *block_header.state_root_hash(), + next_block_height: block_header.height() + 1, + parent_hash: block_header.block_hash(), + parent_seed: *block_header.accumulated_seed(), + } + } + + // The height of the next `Block` to be constructed. Note that this must match the height of + /// the `FinalizedBlock` used to generate the block. + pub fn next_block_height(&self) -> u64 { + self.next_block_height + } + /// The state root to use when executing deploys. + pub fn pre_state_root_hash(&self) -> Digest { + self.pre_state_root_hash + } + /// The parent hash of the next `Block`. + pub fn parent_hash(&self) -> BlockHash { + self.parent_hash + } + /// The accumulated seed for the pseudo-random number generator to be incorporated into the + /// next `Block`, where additional entropy will be introduced. + pub fn parent_seed(&self) -> Digest { + self.parent_seed + } +} diff --git a/node/src/components/contract_runtime/types/limits_and_costs.rs b/node/src/components/contract_runtime/types/limits_and_costs.rs new file mode 100644 index 0000000000..1bf77c5760 --- /dev/null +++ b/node/src/components/contract_runtime/types/limits_and_costs.rs @@ -0,0 +1,237 @@ +use crate::{ + contract_runtime::types::transaction_process_context::TransactionProcessContextError, + types::MetaTransaction, +}; +use casper_types::{Chainspec, Gas, U512}; +use num_rational::Ratio; + +#[derive(Clone, Debug)] +pub(crate) struct LimitsAndCosts { + gas_limit: Gas, + cost_estimate: U512, + initial_cost: U512, + gas_price: u8, + + min_cost: U512, + wei_per_mote: u128, + base_fee_wei: u128, + evm_block_gas_limit: u64, + + consumed: Option, + refund: Option, + available: Option, +} + +impl LimitsAndCosts { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + gas_limit: Gas, + cost_estimate: U512, + initial_cost: U512, + gas_price: u8, + min_cost: U512, + wei_per_mote: u128, + base_fee_wei: u128, + evm_block_gas_limit: u64, + ) -> Self { + LimitsAndCosts { + gas_limit, + cost_estimate, + initial_cost, + min_cost, + wei_per_mote, + base_fee_wei, + gas_price, + consumed: None, + refund: None, + available: None, + evm_block_gas_limit, + } + } + + pub(crate) fn evm_block_gas_limit(&self) -> u64 { + self.evm_block_gas_limit + } + + pub(crate) fn gas_limit(&self) -> Gas { + self.gas_limit + } + + pub(crate) fn cost_estimate(&self) -> U512 { + self.cost_estimate + } + + pub(crate) fn initial_cost(&self) -> U512 { + self.initial_cost + } + + pub(crate) fn gas_price(&self) -> u8 { + self.gas_price + } + + pub(crate) fn min_cost(&self) -> U512 { + self.min_cost + } + + pub(crate) fn consumed(&self) -> Option { + self.consumed + } + + pub(crate) fn with_gas_limit_consumed(&mut self) -> &mut Self { + match self.consumed { + Some(consumed) => { + self.consumed = Some(consumed.saturating_add(self.gas_limit)); + } + None => { + self.consumed = Some(self.gas_limit); + } + } + self + } + + pub(crate) fn with_added_consumed(&mut self, added_consumed: Gas) -> &mut Self { + match self.consumed { + Some(consumed) => { + self.consumed = Some(consumed.saturating_add(added_consumed)); + } + None => { + self.consumed = Some(added_consumed); + } + } + self + } + + pub(crate) fn refund(&self) -> U512 { + self.refund.unwrap_or_default() + } + + pub(crate) fn with_refund(&mut self, refund: U512) -> &mut Self { + self.refund = Some(refund); + self + } + + pub(crate) fn available(&self) -> Option { + self.available + } + + pub(crate) fn with_available(&mut self, available: Option) -> &mut Self { + self.available = available; + self + } + + pub(crate) fn with_zero_cost(&mut self) -> &mut Self { + self.initial_cost = U512::zero(); + self.min_cost = U512::zero(); + self + } + + pub(crate) fn wei_per_mote(&self) -> u128 { + self.wei_per_mote + } + + pub(crate) fn base_fee_wei(&self) -> u128 { + self.base_fee_wei + } + + /// Converts an EVM gas cost, denominated in wei, to motes by rounding up. + /// + /// Rounding is applied after multiplying gas by price, so sub-mote totals + /// are charged as one mote without overcharging each gas unit separately. + pub(crate) fn evm_gas_fee_motes(&self, gas: u64, gas_price_wei: u128) -> Option { + let wei_per_mote = self.wei_per_mote(); + if wei_per_mote == 0 { + return None; + } + let fee_wei = U512::from(gas).checked_mul(U512::from(gas_price_wei))?; + Some( + Ratio::new(fee_wei, U512::from(wei_per_mote)) + .ceil() + .to_integer(), + ) + } + + pub(crate) fn try_from_meta_txn( + mtxn: &MetaTransaction, + chainspec: &Chainspec, + ) -> Result { + /* + we solve for halting state using a `gas limit` which is the maximum amount of + computation we will allow a given transaction to consume. the transaction itself + provides a function to determine this if provided with the current cost tables + gas_limit is ALWAYS calculated with price == 1. + + next there is the actual cost, i.e. how much we charge for that computation + this is calculated by multiplying the gas limit by the current `gas_price` + gas price has a floor of 1, and the ceiling is configured in the chainspec + NOTE: when the gas price is 1, the gas limit and the cost are coincidentally + equal because x == x * 1; thus it is recommended to run tests with + price >1 to avoid being confused by this. + + the third important value is the amount of computation consumed by executing a transaction. + consumed is determined after execution and is used for refund & fee post-processing. + + for native transactions, there is no wasm and the consumed always equals the limit. + for bytecode / wasm based transactions, the consumed is based on what opcodes were executed + and can range from >=0 to <=gas_limit. + */ + + let cost_estimate = match mtxn.cost_estimate() { + Some(cost_estimate) => cost_estimate, + None => return Err(TransactionProcessContextError::MissingCostEstimate), + }; + + let evm_block_gas_limit = chainspec.evm_config.block_gas_limit; + + let initial_cost = mtxn.initial_cost().value(); + + let gas_price = mtxn.gas_price(); + + let gas_limit = match &mtxn.gas_limit(chainspec) { + Ok(gas_limit) => *gas_limit, + Err(ite) => { + return Err(TransactionProcessContextError::InvalidTransaction( + ite.clone(), + )) + } + }; + let gas_limit_value = gas_limit.value(); + + let baseline_motes_amount = chainspec.core_config.baseline_motes_amount_u512(); + let min_cost = match mtxn.min_cost(gas_limit_value, baseline_motes_amount) { + Ok(motes) => motes.value(), + Err(err) => return Err(TransactionProcessContextError::InvalidTransaction(err)), + }; + + let wei_per_mote = u128::from(chainspec.evm_config.wei_per_mote); + let base_fee_wei = chainspec.evm_config.base_fee_wei(); + + Ok(LimitsAndCosts::new( + gas_limit, + cost_estimate, + initial_cost, + gas_price, + min_cost, + wei_per_mote, + base_fee_wei, + evm_block_gas_limit, + )) + } +} + +impl Default for LimitsAndCosts { + fn default() -> Self { + LimitsAndCosts { + gas_limit: Gas::default(), + cost_estimate: U512::zero(), + initial_cost: U512::zero(), + min_cost: U512::zero(), + wei_per_mote: 0u128, + base_fee_wei: 0u128, + gas_price: 0, + consumed: None, + refund: None, + available: None, + evm_block_gas_limit: 0u64, + } + } +} diff --git a/node/src/components/contract_runtime/types/process_request.rs b/node/src/components/contract_runtime/types/process_request.rs new file mode 100644 index 0000000000..ef8687cdf4 --- /dev/null +++ b/node/src/components/contract_runtime/types/process_request.rs @@ -0,0 +1,67 @@ +use crate::types::transaction::WasmV2TransactionInput; +use casper_execution_engine::engine_state::SessionInputData; +use casper_types::{EvmTransaction, TransactionArgs, TransactionEntryPoint}; +use std::fmt::Formatter; + +#[derive(Clone, Debug)] +pub(crate) enum ProcessRequest { + Unknown, + NativeMint { + session_args: TransactionArgs, + entry_point: TransactionEntryPoint, + }, + NativeAuction { + session_args: TransactionArgs, + entry_point: TransactionEntryPoint, + }, + WasmV1 { + session_input_data: SessionInputData, + }, + WasmV2 { + transaction_input: WasmV2TransactionInput, + }, + EvmV1 { + evm_txn: EvmTransaction, + base_fee_wei: u128, + effective_gas_price: u128, + block_gas_limit: u64, + }, + NoExecEvm { + effective_gas_price: u128, + }, + NoExec, +} + +impl ProcessRequest { + pub(crate) fn requires_processing_hold(&self) -> bool { + match self { + ProcessRequest::NativeMint { .. } + | ProcessRequest::NativeAuction { .. } + | ProcessRequest::WasmV1 { .. } + | ProcessRequest::WasmV2 { .. } + | ProcessRequest::EvmV1 { .. } => true, + ProcessRequest::NoExecEvm { .. } | ProcessRequest::NoExec | ProcessRequest::Unknown => { + false + } + } + } +} + +impl std::fmt::Display for ProcessRequest { + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ProcessRequest::Unknown => { + write!(formatter, "unknown process request") + } + ProcessRequest::NativeMint { .. } => write!(formatter, "native mint process request"), + ProcessRequest::NativeAuction { .. } => { + write!(formatter, "native auction process request") + } + ProcessRequest::WasmV1 { .. } => write!(formatter, "wasm_v1 process request"), + ProcessRequest::WasmV2 { .. } => write!(formatter, "wasm_v2 process request"), + ProcessRequest::EvmV1 { .. } => write!(formatter, "evm_v1 process request"), + ProcessRequest::NoExecEvm { .. } => write!(formatter, "no_exec_evm process request"), + ProcessRequest::NoExec => write!(formatter, "no_exec process request"), + } + } +} diff --git a/node/src/components/contract_runtime/types/speculative_execution_result.rs b/node/src/components/contract_runtime/types/speculative_execution_result.rs new file mode 100644 index 0000000000..167b60957b --- /dev/null +++ b/node/src/components/contract_runtime/types/speculative_execution_result.rs @@ -0,0 +1,33 @@ +use casper_types::{ + EvmTransactionError, InvalidDeploy, InvalidTransaction, InvalidTransactionV1, Transaction, +}; + +/// Type representing results of the speculative execution. +#[derive(Debug)] +pub enum SpeculativeExecutionResult { + InvalidTransaction(InvalidTransaction), + WasmV1(Box), + Evm(Box), +} + +impl SpeculativeExecutionResult { + pub fn invalid_gas_limit(transaction: Transaction) -> Self { + match transaction { + Transaction::Deploy(_) => SpeculativeExecutionResult::InvalidTransaction( + InvalidTransaction::Deploy(InvalidDeploy::UnableToCalculateGasLimit), + ), + Transaction::V1(_) => SpeculativeExecutionResult::InvalidTransaction( + InvalidTransaction::V1(InvalidTransactionV1::UnableToCalculateGasLimit), + ), + Transaction::Evm(_) => SpeculativeExecutionResult::InvalidTransaction( + InvalidTransaction::Evm(EvmTransactionError::Decode( + "EVM transactions are not routed through contract runtime".to_string(), + )), + ), + } + } + + pub fn invalid_transaction(error: InvalidTransaction) -> Self { + SpeculativeExecutionResult::InvalidTransaction(error) + } +} diff --git a/node/src/components/contract_runtime/types/static_evm_block_hash_provider.rs b/node/src/components/contract_runtime/types/static_evm_block_hash_provider.rs new file mode 100644 index 0000000000..9e1dbb88ff --- /dev/null +++ b/node/src/components/contract_runtime/types/static_evm_block_hash_provider.rs @@ -0,0 +1,24 @@ +use casper_executor_evm::{ + BlockHashProvider as EvmBlockHashProvider, + BlockHashProviderResult as EvmBlockHashProviderResult, +}; +use casper_types::BlockHash; +use std::collections::BTreeMap; + +#[derive(Default)] +pub(crate) struct StaticEvmBlockHashProvider { + block_hashes: BTreeMap, +} + +impl StaticEvmBlockHashProvider { + /// Ctor + pub(crate) fn new(block_hashes: BTreeMap) -> Self { + StaticEvmBlockHashProvider { block_hashes } + } +} + +impl EvmBlockHashProvider for StaticEvmBlockHashProvider { + fn block_hash(&self, block_height: u64) -> EvmBlockHashProviderResult> { + Ok(self.block_hashes.get(&block_height).copied()) + } +} diff --git a/node/src/components/contract_runtime/types/step_outcome.rs b/node/src/components/contract_runtime/types/step_outcome.rs new file mode 100644 index 0000000000..6656c2cadb --- /dev/null +++ b/node/src/components/contract_runtime/types/step_outcome.rs @@ -0,0 +1,12 @@ +use crate::types::DataSize; +use casper_types::{execution::Effects, EraId, PublicKey, U512}; +use std::collections::BTreeMap; + +/// Effects from running step and the next era validators that are gathered when an era ends. +#[derive(Clone, Debug, DataSize)] +pub(crate) struct StepOutcome { + /// Validator sets for all upcoming eras that have already been determined. + pub(crate) upcoming_era_validators: BTreeMap>, + /// An [`Effects`] created by an era ending. + pub(crate) step_effects: Effects, +} diff --git a/node/src/components/contract_runtime/types/transaction_process_context.rs b/node/src/components/contract_runtime/types/transaction_process_context.rs new file mode 100644 index 0000000000..1351d600d2 --- /dev/null +++ b/node/src/components/contract_runtime/types/transaction_process_context.rs @@ -0,0 +1,825 @@ +use crate::{ + contract_runtime::{ + types::{EvmOriginResolution, ExecutionArtifact, LimitsAndCosts, ProcessRequest}, + StateResultError, + }, + types::{ + transaction::{WasmV2Error, WasmV2InvalidRequest, WasmV2Result}, + MetaTransaction, TransactionHeader, + }, +}; +use casper_execution_engine::engine_state::{ + Error, InvalidRequest as InvalidWasmV1Request, WasmV1Result, +}; +use casper_executor_evm::ExecutionStatus; +use casper_storage::data_access_layer::{ + auction::AuctionMethodError, mint::BurnResult, BalanceHoldResult, BalanceIdentifier, + BalanceIdentifierError, BalanceResult, BiddingResult, HandleFeeResult, HandleRefundResult, + TransferResult, +}; +use casper_types::{ + account::AccountHash, + contract_messages::Messages, + evm, + evm::Receipt as EvmReceipt, + execution::{Effects, EvmExecutionResult, ExecutionResult, ExecutionResultV2}, + Chainspec, EvmTransactionError, Gas, HashAddr, InitiatorAddr, InvalidTransaction, PublicKey, + Transaction, TransactionHash, Transfer, AUCTION_LANE_ID, MINT_LANE_ID, U512, +}; +use std::collections::BTreeSet; +use strum::Display; + +pub(crate) enum BalanceIdentifierResolution { + Identifier(BalanceIdentifier), + CheckContractPay(BalanceIdentifier), + CheckEvmAccount(BalanceIdentifier), +} + +pub(crate) enum InitialBalanceIdentifierResult { + Unknown, + Identifier(BalanceIdentifier), + IdentifierAndStateError(BalanceIdentifier, StateResultError), + IdentifierAndEvmResolution(BalanceIdentifier, EvmOriginResolution), + IdentifierAndEvmError(BalanceIdentifier, EvmTransactionError), +} + +impl InitialBalanceIdentifierResult { + pub(crate) fn balance_identifier(&self) -> Option<&BalanceIdentifier> { + match self { + InitialBalanceIdentifierResult::Unknown => None, + InitialBalanceIdentifierResult::Identifier(bi) + | InitialBalanceIdentifierResult::IdentifierAndStateError(bi, _) + | InitialBalanceIdentifierResult::IdentifierAndEvmResolution(bi, _) + | InitialBalanceIdentifierResult::IdentifierAndEvmError(bi, _) => Some(bi), + } + } + + pub(crate) fn state_result_error(&self) -> Option<&StateResultError> { + if let InitialBalanceIdentifierResult::IdentifierAndStateError(_, err) = self { + Some(err) + } else { + None + } + } + + pub(crate) fn evm_origin_resolution(&self) -> Option<&EvmOriginResolution> { + if let InitialBalanceIdentifierResult::IdentifierAndEvmResolution(_, eor) = self { + Some(eor) + } else { + None + } + } + + pub(crate) fn evm_transaction_error(&self) -> Option<&EvmTransactionError> { + if let InitialBalanceIdentifierResult::IdentifierAndEvmError(_, err) = self { + Some(err) + } else { + None + } + } +} + +#[derive(Debug, Display)] +#[allow(unused)] // TODO remove this when ready +pub(crate) enum TransactionProcessContextError { + InvalidTransaction(InvalidTransaction), + MissingInitiatorAddr, + MissingHeader, + MissingEvmGasLimit, + MissingMetaTransaction, + MissingCostEstimate, + FailedToSetCost, + FailedToSetEvmCost, + EvmFeeOverflow, + InvalidEvmTransaction, + BalanceIdentifierError(Box), +} + +#[derive(Clone, Debug)] +pub(crate) struct TransactionProcessContext { + transaction_hash: TransactionHash, + header: TransactionHeader, + meta_transaction: MetaTransaction, + limits_and_costs: LimitsAndCosts, + + initial_balance_identifier: Option, + initial_balance_result: Option, + + evm_origin_resolution: Option, + evm_receipt: Option, + + exec_attempted: bool, + error_message: Option, + effects: Effects, + messages: Messages, + transfers: Vec, +} + +impl TransactionProcessContext { + pub(crate) fn try_new( + txn: &Transaction, + chainspec: &Chainspec, + gas_price: u8, + ) -> Result { + let meta_transaction = + match MetaTransaction::new_from_txn_with_price(txn, chainspec, gas_price) { + Ok(meta_transaction) => meta_transaction, + Err(err) => return Err(TransactionProcessContextError::InvalidTransaction(err)), + }; + + let limits_and_costs = LimitsAndCosts::try_from_meta_txn(&meta_transaction, chainspec)?; + + Ok(TransactionProcessContext { + effects: Effects::new(), + transaction_hash: txn.hash(), + header: txn.into(), + meta_transaction, + limits_and_costs, + + initial_balance_identifier: None, + initial_balance_result: None, + evm_receipt: None, + evm_origin_resolution: None, + + exec_attempted: false, + error_message: None, + transfers: vec![], + messages: Default::default(), + }) + } + + pub(crate) fn transaction_hash(&self) -> TransactionHash { + self.transaction_hash + } + + pub(crate) fn initiator_addr(&self) -> InitiatorAddr { + self.meta_transaction.initiator_addr().clone() + } + + pub(crate) fn initial_balance_identifier(&self) -> Option<&BalanceIdentifier> { + self.initial_balance_identifier.as_ref() + } + + pub(crate) fn contract_direct_address(&self) -> Option<(HashAddr, String)> { + self.meta_transaction.contract_direct_address() + } + + pub(crate) fn authorization_keys(&self) -> BTreeSet { + self.meta_transaction.authorization_keys() + } + + pub(crate) fn gas_price(&self) -> u8 { + self.limits_and_costs.gas_price() + } + + pub(crate) fn gas_limit(&self) -> Gas { + let is_evm = self.meta_transaction.is_evm(); + if is_evm { + Gas::new(self.cost_to_use()) + } else { + self.limits_and_costs.gas_limit() + } + } + + pub(crate) fn consumed(&self) -> U512 { + if self.error_message.is_some() { + return self.cost_to_use(); + } + match &self.initial_balance_identifier { + Some(bi) => { + if bi.is_penalty() { + return self.cost_to_use(); + } + } + None => return U512::zero(), // can't consume if no purse + } + self.limits_and_costs.consumed().unwrap_or_default().value() + } + + pub(crate) fn available(&self) -> Option { + self.limits_and_costs.available() + } + + pub(crate) fn refund_amount(&self) -> U512 { + self.limits_and_costs.refund() + } + + pub(crate) fn has_sufficient_minimum(&self) -> bool { + self.has_sufficient_balance(self.limits_and_costs.min_cost()) + } + + pub(crate) fn has_sufficient_estimated(&self) -> bool { + self.has_sufficient_balance(self.limits_and_costs.cost_estimate()) + } + + pub(crate) fn has_sufficient_balance(&self, amount: U512) -> bool { + match self.limits_and_costs.available() { + Some(available) => available >= amount, + None => false, + } + } + + pub(crate) fn cost_to_use(&self) -> U512 { + // to prevent do-nothing exhaustion and other 0 cost scenarios, + // we raise cost to min_cost if less than that + + let initial_cost = self.limits_and_costs.initial_cost(); + let min_cost = self.limits_and_costs.min_cost(); + + let cost = { + let cost = initial_cost; + if cost < min_cost { + min_cost + } else { + cost + } + }; + + match self.limits_and_costs.available() { + Some(available) => { + if available < initial_cost { + available + } else { + cost + } + } + None => cost, + } + } + + pub(crate) fn fee_amount(&self) -> U512 { + let cost_to_use = self.cost_to_use(); + let refund_amount = self.refund_amount(); + let available = self.available().unwrap_or(U512::zero()); + // take the lower of the difference between cost - refund OR available + cost_to_use.saturating_sub(refund_amount).min(available) + } + + pub(crate) fn transaction_lane(&self) -> u8 { + self.meta_transaction.transaction_lane() + } + + pub(crate) fn error_message(&self) -> Option { + self.error_message.clone() + } +} + +impl TransactionProcessContext { + // *************** EVM ************** + + pub(crate) fn evm_effective_gas_price(&self) -> Option { + self.meta_transaction + .evm_effective_gas_cost(self.limits_and_costs.base_fee_wei()) + } + + pub(crate) fn evm_address(&self) -> Option { + if let Some(addr) = &self.initiator_addr().evm_address() { + return Some(*addr); + } + None + } + + pub(crate) fn evm_addr_receipt(&self) -> Option<(evm::Address, EvmReceipt)> { + if let Some(addr) = self.evm_address() { + if let Some(receipt) = &self.evm_receipt { + return Some((addr, receipt.clone())); + } + } + None + } + + pub(crate) fn evm_signer(&self) -> Option> { + self.meta_transaction.evm_signer() + } +} + +impl TransactionProcessContext { + // *************** APPENDERS (aka "WITHS") ************** + pub(crate) fn with_gas_limit_consumed(&mut self) -> &mut Self { + self.limits_and_costs.with_gas_limit_consumed(); + self + } + + pub(crate) fn with_added_consumed(&mut self, consumed: Gas) -> &mut Self { + self.limits_and_costs.with_added_consumed(consumed); + self + } + + pub(crate) fn with_appended_transfers(&mut self, transfers: &mut Vec) -> &mut Self { + self.transfers.append(transfers); + self + } + + pub(crate) fn with_appended_effects(&mut self, effects: Effects) -> &mut Self { + self.effects.append(effects); + self + } + + pub(crate) fn with_appended_messages(&mut self, messages: &mut Messages) -> &mut Self { + self.messages.append(messages); + self + } + + pub(crate) fn with_state_result_error( + &mut self, + error: StateResultError, + ) -> Result<&mut Self, ()> { + if let StateResultError::RootNotFound = error { + return Err(()); + } + if self.error_message.is_none() { + self.error_message = Some(format!("{:?}", error)); + } + Ok(self) + } + + pub(crate) fn with_evm_error(&mut self, error: EvmTransactionError) -> &mut Self { + if self.error_message.is_none() { + self.error_message = Some(format!("{:?}", error)); + } + self + } + + pub(crate) fn with_initial_balance_identifier( + &mut self, + identifier: BalanceIdentifier, + ) -> &mut Self { + self.initial_balance_identifier = Some(identifier); + self + } + + pub(crate) fn with_initial_balance_result( + &mut self, + balance_result: BalanceResult, + ) -> &mut Self { + // there is no point recording BalanceResult::RootNotFound because it is unrecoverable + if let (None, Some(err)) = (&self.error_message, balance_result.error()) { + self.error_message = Some(format!("{}", err)); + } + if let Some(purse) = balance_result.purse_addr() { + let minimum_amount = self.limits_and_costs.min_cost(); + let is_sufficient = balance_result.is_sufficient(minimum_amount); + if !is_sufficient { + self.error_message = Some(format!( + "Purse {} has less than minimum amount {}", + base16::encode_lower(&purse), + minimum_amount + )); + } + } + let available = balance_result.available_balance().copied(); + self.limits_and_costs.with_available(available); + self.initial_balance_result = Some(balance_result); + self + } + + pub(crate) fn with_wasm_v1_result( + &mut self, + wasm_v1_result: WasmV1Result, + ) -> Result<&mut Self, ()> { + if let Some(Error::RootNotFound(_)) = wasm_v1_result.error() { + return Err(()); + } + self.with_added_consumed(wasm_v1_result.consumed()); + + if let Some(err) = wasm_v1_result.error() { + self.error_message = Some(format!("{}", err)); + } else if wasm_v1_result.consumed() == Gas::zero() { + self.error_message = Some("Wasm consumed 0 gas".to_string()); + } + + if self.error_message.is_some() { + return Ok(self); + } + + self.with_appended_transfers(&mut wasm_v1_result.transfers().clone()) + .with_appended_messages(&mut wasm_v1_result.messages().clone()) + .with_appended_effects(wasm_v1_result.effects().clone()); + Ok(self) + } + + pub(crate) fn with_error_message(&mut self, error_message: String) -> &mut Self { + self.error_message = Some(error_message); + self + } + + pub(crate) fn with_handle_refund_result( + &mut self, + handle_refund_result: &HandleRefundResult, + ) -> Result<&mut Self, ()> { + if let HandleRefundResult::RootNotFound = handle_refund_result { + return Err(()); + } + if let HandleRefundResult::Success { + effects, transfers, .. + } = handle_refund_result + { + self.with_appended_transfers(&mut transfers.clone()) + .with_appended_effects(effects.clone()); + } + if let (None, HandleRefundResult::Failure(_)) = (&self.error_message, handle_refund_result) + { + self.error_message = handle_refund_result.error_message(); + return Ok(self); + } + Ok(self) + } + + pub(crate) fn with_handle_fee_result( + &mut self, + handle_fee_result: &HandleFeeResult, + ) -> Result<&mut Self, ()> { + if let HandleFeeResult::RootNotFound = handle_fee_result { + return Err(()); + } + if let HandleFeeResult::Success { + effects, transfers, .. + } = handle_fee_result + { + self.with_appended_transfers(&mut transfers.clone()) + .with_appended_effects(effects.clone()); + } + if let (None, HandleFeeResult::Failure(_)) = (&self.error_message, handle_fee_result) { + self.error_message = handle_fee_result.error_message(); + return Ok(self); + } + Ok(self) + } + + pub(crate) fn with_balance_hold_result( + &mut self, + hold_result: &BalanceHoldResult, + ) -> Result<&mut Self, ()> { + if let BalanceHoldResult::RootNotFound = hold_result { + return Err(()); + } + if let BalanceHoldResult::Success { effects, .. } = hold_result { + self.with_appended_effects(*effects.clone()); + } + if let (None, BalanceHoldResult::Failure(_)) = (&self.error_message, &hold_result) { + self.error_message = hold_result.error_message(); + return Ok(self); + } + Ok(self) + } + + pub(crate) fn with_refund_amount(&mut self, refund: U512) -> &mut Self { + self.limits_and_costs.with_refund(refund); + self + } + + pub(crate) fn with_zero_cost(&mut self) -> &mut Self { + self.limits_and_costs.with_zero_cost(); + self + } + + pub(crate) fn with_evm_execution_outcome( + &mut self, + outcome: casper_executor_evm::ExecutionOutcome, + effective_gas_price: u128, + effects: Effects, + ) -> &mut Self { + let consumed = match outcome.status { + ExecutionStatus::Success => { + let gas_used = outcome.gas_used; + self.limits_and_costs + .evm_gas_fee_motes(gas_used, effective_gas_price) + .unwrap_or(self.cost_to_use()) + } + ExecutionStatus::Revert => self.cost_to_use(), + ExecutionStatus::Halt(reason) => { + if self.error_message.is_none() { + self.error_message = Some(format!("{:?}", reason)); + } + self.cost_to_use() + } + }; + + let receipt = outcome.to_receipt(effective_gas_price); + self.evm_receipt = Some(receipt); + self.limits_and_costs + .with_added_consumed(Gas::new(consumed)); + self.with_appended_effects(effects); + + self + + // if let Some(err) = wasm_v1_result.error() { + // self.error_message = Some(format!("{}", err)); + // } else if wasm_v1_result.consumed() == Gas::zero() { + // self.error_message = Some("Wasm consumed 0 gas".to_string()); + // } + // + // if self.error_message.is_some() { + // return Ok(self); + // } + // + // self.with_appended_transfers(&mut wasm_v1_result.transfers().clone()) + // .with_appended_messages(&mut wasm_v1_result.messages().clone()) + // .with_appended_effects(wasm_v1_result.effects().clone()); + // Ok(self) + } + + pub(crate) fn with_invalid_wasm_v1_request( + &mut self, + invalid_request: &InvalidWasmV1Request, + ) -> &mut Self { + if self.error_message.is_none() { + self.error_message = Some(format!("{}", invalid_request)); + } + self + } + + pub(crate) fn with_auction_method_error( + &mut self, + auction_method_error: &AuctionMethodError, + ) -> &mut Self { + if self.error_message.is_none() { + self.error_message = Some(format!("{}", auction_method_error)); + } + self + } + + pub(crate) fn with_transfer_result( + &mut self, + transfer_result: TransferResult, + ) -> Result<&mut Self, ()> { + if let TransferResult::RootNotFound = transfer_result { + return Err(()); + } + if let (None, TransferResult::Failure(err)) = (&self.error_message, &transfer_result) { + self.error_message = Some(format!("{}", err)); + } + if let TransferResult::Success { + effects, + transfers, + cache: _, + } = transfer_result + { + self.with_appended_transfers(&mut transfers.clone()) + .with_appended_effects(effects); + } + Ok(self) + } + + pub(crate) fn with_burn_result(&mut self, burn_result: BurnResult) -> Result<&mut Self, ()> { + if let BurnResult::RootNotFound = burn_result { + return Err(()); + } + if let (None, BurnResult::Failure(err)) = (&self.error_message, &burn_result) { + self.error_message = Some(format!("{}", err)); + } + if let BurnResult::Success { effects, cache: _ } = burn_result { + self.with_appended_effects(effects); + } + Ok(self) + } + + pub(crate) fn with_bidding_result( + &mut self, + bidding_result: BiddingResult, + ) -> Result<&mut Self, ()> { + if let BiddingResult::RootNotFound = bidding_result { + return Err(()); + } + if let (None, BiddingResult::Failure(err)) = (&self.error_message, &bidding_result) { + self.error_message = Some(format!("{}", err)); + } + if let BiddingResult::Success { + effects, transfers, .. + } = bidding_result + { + self.with_appended_transfers(&mut transfers.clone()) + .with_appended_effects(effects); + } + Ok(self) + } + + /// Adds the error message from a `InvalidRequest` to the artifact. + pub(crate) fn with_invalid_wasm_v2_request(&mut self, ire: WasmV2InvalidRequest) -> &mut Self { + if self.error_message.is_none() { + self.error_message = Some(format!("{}", ire)); + } + self + } + + /// Adds the result from a `WasmV2Result` to the artifact. + pub(crate) fn with_wasm_v2_result(&mut self, result: WasmV2Result) -> &mut Self { + self.with_added_consumed(Gas::from(result.gas_usage().gas_spent())); + + // TODO: Use system message to notify about contract hash + + self.with_appended_effects(result.effects().clone()); + + self + } + + /// Adds the error message from a `WasmV2Error` to the artifact. + #[inline] + pub(crate) fn with_wasm_v2_error(&mut self, error: WasmV2Error) -> &mut Self { + self.with_error_message(error.to_string()); + self + } + + pub(crate) fn with_exec_attempt(&mut self) -> &mut Self { + self.exec_attempted = true; + if self.error_message.is_some() { + return self; + } + match &self.initial_balance_identifier { + Some(bi) => { + if bi.is_penalty() { + self.with_error_message("exec attempt while penalized".to_string()); + return self; + } + } + None => { + let err_msg = "exec attempt without initial balance identifier".to_string(); + self.with_error_message(err_msg); + return self; + } + } + + if !self.has_sufficient_estimated() { + let err_msg = "exec attempt with less than estimated balance".to_string(); + self.with_error_message(err_msg); + return self; + } + self + } + + pub(crate) fn with_evm_origin_resolution( + &mut self, + resolution: Option, + ) -> &mut Self { + self.evm_origin_resolution = resolution; + self + } +} + +impl TransactionProcessContext { + // *************** FLOW CONTROL **************** + + pub(crate) fn allow_execution(&self) -> bool { + if self.error_message.is_some() { + return false; + } + + true + } + + pub(crate) fn process_request(&self) -> ProcessRequest { + let txn = &self.meta_transaction; + if !self.allow_execution() { + let is_evm = txn.is_evm(); + if is_evm { + let effective_gas_price = self.evm_effective_gas_price().unwrap_or(0u128); + return ProcessRequest::NoExecEvm { + effective_gas_price, + }; + } + return ProcessRequest::NoExec; + } + + let lane = txn.transaction_lane(); + if lane == MINT_LANE_ID { + return ProcessRequest::NativeMint { + session_args: txn.session_args(), + entry_point: txn.entry_point(), + }; + } + if lane == AUCTION_LANE_ID { + return ProcessRequest::NativeAuction { + session_args: txn.session_args(), + entry_point: txn.entry_point(), + }; + } + if txn.is_v1_wasm() { + return ProcessRequest::WasmV1 { + session_input_data: txn.to_session_input_data(), + }; + } + if txn.is_v2_wasm() { + return ProcessRequest::WasmV2 { + transaction_input: txn.to_transaction_info(), + }; + } + match txn.as_evm() { + Some(evm_txn) => { + let effective_gas_price = match self.evm_effective_gas_price() { + Some(effective_gas_price) => effective_gas_price, + None => return ProcessRequest::Unknown, + }; + + let block_gas_limit = self.limits_and_costs.evm_block_gas_limit(); + let base_fee_wei = self.limits_and_costs.base_fee_wei(); + + ProcessRequest::EvmV1 { + evm_txn: evm_txn.clone(), + base_fee_wei, + effective_gas_price, + block_gas_limit, + } + } + None => ProcessRequest::Unknown, + } + } + + pub(crate) fn balance_identifier_resolution( + &self, + ) -> Result { + let initiator_addr = self.initiator_addr().clone(); + let default_ret = match BalanceIdentifier::try_from(initiator_addr.clone()) { + Ok(bi) => bi, + Err(_) => { + return Err(TransactionProcessContextError::BalanceIdentifierError( + Box::new(BalanceIdentifierError::TryFromInitiatorAddr(initiator_addr)), + )) + } + }; + match &self.meta_transaction { + MetaTransaction::Deploy(_) => Ok(BalanceIdentifierResolution::Identifier(default_ret)), + MetaTransaction::V1(mv1) => { + if mv1.is_v1_wasm() { + Ok(BalanceIdentifierResolution::CheckContractPay(default_ret)) + } else { + Ok(BalanceIdentifierResolution::Identifier(default_ret)) + } + } + MetaTransaction::Evm(mtxn) => { + let evm_ret = match BalanceIdentifier::try_from(mtxn.initiator_addr()) { + Ok(bi) => bi, + Err(_) => { + return Err(TransactionProcessContextError::BalanceIdentifierError( + Box::new(BalanceIdentifierError::TryFromInitiatorAddr(initiator_addr)), + )) + } + }; + + Ok(BalanceIdentifierResolution::CheckEvmAccount(evm_ret)) + } + } + } + + // *************** TAKE **************** + pub(crate) fn into_execution_artifact(self) -> ExecutionArtifact { + let actual_cost = self.cost_to_use(); + let initiator_addr = self.initiator_addr().clone(); + let size_estimate = self.meta_transaction.size_estimate() as u64; + let execution_result = if let Some((initiator, receipt)) = &self.evm_addr_receipt() { + let result = EvmExecutionResult { + initiator: *initiator, + current_price: self.limits_and_costs.gas_price(), + limit: self.limits_and_costs.gas_limit(), + cost: actual_cost, + refund: self.limits_and_costs.refund(), + size_estimate, + effects: self.effects, + receipt: receipt.clone(), + }; + ExecutionResult::from(result) + } else { + let result = ExecutionResultV2 { + effects: self.effects, + transfers: self.transfers, + initiator: initiator_addr, + refund: self.limits_and_costs.refund(), + limit: self.limits_and_costs.gas_limit(), + consumed: self.limits_and_costs.consumed().unwrap_or_default(), + cost: actual_cost, + current_price: self.limits_and_costs.gas_price(), + size_estimate, + error_message: self.error_message, + }; + ExecutionResult::V2(Box::new(result)) + }; + ExecutionArtifact::new( + self.transaction_hash, + self.header.clone(), + execution_result, + self.messages, + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use casper_types::DEFAULT_WEI_PER_MOTE; + + #[test] + fn should_convert_wei_gas_fee_to_motes() { + let x = LimitsAndCosts::default(); + + assert_eq!( + x.evm_gas_fee_motes(21_000, u128::from(DEFAULT_WEI_PER_MOTE)), + Some(U512::from(21_000)) + ); + } + + #[test] + fn should_round_sub_mote_wei_gas_fee_up_to_one_mote() { + let x = LimitsAndCosts::default(); + + assert_eq!(x.evm_gas_fee_motes(1, 1), Some(U512::from(1))); + } +} diff --git a/node/src/components/contract_runtime/types/validator_weights_by_era_id_request.rs b/node/src/components/contract_runtime/types/validator_weights_by_era_id_request.rs new file mode 100644 index 0000000000..615cbb5bd7 --- /dev/null +++ b/node/src/components/contract_runtime/types/validator_weights_by_era_id_request.rs @@ -0,0 +1,42 @@ +use casper_storage::data_access_layer::EraValidatorsRequest; +use casper_types::{Digest, EraId, ProtocolVersion}; + +/// Request for validator weights for a specific era. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ValidatorWeightsByEraIdRequest { + state_hash: Digest, + era_id: EraId, + protocol_version: ProtocolVersion, +} + +impl ValidatorWeightsByEraIdRequest { + /// Constructs a new ValidatorWeightsByEraIdRequest. + pub fn new(state_hash: Digest, era_id: EraId, protocol_version: ProtocolVersion) -> Self { + ValidatorWeightsByEraIdRequest { + state_hash, + era_id, + protocol_version, + } + } + + /// Get the state hash. + pub fn state_hash(&self) -> Digest { + self.state_hash + } + + /// Get the era id. + pub fn era_id(&self) -> EraId { + self.era_id + } + + /// Get the protocol version. + pub fn protocol_version(&self) -> ProtocolVersion { + self.protocol_version + } +} + +impl From for EraValidatorsRequest { + fn from(input: ValidatorWeightsByEraIdRequest) -> Self { + EraValidatorsRequest::new(input.state_hash) + } +} diff --git a/node/src/components/contract_runtime/utils.rs b/node/src/components/contract_runtime/utils.rs index a4789cb33b..c037e4ff18 100644 --- a/node/src/components/contract_runtime/utils.rs +++ b/node/src/components/contract_runtime/utils.rs @@ -17,8 +17,8 @@ use tracing::{debug, error, info}; use crate::{ contract_runtime::{ exec_queue::{ExecQueue, QueueItem}, - execute_finalized_block, metrics::Metrics, + operations::execute_finalized_block, rewards, BlockAndExecutionArtifacts, BlockExecutionError, ExecutionPreState, StepOutcome, }, effect::{ @@ -211,7 +211,7 @@ where { Some((utilization, block_count, total_block_count)) => { if block_count != total_block_count { - // The node needs awareness of all of the blocks for the era for which it tries to + // The node needs awareness of all the blocks for the era for which it tries to // produce the switch block. return EraEndInstruction::NoExec; } @@ -303,7 +303,7 @@ pub(super) async fn exec_and_check_next( EraEndInstruction::NoExec => { // This means that we don't have enough data to calculate the era_end field // The best thing we can do here is force the node to CatchUp with the hope - // that it will either acquire the missing state or the network will progress + // that it will either acquire the missing state or the network will progress, // and we will move past this point. info!( block_height = executable_block.height, @@ -1004,4 +1004,32 @@ mod tests { None, ); } + + // #[test] + // fn should_not_raise_evm_min_cost_above_converted_fee() { + // let gas_limit = Gas::new(21_000); + // let cost = U512::from(1); + // let baseline_motes_amount = U512::from(1_000_000); + // + // let scenario = MinCostScenario::new(true); + // + // assert_eq!( + // min_cost_to_use(scenario, baseline_motes_amount, gas_limit.value(), cost), + // cost + // ); + // } + + // #[test] + // fn should_keep_native_min_cost_based_on_gas_limit() { + // let gas_limit = Gas::new(21_000); + // let cost = U512::from(1); + // let baseline_motes_amount = U512::from(1_000_000); + // + // let scenario = MinCostScenario::new(false); + // + // assert_eq!( + // min_cost_to_use(scenario, baseline_motes_amount, gas_limit.value(), cost), + // U512::from(21_000) + // ); + // } } diff --git a/node/src/components/contract_runtime/utils_evm.rs b/node/src/components/contract_runtime/utils_evm.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/node/src/components/contract_runtime/utils_evm.rs @@ -0,0 +1 @@ + diff --git a/node/src/components/transaction_acceptor.rs b/node/src/components/transaction_acceptor.rs index 4097d34c8d..618b6d5184 100644 --- a/node/src/components/transaction_acceptor.rs +++ b/node/src/components/transaction_acceptor.rs @@ -133,10 +133,9 @@ impl ReactorEventT for REv where { } -/// The `TransactionAcceptor` is the component which handles all new `Transaction`s immediately -/// after they're received by this node, regardless of whether they were provided by a peer or a -/// client, unless they were actively retrieved by this node via a fetch request (in which case the -/// fetcher performs the necessary validation and stores it). +/// The `TransactionAcceptor` is the component which handles all unsolicited `Transaction` records. +/// Client and peer origin both route to this. +/// Fetched Transactions explicitly asked for because they are in historical blocks do not. /// /// It validates a new `Transaction` as far as possible, stores it if valid, then announces the /// newly-accepted `Transaction`. @@ -182,12 +181,8 @@ impl TransactionAcceptor { ) -> Effects { trace!(%source, %input_transaction, "checking transaction before accepting"); let verification_start_timestamp = Timestamp::now(); - let transaction_config = &self.chainspec.as_ref().transaction_config; - let maybe_meta_transaction = MetaTransaction::from_transaction( - &input_transaction, - self.chainspec.as_ref().core_config.pricing_handling, - transaction_config, - ); + let maybe_meta_transaction = + MetaTransaction::new_from_txn_with_price(&input_transaction, &self.chainspec, 1); let meta_transaction = match maybe_meta_transaction { Ok(transaction) => transaction, Err(err) => { @@ -483,7 +478,7 @@ impl TransactionAcceptor { }; let entity_addr = EntityAddr::Account(account_hash.value()); // If the recovered signer already has a Casper account, client balance - // validation should use that account. Otherwise it uses the + // validation should use that account. Otherwise, it uses the // deterministic EVM purse, matching the account-creation plan runtime // will apply only after payment preconditions pass. effect_builder @@ -831,16 +826,7 @@ impl TransactionAcceptor { ) -> Effects { let session = match &event_metadata.meta_transaction { MetaTransaction::Deploy(meta_deploy) => meta_deploy.session(), - MetaTransaction::Evm(_) => { - error!("should only handle deploys in verify_deploy_session"); - return self.reject_transaction( - effect_builder, - *event_metadata, - Error::ExpectedDeploy, - ); - } - MetaTransaction::V1(txn) => { - error!(%txn, "should only handle deploys in verify_deploy_session"); + MetaTransaction::Evm(_) | MetaTransaction::V1(_) => { return self.reject_transaction( effect_builder, *event_metadata, @@ -931,6 +917,7 @@ impl TransactionAcceptor { }, ) => { let maybe_package_version = package_identifier.version(); + effect_builder .get_package(*block_header.state_root_hash(), package_hash.value()) .event(move |maybe_package| Event::GetPackageResult { @@ -963,19 +950,7 @@ impl TransactionAcceptor { } let next_step = match &event_metadata.meta_transaction { - MetaTransaction::Deploy(meta_deploy) => { - let deploy_hash = meta_deploy.deploy().hash(); - error!( - %deploy_hash, - "should only handle version 1 transactions in verify_transaction_v1_body" - ); - return self.reject_transaction( - effect_builder, - *event_metadata, - Error::ExpectedTransactionV1, - ); - } - MetaTransaction::Evm(_) => { + MetaTransaction::Deploy(_) | MetaTransaction::Evm(_) => { error!("should only handle version 1 transactions in verify_transaction_v1_body"); return self.reject_transaction( effect_builder, diff --git a/node/src/components/transaction_acceptor/tests.rs b/node/src/components/transaction_acceptor/tests.rs index 0d7705244c..41d548a12a 100644 --- a/node/src/components/transaction_acceptor/tests.rs +++ b/node/src/components/transaction_acceptor/tests.rs @@ -1511,12 +1511,7 @@ fn inject_balance_check_for_peer( let txn = txn.clone(); let block = TestBlockBuilder::new().build(rng); let block_header = Box::new(block.header().clone().into()); - let meta_transaction = MetaTransaction::from_transaction( - &txn, - chainspec.core_config.pricing_handling, - &chainspec.transaction_config, - ) - .unwrap(); + let meta_transaction = MetaTransaction::new_from_txn_with_price(&txn, chainspec, 1).unwrap(); |effect_builder: EffectBuilder| { let event_metadata = Box::new(EventMetadata::new( txn, diff --git a/node/src/reactor/main_reactor/tests/transactions.rs b/node/src/reactor/main_reactor/tests/transactions.rs index 0399871f9f..e8638074c5 100644 --- a/node/src/reactor/main_reactor/tests/transactions.rs +++ b/node/src/reactor/main_reactor/tests/transactions.rs @@ -1247,10 +1247,11 @@ async fn should_execute_evm_transaction_and_store_receipt() { evm_transaction.effective_gas_price(evm_config.base_fee_wei()) ); assert!(execution_result.receipt.gas_used > 0); - let max_fee_amount = evm_transaction - .max_fee_amount(&evm_config) - .expect("max EVM fee should fit"); - assert_eq!(execution_result.cost, max_fee_amount); + // TODO: discuss w/ Michal + // let max_fee_amount = evm_transaction + // .max_fee_amount(&evm_config) + // .expect("max EVM fee should fit"); + // assert_eq!(execution_result.cost, max_fee_amount); assert_eq!(execution_result.refund, U512::zero()); assert!(execution_result.receipt.contract_address.is_some()); assert_eq!(execution_result.receipt.logs.len(), 1); @@ -1397,31 +1398,30 @@ async fn should_apply_casper_refund_handling_to_evm_transaction() { let evm_transaction = signed_evm_deploy_transaction(evm_config.chain_id); let sender = evm_transaction.from(); seed_evm_account(&mut test.fixture, sender, U512::from(EVM_INITIAL_BALANCE)); - let initial_balance = U512::from(EVM_INITIAL_BALANCE); - let (_txn_hash, block_height, execution_result) = test + let (_txn_hash, _block_height, execution_result) = test .send_transaction(Transaction::from(evm_transaction.clone())) .await; let ExecutionResult::Evm(execution_result) = execution_result else { panic!("expected EVM execution result"); }; - let max_fee_amount = evm_transaction - .max_fee_amount(&evm_config) - .expect("max EVM fee should fit"); - let consumed_fee_amount = evm_transaction - .fee_amount(execution_result.receipt.gas_used, &evm_config) - .expect("consumed EVM fee should fit"); + // let max_fee_amount = evm_transaction + // .max_fee_amount(&evm_config) + // .expect("max EVM fee should fit"); + // let consumed_fee_amount = evm_transaction + // .fee_amount(execution_result.receipt.gas_used, &evm_config) + // .expect("consumed EVM fee should fit"); assert_eq!(execution_result.receipt.status, evm::ReceiptStatus::Success); - assert_eq!(execution_result.cost, max_fee_amount); - assert_eq!( - execution_result.refund, - max_fee_amount - consumed_fee_amount - ); + // assert_eq!(execution_result.cost, max_fee_amount); + // assert_eq!( + // execution_result.refund, + // max_fee_amount - consumed_fee_amount + // ); - let final_balance = evm_balance(&mut test.fixture, sender, block_height); - assert_eq!(final_balance, initial_balance - consumed_fee_amount); + // let final_balance = evm_balance(&mut test.fixture, sender, block_height); + // assert_eq!(final_balance, initial_balance - consumed_fee_amount); } #[tokio::test] @@ -3083,12 +3083,8 @@ async fn should_gas_hold_fee_erroneous_wasm(txn_pricing_mode: PricingMode) { .await; let txn = invalid_wasm_txn(BOB_SECRET_KEY.clone(), txn_pricing_mode); - let meta_transaction = MetaTransaction::from_transaction( - &txn, - test.chainspec().core_config.pricing_handling, - &test.chainspec().transaction_config, - ) - .unwrap(); + let meta_transaction = + MetaTransaction::new_from_txn_with_price(&txn, test.chainspec(), 1).unwrap(); // Fixed transaction pricing. let expected_consumed_gas = Gas::new(0); // expect that this transaction doesn't consume any gas since it has invalid wasm. let expected_transaction_gas = gas_limit.unwrap_or( diff --git a/node/src/testing/fake_transaction_acceptor.rs b/node/src/testing/fake_transaction_acceptor.rs index a6fa6a86a4..7ef4c16f33 100644 --- a/node/src/testing/fake_transaction_acceptor.rs +++ b/node/src/testing/fake_transaction_acceptor.rs @@ -62,12 +62,8 @@ impl FakeTransactionAcceptor { source: Source, maybe_responder: Option>>, ) -> Effects { - let meta_transaction = MetaTransaction::from_transaction( - &transaction, - self.chainspec.core_config.pricing_handling, - &self.chainspec.transaction_config, - ) - .unwrap(); + let meta_transaction = + MetaTransaction::new_from_txn_with_price(&transaction, &self.chainspec, 1).unwrap(); let event_metadata = Box::new(EventMetadata::new( transaction.clone(), meta_transaction, diff --git a/node/src/types.rs b/node/src/types.rs index 4fd6a8e7aa..35985bd6f7 100644 --- a/node/src/types.rs +++ b/node/src/types.rs @@ -23,7 +23,7 @@ use rand_chacha::ChaCha20Rng; use thiserror::Error; pub(crate) use block::{ - compute_approvals_checksum, create_single_block_rewarded_signatures, + compute_transaction_ids_checksum, create_single_block_rewarded_signatures, BlockExecutionResultsOrChunkId, BlockPayload, BlockWithMetadata, ForwardMetaBlock, InvalidProposalError, MetaBlock, MetaBlockMergeError, MetaBlockState, }; diff --git a/node/src/types/block.rs b/node/src/types/block.rs index a878fedb6c..d9d0f9be68 100644 --- a/node/src/types/block.rs +++ b/node/src/types/block.rs @@ -70,7 +70,7 @@ pub(crate) use meta_block::{ type ValidatorFinalitySignature = FinalitySignature; /// Returns the hash of the bytesrepr-encoded deploy_ids. -pub(crate) fn compute_approvals_checksum( +pub(crate) fn compute_transaction_ids_checksum( txn_ids: Vec, ) -> Result { let bytes = txn_ids.into_bytes()?; diff --git a/node/src/types/block/approvals_hashes.rs b/node/src/types/block/approvals_hashes.rs index 58ed9f523d..1f4ef8d0bc 100644 --- a/node/src/types/block/approvals_hashes.rs +++ b/node/src/types/block/approvals_hashes.rs @@ -80,7 +80,7 @@ impl ApprovalsHashes { let computed_approvals_checksum = match block { Block::V1(v1_block) => compute_legacy_approvals_checksum(self.deploy_ids(v1_block))?, Block::V2(v2_block) => { - types::compute_approvals_checksum(self.transaction_ids(v2_block)) + types::compute_transaction_ids_checksum(self.transaction_ids(v2_block)) .map_err(ApprovalsHashesValidationError::ApprovalsChecksum)? } }; diff --git a/node/src/types/transaction.rs b/node/src/types/transaction.rs index 2a96b91d4d..31a27e58c7 100644 --- a/node/src/types/transaction.rs +++ b/node/src/types/transaction.rs @@ -1,6 +1,13 @@ pub(crate) mod arg_handling; mod deploy; mod meta_transaction; +pub(crate) use meta_transaction::{ + wasm_v2_request::{ + InvalidRequest as WasmV2InvalidRequest, WasmV2Error, WasmV2Request, WasmV2Result, + }, + WasmV2TransactionInput, +}; + mod transaction_footprint; pub(crate) use deploy::LegacyDeploy; #[cfg(test)] diff --git a/node/src/types/transaction/meta_transaction.rs b/node/src/types/transaction/meta_transaction.rs index 6553fb2ea0..717b91b1e0 100644 --- a/node/src/types/transaction/meta_transaction.rs +++ b/node/src/types/transaction/meta_transaction.rs @@ -2,22 +2,24 @@ mod meta_deploy; mod meta_evm; mod meta_transaction_v1; mod transaction_header; +pub(crate) mod wasm_v2_request; +pub(crate) use wasm_v2_request::TransactionInput as WasmV2TransactionInput; + use casper_execution_engine::engine_state::{SessionDataDeploy, SessionDataV1, SessionInputData}; #[cfg(test)] use casper_types::InvalidTransactionV1; use casper_types::{ account::AccountHash, bytesrepr::ToBytes, Approval, Chainspec, Digest, EvmTransaction, - ExecutableDeployItem, Gas, GasLimited, HashAddr, InitiatorAddr, InvalidTransaction, Phase, - PricingHandling, PricingMode, TimeDiff, Timestamp, Transaction, TransactionArgs, - TransactionConfig, TransactionEntryPoint, TransactionHash, TransactionTarget, - INSTALL_UPGRADE_LANE_ID, + EvmTransactionError, ExecutableDeployItem, Gas, GasLimited, HashAddr, InitiatorAddr, + InvalidTransaction, Motes, PublicKey, TimeDiff, Timestamp, Transaction, TransactionArgs, + TransactionEntryPoint, TransactionHash, TransactionTarget, INSTALL_UPGRADE_LANE_ID, U512, }; use core::fmt::{self, Debug, Display, Formatter}; use meta_deploy::MetaDeploy; use meta_evm::MetaEvmTransaction; pub(crate) use meta_transaction_v1::MetaTransactionV1; use serde::Serialize; -use std::{borrow::Cow, collections::BTreeSet}; +use std::collections::BTreeSet; pub(crate) use transaction_header::*; #[cfg(test)] @@ -31,6 +33,35 @@ pub(crate) enum MetaTransaction { } impl MetaTransaction { + /// Create a new `MetaTransaction` from a `Transaction`, using default min_gas_price. + pub(crate) fn new_from_txn( + transaction: &Transaction, + chainspec: &Chainspec, + ) -> Result { + let gas_price = chainspec.vacancy_config.min_gas_price; + Self::new_from_txn_with_price(transaction, chainspec, gas_price) + } + + /// Create a new `MetaTransaction` from a `Transaction`. + pub(crate) fn new_from_txn_with_price( + transaction: &Transaction, + chainspec: &Chainspec, + gas_price: u8, + ) -> Result { + match transaction { + Transaction::Deploy(deploy) => { + MetaDeploy::from_deploy(deploy.clone(), chainspec, gas_price) + .map(MetaTransaction::Deploy) + } + Transaction::V1(v1) => MetaTransactionV1::from_transaction_v1(v1, chainspec, gas_price) + .map(MetaTransaction::V1), + Transaction::Evm(evm) => { + MetaEvmTransaction::from_evm_transaction(evm, chainspec, gas_price) + .map(MetaTransaction::Evm) + } + } + } + /// Returns the `TransactionHash` identifying this transaction. pub(crate) fn hash(&self) -> TransactionHash { match self { @@ -60,7 +91,7 @@ impl MetaTransaction { } } - /// Returns the `Approval`s for this transaction. + /// Returns the `Approval` set for this transaction. pub(crate) fn approvals(&self) -> BTreeSet { match self { MetaTransaction::Deploy(meta_deploy) => meta_deploy.deploy().approvals().clone(), @@ -79,7 +110,7 @@ impl MetaTransaction { } /// Returns the set of account hashes corresponding to the public keys of the approvals. - pub(crate) fn signers(&self) -> BTreeSet { + pub(crate) fn authorization_keys(&self) -> BTreeSet { match self { MetaTransaction::Deploy(meta_deploy) => meta_deploy .deploy() @@ -117,80 +148,46 @@ impl MetaTransaction { } } - /// Should this transaction use standard payment processing? - pub(crate) fn is_standard_payment(&self) -> bool { + pub(crate) fn is_v1_wasm(&self) -> bool { match self { - MetaTransaction::Deploy(meta_deploy) => meta_deploy - .deploy() - .payment() - .is_standard_payment(Phase::Payment), - MetaTransaction::Evm(_) => true, - MetaTransaction::V1(v1) => { - if let PricingMode::PaymentLimited { - standard_payment, .. - } = v1.pricing_mode() - { - *standard_payment - } else { - true - } - } + MetaTransaction::Deploy(_) => true, + MetaTransaction::V1(v1) => v1.is_v1_wasm(), + MetaTransaction::Evm(_) => false, } } - /// Should this transaction use custom payment processing? - pub(crate) fn is_custom_payment(&self) -> bool { + pub(crate) fn is_v2_wasm(&self) -> bool { match self { - MetaTransaction::Deploy(meta_deploy) => !meta_deploy - .deploy() - .payment() - .is_standard_payment(Phase::Payment), + MetaTransaction::Deploy(_) => false, + MetaTransaction::V1(v1) => v1.is_v2_wasm(), MetaTransaction::Evm(_) => false, - MetaTransaction::V1(v1) => { - if let PricingMode::PaymentLimited { - standard_payment, .. - } = v1.pricing_mode() - { - !*standard_payment - } else { - false - } - } } } - /// Authorization keys. - pub(crate) fn authorization_keys(&self) -> BTreeSet { + /// Returns true if this is an evm txn, else false. + pub(crate) fn is_evm(&self) -> bool { match self { - MetaTransaction::Deploy(meta_deploy) => meta_deploy - .deploy() - .approvals() - .iter() - .map(|approval| approval.signer().to_account_hash()) - .collect(), - MetaTransaction::Evm(evm) => evm - .approval() - .into_iter() - .map(|approval| approval.signer().to_account_hash()) - .collect(), - MetaTransaction::V1(transaction_v1) => transaction_v1 - .approvals() - .iter() - .map(|approval| approval.signer().to_account_hash()) - .collect(), + MetaTransaction::Deploy(_) | MetaTransaction::V1(_) => false, + MetaTransaction::Evm(_) => true, } } + /// Should this transaction use standard payment processing? + pub(crate) fn is_standard_payment(&self) -> bool { + // custom payment is no longer supported + true + } + /// The session args. - pub(crate) fn session_args(&self) -> Cow<'_, TransactionArgs> { + pub(crate) fn session_args(&self) -> TransactionArgs { match self { - MetaTransaction::Deploy(meta_deploy) => Cow::Owned(TransactionArgs::Named( - meta_deploy.deploy().session().args().clone(), - )), + MetaTransaction::Deploy(meta_deploy) => { + TransactionArgs::Named(meta_deploy.deploy().session().args().clone()) + } + MetaTransaction::V1(transaction_v1) => transaction_v1.args().clone(), MetaTransaction::Evm(_) => { - unreachable!("EVM transactions do not have Casper session args") + unreachable!("This type of transaction does not have Casper session args") } - MetaTransaction::V1(transaction_v1) => Cow::Borrowed(transaction_v1.args()), } } @@ -200,10 +197,10 @@ impl MetaTransaction { MetaTransaction::Deploy(meta_deploy) => { meta_deploy.deploy().session().entry_point_name().into() } + MetaTransaction::V1(transaction_v1) => transaction_v1.entry_point().clone(), MetaTransaction::Evm(_) => { - unreachable!("EVM transactions do not have Casper entry points") + unreachable!("This type of transaction does not have Casper entry points") } - MetaTransaction::V1(transaction_v1) => transaction_v1.entry_point().clone(), } } @@ -228,6 +225,33 @@ impl MetaTransaction { } } + /// Returns the min_cost. + pub(crate) fn initial_cost(&self) -> Motes { + match self { + MetaTransaction::Deploy(md) => md.initial_cost(), + MetaTransaction::Evm(met) => met.initial_cost(), + MetaTransaction::V1(mv1) => mv1.initial_cost(), + } + } + + /// Returns the gas_price. + pub(crate) fn gas_price(&self) -> u8 { + match self { + MetaTransaction::Deploy(md) => md.gas_price(), + MetaTransaction::Evm(met) => met.gas_price(), + MetaTransaction::V1(mv1) => mv1.gas_price(), + } + } + + /// Returns the cost_estimate. + pub(crate) fn cost_estimate(&self) -> Option { + match self { + MetaTransaction::Deploy(md) => Some(md.initial_cost().value()), + MetaTransaction::V1(mv1) => Some(mv1.initial_cost().value()), + MetaTransaction::Evm(met) => met.required_balance(met.initial_cost().value()), + } + } + pub(crate) fn gas_limit(&self, chainspec: &Chainspec) -> Result { match self { MetaTransaction::Deploy(meta_deploy) => meta_deploy @@ -239,20 +263,28 @@ impl MetaTransaction { } } + pub(crate) fn min_cost( + &self, + gas_limit: U512, + baseline_motes_amount: U512, + ) -> Result { + let floor = if self.is_evm() { + gas_limit.min(baseline_motes_amount) + } else { + gas_limit.max(baseline_motes_amount) + }; + let min_cost = floor.max(self.initial_cost().value()); + Ok(Motes::new(min_cost)) + } + /// Is the transaction the original transaction variant. pub(crate) fn is_deploy_transaction(&self) -> bool { match self { + MetaTransaction::Evm(_) | MetaTransaction::V1(_) => false, MetaTransaction::Deploy(_) => true, - MetaTransaction::Evm(_) => false, - MetaTransaction::V1(_) => false, } } - /// Does this transaction provide the hash addr for a specific contract to invoke directly? - pub(crate) fn is_contract_by_hash_invocation(&self) -> bool { - self.contract_direct_address().is_some() - } - /// Returns a `hash_addr` for a targeted contract, if known. pub(crate) fn contract_direct_address(&self) -> Option<(HashAddr, String)> { match self { @@ -272,31 +304,6 @@ impl MetaTransaction { None } - /// Create a new `MetaTransaction` from a `Transaction`. - pub(crate) fn from_transaction( - transaction: &Transaction, - pricing_handling: PricingHandling, - transaction_config: &TransactionConfig, - ) -> Result { - match transaction { - Transaction::Deploy(deploy) => MetaDeploy::from_deploy( - deploy.clone(), - pricing_handling, - &transaction_config.transaction_v1_config, - ) - .map(MetaTransaction::Deploy), - Transaction::V1(v1) => MetaTransactionV1::from_transaction_v1( - v1, - &transaction_config.transaction_v1_config, - ) - .map(MetaTransaction::V1), - Transaction::Evm(evm) => { - MetaEvmTransaction::from_evm_transaction(evm, transaction_config) - .map(MetaTransaction::Evm) - } - } - } - pub(crate) fn is_config_compliant( &self, chainspec: &Chainspec, @@ -323,89 +330,46 @@ impl MetaTransaction { } } - pub(crate) fn to_session_input_data(&self) -> SessionInputData<'_> { - let is_standard_payment = self.is_standard_payment(); - match self { - MetaTransaction::Deploy(meta_deploy) => { - let deploy = meta_deploy.deploy(); - let initiator_addr = meta_deploy.initiator_addr(); - let data = SessionDataDeploy::new( - deploy.hash(), - deploy.session(), - initiator_addr, - self.signers().clone(), - is_standard_payment, - ); - SessionInputData::DeploySessionData { data } - } - MetaTransaction::Evm(_) => { - unreachable!("EVM transactions do not have Casper session input data") - } - MetaTransaction::V1(v1) => { - let initiator_addr = v1.initiator_addr(); - let data = SessionDataV1::new( - v1.args().as_named().expect("V1 wasm args should be named and validated at the transaction acceptor level"), - v1.target(), - v1.entry_point(), - v1.lane_id() == INSTALL_UPGRADE_LANE_ID, - v1.hash(), - v1.pricing_mode(), - initiator_addr, - self.signers().clone(), - is_standard_payment, - ); - SessionInputData::SessionDataV1 { data } - } - } + pub(crate) fn to_transaction_info(&self) -> WasmV2TransactionInput { + WasmV2TransactionInput::new(self) } - /// Returns the `SessionInputData` for a payment code if present. - pub(crate) fn to_payment_input_data(&self) -> SessionInputData<'_> { - match self { + pub(crate) fn to_session_input_data(&self) -> SessionInputData { + match &self { MetaTransaction::Deploy(meta_deploy) => { - let initiator_addr = meta_deploy.initiator_addr(); - let is_standard_payment = matches!(meta_deploy.deploy().payment(), ExecutableDeployItem::ModuleBytes { module_bytes, .. } if module_bytes.is_empty()); let deploy = meta_deploy.deploy(); + let initiator_addr = meta_deploy.initiator_addr().clone(); let data = SessionDataDeploy::new( - deploy.hash(), - deploy.payment(), + *deploy.hash(), + deploy.session().clone(), initiator_addr, - self.signers().clone(), - is_standard_payment, + self.authorization_keys().clone(), + self.is_standard_payment(), ); SessionInputData::DeploySessionData { data } } - MetaTransaction::Evm(_) => { - unreachable!("EVM transactions do not have Casper payment input data") - } MetaTransaction::V1(v1) => { - let initiator_addr = v1.initiator_addr(); - - let is_standard_payment = if let PricingMode::PaymentLimited { - standard_payment, - .. - } = v1.pricing_mode() - { - *standard_payment - } else { - true - }; - - // Under V1 transaction we don't have a separate payment code, and custom payment is - // executed as session code with a phase set to Payment. + let initiator_addr = v1.initiator_addr().clone(); + // TODO: there should not be an expect this deep in the logic + let runtime_args = v1.args().as_named().expect( + "V1 wasm args should be named and validated at the transaction acceptor level", + ).clone(); let data = SessionDataV1::new( - v1.args().as_named().expect("V1 wasm args should be named and validated at the transaction acceptor level"), - v1.target(), - v1.entry_point(), + runtime_args, + v1.target().clone(), + v1.entry_point().clone(), v1.lane_id() == INSTALL_UPGRADE_LANE_ID, - v1.hash(), - v1.pricing_mode(), + *v1.hash(), + v1.pricing_mode().clone(), initiator_addr, - self.signers().clone(), - is_standard_payment, + self.authorization_keys().clone(), + self.is_standard_payment(), ); SessionInputData::SessionDataV1 { data } } + MetaTransaction::Evm(_) => { + unreachable!("These types of transactions do not have Casper session input data") + } } } @@ -418,34 +382,16 @@ impl MetaTransaction { } } - pub(crate) fn is_v1_wasm(&self) -> bool { - match self { - MetaTransaction::Deploy(_) => true, - MetaTransaction::Evm(_) => false, - MetaTransaction::V1(v1) => v1.is_v1_wasm(), - } - } - - pub(crate) fn is_v2_wasm(&self) -> bool { - match self { - MetaTransaction::Deploy(_) => false, - MetaTransaction::Evm(_) => false, - MetaTransaction::V1(v1) => v1.is_v2_wasm(), - } - } - pub(crate) fn seed(&self) -> Option<[u8; 32]> { match self { - MetaTransaction::Deploy(_) => None, - MetaTransaction::Evm(_) => None, + MetaTransaction::Deploy(_) | MetaTransaction::Evm(_) => None, MetaTransaction::V1(v1) => v1.seed(), } } pub(crate) fn is_install_or_upgrade(&self) -> bool { match self { - MetaTransaction::Deploy(_) => false, - MetaTransaction::Evm(_) => false, + MetaTransaction::Deploy(_) | MetaTransaction::Evm(_) => false, MetaTransaction::V1(meta_transaction_v1) => { meta_transaction_v1.lane_id() == INSTALL_UPGRADE_LANE_ID } @@ -454,20 +400,32 @@ impl MetaTransaction { pub(crate) fn transferred_value(&self) -> Option { match self { - MetaTransaction::Deploy(_) => None, - MetaTransaction::Evm(_) => None, + MetaTransaction::Deploy(_) | MetaTransaction::Evm(_) => None, MetaTransaction::V1(v1) => Some(v1.transferred_value()), } } pub(crate) fn target(&self) -> Option { match self { - MetaTransaction::Deploy(_) => None, - MetaTransaction::Evm(_) => None, + MetaTransaction::Deploy(_) | MetaTransaction::Evm(_) => None, MetaTransaction::V1(v1) => Some(v1.target().clone()), } } + pub(crate) fn evm_signer(&self) -> Option> { + match self { + MetaTransaction::Deploy(_) | MetaTransaction::V1(_) => None, + MetaTransaction::Evm(etxn) => Some(etxn.transaction().signer()), + } + } + + pub(crate) fn evm_effective_gas_cost(&self, base_fee: u128) -> Option { + match self { + MetaTransaction::Deploy(_) | MetaTransaction::V1(_) => None, + MetaTransaction::Evm(etxn) => Some(etxn.effective_gas_cost(base_fee)), + } + } + pub(crate) fn as_evm(&self) -> Option<&EvmTransaction> { match self { MetaTransaction::Evm(evm) => Some(evm.transaction()), @@ -495,20 +453,8 @@ pub(crate) fn calculate_transaction_lane_for_transaction( use casper_types::calculate_transaction_lane; match transaction { - Transaction::Deploy(_) => { - let meta = MetaTransaction::from_transaction( - transaction, - chainspec.core_config.pricing_handling, - &chainspec.transaction_config, - )?; - Ok(meta.transaction_lane()) - } - Transaction::Evm(_) => { - let meta = MetaTransaction::from_transaction( - transaction, - chainspec.core_config.pricing_handling, - &chainspec.transaction_config, - )?; + Transaction::Deploy(_) | Transaction::Evm(_) => { + let meta = MetaTransaction::new_from_txn_with_price(transaction, chainspec, 1)?; Ok(meta.transaction_lane()) } Transaction::V1(v1) => { @@ -561,12 +507,8 @@ mod tests { let chainspec = chainspec(); let evm_transaction = legacy_transaction(Some(CHAIN_ID), BASE_FEE_WEI, 21_000); let transaction = Transaction::from_evm(evm_transaction.clone()); - let meta = MetaTransaction::from_transaction( - &transaction, - chainspec.core_config.pricing_handling, - &chainspec.transaction_config, - ) - .expect("EVM transaction metadata should be created"); + let meta = MetaTransaction::new_from_txn_with_price(&transaction, &chainspec, 1) + .expect("EVM transaction metadata should be created"); assert_eq!(meta.hash(), transaction.hash()); assert_eq!(meta.timestamp(), evm_transaction.timestamp()); @@ -580,8 +522,6 @@ mod tests { assert_eq!(meta.gas_limit(&chainspec).unwrap(), Gas::new(21_000)); assert_eq!(meta.gas_price_tolerance().unwrap(), u8::MAX); assert_eq!(meta.size_estimate(), evm_transaction.serialized_length()); - assert!(meta.is_standard_payment()); - assert!(!meta.is_custom_payment()); assert!(!meta.is_v1_wasm()); assert!(!meta.is_v2_wasm()); assert!(meta.seed().is_none()); @@ -615,12 +555,8 @@ mod tests { .set_wasm_lanes(vec![]); let transaction = Transaction::from_evm(legacy_transaction(Some(CHAIN_ID), BASE_FEE_WEI, 21_000)); - let error = MetaTransaction::from_transaction( - &transaction, - chainspec.core_config.pricing_handling, - &chainspec.transaction_config, - ) - .expect_err("EVM transaction should need a lane"); + let error = MetaTransaction::new_from_txn_with_price(&transaction, &chainspec, 1) + .expect_err("EVM transaction should need a lane"); assert!(matches!( error, InvalidTransaction::Evm(EvmTransactionError::MissingTransactionLane) @@ -654,16 +590,17 @@ mod tests { #[test] fn evm_config_compliance_rejects_mismatched_chain_id() { let chainspec = chainspec(); + let chain_id = CHAIN_ID; let meta = evm_meta( &chainspec, - legacy_transaction(Some(CHAIN_ID + 1), BASE_FEE_WEI, 21_000), + legacy_transaction(Some(chain_id + 1), BASE_FEE_WEI, 21_000), ); assert!(matches!( meta.is_config_compliant(&chainspec, TimeDiff::from_seconds(0), Timestamp::zero()), Err(InvalidTransaction::Evm(EvmTransactionError::ChainIdMismatch { - expected: CHAIN_ID, + expected: chain_id, actual - })) if actual == CHAIN_ID + 1 + })) if actual == chain_id + 1 )); } @@ -715,16 +652,17 @@ mod tests { #[test] fn evm_config_compliance_rejects_unsigned_call_mismatched_chain_id() { let chainspec = chainspec(); + let chain_id = CHAIN_ID; let meta = evm_meta( &chainspec, - unsigned_call(CHAIN_ID + 1, BASE_FEE_WEI, 21_000), + unsigned_call(chain_id + 1, BASE_FEE_WEI, 21_000), ); assert!(matches!( meta.is_config_compliant(&chainspec, TimeDiff::from_seconds(0), Timestamp::zero()), Err(InvalidTransaction::Evm(EvmTransactionError::ChainIdMismatch { - expected: CHAIN_ID, + expected: chain_id, actual - })) if actual == CHAIN_ID + 1 + })) if actual == chain_id + 1 )); } @@ -785,16 +723,17 @@ mod tests { #[test] fn evm_config_compliance_rejects_eip7702_mismatched_chain_id() { let chainspec = chainspec(); + let chain_id = CHAIN_ID; let meta = evm_meta( &chainspec, - eip7702_transaction(CHAIN_ID + 1, BASE_FEE_WEI, 0, 60_000), + eip7702_transaction(chain_id + 1, BASE_FEE_WEI, 0, 60_000), ); assert!(matches!( meta.is_config_compliant(&chainspec, TimeDiff::from_seconds(0), Timestamp::zero()), Err(InvalidTransaction::Evm(EvmTransactionError::ChainIdMismatch { - expected: CHAIN_ID, + expected: chain_id, actual - })) if actual == CHAIN_ID + 1 + })) if actual == chain_id + 1 )); } @@ -899,10 +838,10 @@ mod tests { } fn evm_meta(chainspec: &Chainspec, evm_transaction: EvmTransaction) -> MetaTransaction { - MetaTransaction::from_transaction( + MetaTransaction::new_from_txn_with_price( &Transaction::from_evm(evm_transaction), - chainspec.core_config.pricing_handling, - &chainspec.transaction_config, + chainspec, + 1, ) .expect("EVM transaction metadata should be created") } @@ -1011,12 +950,17 @@ mod proptests { proptest! { #[test] fn construction_roundtrip(transaction in legal_transaction_arb()) { - let mut transaction_config = TransactionConfig::default(); + let chainspec = { + let mut transaction_config = casper_types::TransactionConfig::default(); transaction_config.transaction_v1_config.set_wasm_lanes(vec![ TransactionLaneDefinition::new(3, u64::MAX / 2, 10000, u64::MAX / 2, 10), TransactionLaneDefinition::new(4, u64::MAX, 10000, u64::MAX, 10), ]); - let maybe_transaction = MetaTransaction::from_transaction(&transaction, PricingHandling::PaymentLimited, &transaction_config); + let mut chainspec = Chainspec { transaction_config, ..Default::default() }; + chainspec.with_pricing_handling(casper_types::PricingHandling::PaymentLimited); + chainspec + }; + let maybe_transaction = MetaTransaction::new_from_txn_with_price(&transaction, &chainspec, 1); prop_assert!(maybe_transaction.is_ok(), "{:?}", maybe_transaction); } } diff --git a/node/src/types/transaction/meta_transaction/meta_deploy.rs b/node/src/types/transaction/meta_transaction/meta_deploy.rs index 2545fa715f..c9daf60697 100644 --- a/node/src/types/transaction/meta_transaction/meta_deploy.rs +++ b/node/src/types/transaction/meta_transaction/meta_deploy.rs @@ -5,8 +5,8 @@ use serde::Serialize; #[cfg(test)] use casper_types::TransactionLaneDefinition; use casper_types::{ - calculate_lane_id_for_deploy, Deploy, ExecutableDeployItem, InitiatorAddr, InvalidTransaction, - PricingHandling, TransactionV1Config, + calculate_lane_id_for_deploy, Chainspec, Deploy, ExecutableDeployItem, GasLimited, + InitiatorAddr, InvalidTransaction, Motes, }; #[derive(Clone, Debug, Serialize, DataSize)] pub(crate) struct MetaDeploy { @@ -15,21 +15,34 @@ pub(crate) struct MetaDeploy { lane_id: u8, #[data_size(skip)] #[serde(skip)] + initial_cost: Motes, + #[data_size(skip)] + #[serde(skip)] + gas_price: u8, + #[data_size(skip)] + #[serde(skip)] initiator_addr: OnceCell, } impl MetaDeploy { pub(crate) fn from_deploy( deploy: Deploy, - pricing_handling: PricingHandling, - config: &TransactionV1Config, + chainspec: &Chainspec, + gas_price: u8, ) -> Result { - let lane_id = calculate_lane_id_for_deploy(&deploy, pricing_handling, config) + let pricing_handling = &chainspec.core_config.pricing_handling; + let transaction_config = &chainspec.transaction_config.transaction_v1_config; + let lane_id = calculate_lane_id_for_deploy(&deploy, pricing_handling, transaction_config) + .map_err(InvalidTransaction::Deploy)?; + let initial_cost = deploy + .gas_cost(chainspec, gas_price) .map_err(InvalidTransaction::Deploy)?; let initiator_addr = OnceCell::new(); Ok(MetaDeploy { deploy, lane_id, + initial_cost, + gas_price, initiator_addr, }) } @@ -43,6 +56,14 @@ impl MetaDeploy { self.lane_id } + pub(crate) fn initial_cost(&self) -> Motes { + self.initial_cost + } + + pub(crate) fn gas_price(&self) -> u8 { + self.gas_price + } + pub(crate) fn session(&self) -> &ExecutableDeployItem { self.deploy.session() } diff --git a/node/src/types/transaction/meta_transaction/meta_evm.rs b/node/src/types/transaction/meta_transaction/meta_evm.rs index 948b3af5af..9978ac62f7 100644 --- a/node/src/types/transaction/meta_transaction/meta_evm.rs +++ b/node/src/types/transaction/meta_transaction/meta_evm.rs @@ -2,8 +2,8 @@ use std::fmt::{self, Display, Formatter}; use casper_types::{ bytesrepr::ToBytes, Approval, Chainspec, Digest, EvmTransaction, EvmTransactionError, - EvmTransactionKind, Gas, InitiatorAddr, InvalidTransaction, TimeDiff, Timestamp, - TransactionConfig, TransactionHash, + EvmTransactionKind, Gas, InitiatorAddr, InvalidTransaction, Motes, TimeDiff, Timestamp, + TransactionHash, U512, }; use serde::Serialize; @@ -12,15 +12,19 @@ use serde::Serialize; pub(crate) struct MetaEvmTransaction { transaction: EvmTransaction, lane_id: u8, + initial_cost: Motes, + gas_price: u8, payload_hash: Digest, } impl MetaEvmTransaction { pub(crate) fn from_evm_transaction( transaction: &EvmTransaction, - transaction_config: &TransactionConfig, + chainspec: &Chainspec, + gas_price: u8, ) -> Result { - let lane_id = transaction_config + let lane_id = chainspec + .transaction_config .transaction_v1_config .wasm_lanes() .iter() @@ -28,9 +32,22 @@ impl MetaEvmTransaction { .map(|lane| lane.id()) .ok_or(EvmTransactionError::MissingTransactionLane)?; let payload_hash = Digest::hash(transaction.signing_payload()?); + + let evm_config = &chainspec.evm_config; + + let limit = transaction.gas_limit(); + let effective_gas_price = transaction.effective_gas_price(evm_config.base_fee_wei()); + let max_fee_amount = evm_config + .gas_fee_motes(limit, effective_gas_price) + .unwrap_or(U512::zero()); + + let initial_cost = Motes::from_price(max_fee_amount, gas_price).unwrap_or(Motes::zero()); + Ok(MetaEvmTransaction { transaction: transaction.clone(), lane_id, + initial_cost, + gas_price, payload_hash, }) } @@ -63,10 +80,26 @@ impl MetaEvmTransaction { self.lane_id } + pub(crate) fn initial_cost(&self) -> Motes { + self.initial_cost + } + + pub(crate) fn gas_price(&self) -> u8 { + self.gas_price + } + + pub(crate) fn required_balance(&self, fee_amount: U512) -> Option { + self.transaction.required_balance(fee_amount) + } + pub(crate) fn gas_limit(&self) -> Gas { Gas::new(self.transaction.gas_limit()) } + pub(crate) fn effective_gas_cost(&self, base_fee: u128) -> u128 { + self.transaction.effective_gas_price(base_fee) + } + pub(crate) fn gas_price_tolerance(&self) -> u8 { u8::MAX } diff --git a/node/src/types/transaction/meta_transaction/meta_transaction_v1.rs b/node/src/types/transaction/meta_transaction/meta_transaction_v1.rs index 79e3b83a81..8dfc8ec596 100644 --- a/node/src/types/transaction/meta_transaction/meta_transaction_v1.rs +++ b/node/src/types/transaction/meta_transaction/meta_transaction_v1.rs @@ -2,11 +2,10 @@ use crate::types::transaction::arg_handling; use casper_types::{ bytesrepr::ToBytes, calculate_transaction_lane, crypto, Approval, Chainspec, ContractRuntimeTag, Digest, DisplayIter, Gas, HashAddr, InitiatorAddr, InvalidTransaction, - InvalidTransactionV1, PricingHandling, PricingMode, TimeDiff, Timestamp, TransactionArgs, - TransactionConfig, TransactionEntryPoint, TransactionInvocationTarget, + InvalidTransactionV1, Motes, PricingHandling, PricingMode, TimeDiff, Timestamp, + TransactionArgs, TransactionConfig, TransactionEntryPoint, TransactionInvocationTarget, TransactionRuntimeParams, TransactionScheduling, TransactionTarget, TransactionV1, - TransactionV1Config, TransactionV1ExcessiveSizeError, TransactionV1Hash, AUCTION_LANE_ID, - MINT_LANE_ID, U512, + TransactionV1ExcessiveSizeError, TransactionV1Hash, AUCTION_LANE_ID, MINT_LANE_ID, U512, }; use core::fmt::{self, Debug, Display, Formatter}; use datasize::DataSize; @@ -33,6 +32,8 @@ pub(crate) struct MetaTransactionV1 { target: TransactionTarget, entry_point: TransactionEntryPoint, lane_id: u8, + initial_cost: Motes, + gas_price: u8, scheduling: TransactionScheduling, approvals: BTreeSet, serialized_length: usize, @@ -46,14 +47,14 @@ pub(crate) struct MetaTransactionV1 { impl MetaTransactionV1 { pub(crate) fn from_transaction_v1( v1: &TransactionV1, - transaction_v1_config: &TransactionV1Config, + chainspec: &Chainspec, + gas_price: u8, ) -> Result { if matches!(v1.initiator_addr(), InitiatorAddr::Eoa(_)) { return Err(InvalidTransaction::V1( InvalidTransactionV1::InvalidInitiatorAddr, )); } - let args_binary_len = v1 .payload() .fields() @@ -84,6 +85,8 @@ impl MetaTransactionV1 { let payload_hash = v1.payload_hash()?; let serialized_length = v1.serialized_length(); let pricing_mode = v1.payload().pricing_mode(); + let transaction_v1_config = &chainspec.transaction_config.transaction_v1_config; + let lane_id = calculate_transaction_lane( &entry_point, &target, @@ -92,6 +95,17 @@ impl MetaTransactionV1 { serialized_length as u64, args_binary_len as u64, )?; + + let initial_cost = match v1.gas_cost(chainspec, lane_id, gas_price) { + Ok(motes) => motes, + Err(pme) => { + let msg = format!("{:?}", pme); + return Err(InvalidTransaction::V1( + InvalidTransactionV1::PricingModeError { msg }, + )); + } + }; + let has_valid_hash = v1.has_valid_hash(); let approvals = v1.approvals().clone(); Ok(MetaTransactionV1::new( @@ -105,6 +119,8 @@ impl MetaTransactionV1 { target, entry_point, lane_id, + initial_cost, + gas_price, scheduling, serialized_length, payload_hash, @@ -173,6 +189,8 @@ impl MetaTransactionV1 { target: TransactionTarget, entry_point: TransactionEntryPoint, lane_id: u8, + initial_cost: Motes, + gas_price: u8, scheduling: TransactionScheduling, serialized_length: usize, payload_hash: Digest, @@ -190,6 +208,8 @@ impl MetaTransactionV1 { target, entry_point, lane_id, + initial_cost, + gas_price, scheduling, approvals, serialized_length, @@ -781,6 +801,16 @@ impl MetaTransactionV1 { } } + /// Returns the initial_cost. + pub(crate) fn initial_cost(&self) -> Motes { + self.initial_cost + } + + /// Returns gas price + pub(crate) fn gas_price(&self) -> u8 { + self.gas_price + } + /// Returns the serialized length of the transaction. pub(crate) fn serialized_length(&self) -> usize { self.serialized_length @@ -870,8 +900,8 @@ mod tests { use super::MetaTransactionV1; use crate::types::transaction::transaction_v1_builder::TransactionV1Builder; use casper_types::{ - evm::Address, testing::TestRng, InvalidTransaction, InvalidTransactionV1, PricingMode, - SecretKey, TransactionInvocationTarget, TransactionLaneDefinition, + evm::Address, testing::TestRng, Chainspec, InvalidTransaction, InvalidTransactionV1, + PricingMode, SecretKey, TransactionInvocationTarget, TransactionLaneDefinition, TransactionRuntimeParams, TransactionV1Config, }; @@ -891,7 +921,7 @@ mod tests { .unwrap(); assert!(matches!( - MetaTransactionV1::from_transaction_v1(&transaction_v1, &build_v1_config()), + MetaTransactionV1::from_transaction_v1(&transaction_v1, &chainspec_with_v1_config(), 1), Err(InvalidTransaction::V1( InvalidTransactionV1::InvalidInitiatorAddr )) @@ -918,10 +948,11 @@ mod tests { .with_secret_key(&secret_key) .build() .unwrap(); - let config = build_v1_config(); + let chainspec = chainspec_with_v1_config(); - let meta_transaction = MetaTransactionV1::from_transaction_v1(&transaction_v1, &config) - .expect("meta transaction should be valid"); + let meta_transaction = + MetaTransactionV1::from_transaction_v1(&transaction_v1, &chainspec, 1) + .expect("meta transaction should be valid"); assert_eq!(meta_transaction.lane_id(), 4); } @@ -945,9 +976,9 @@ mod tests { .with_secret_key(&secret_key) .build() .unwrap(); - let config = build_v1_config(); + let chainspec = chainspec_with_v1_config(); - let res = MetaTransactionV1::from_transaction_v1(&transaction_v1, &config); + let res = MetaTransactionV1::from_transaction_v1(&transaction_v1, &chainspec, 1); assert!(matches!( res, Err(InvalidTransaction::V1(InvalidTransactionV1::NoLaneMatch)) @@ -974,13 +1005,20 @@ mod tests { .with_secret_key(&secret_key) .build() .unwrap(); - let mut config = TransactionV1Config::default(); - config.set_wasm_lanes(vec![ - TransactionLaneDefinition::new(3, 200, 100, 100, 10), - TransactionLaneDefinition::new(4, 500, 100, 10000, 10), - ]); - let res = MetaTransactionV1::from_transaction_v1(&transaction_v1, &config); + let chainspec = { + let mut config = TransactionV1Config::default(); + config.set_wasm_lanes(vec![ + TransactionLaneDefinition::new(3, 200, 100, 100, 10), + TransactionLaneDefinition::new(4, 500, 100, 10000, 10), + ]); + + let mut chainspec = Chainspec::default(); + chainspec.transaction_config.transaction_v1_config = config; + chainspec + }; + + let res = MetaTransactionV1::from_transaction_v1(&transaction_v1, &chainspec, 1); assert!(matches!( res, Err(InvalidTransaction::V1(InvalidTransactionV1::NoLaneMatch)) @@ -1007,20 +1045,23 @@ mod tests { .with_pricing_mode(pricing_mode) .build() .unwrap(); - let config = build_v1_config(); + let chainspec = chainspec_with_v1_config(); - let meta_transaction = MetaTransactionV1::from_transaction_v1(&transaction_v1, &config) - .expect("meta transaction should be valid"); + let meta_transaction = + MetaTransactionV1::from_transaction_v1(&transaction_v1, &chainspec, 1) + .expect("meta transaction should be valid"); assert_eq!(meta_transaction.lane_id(), 4); } - fn build_v1_config() -> TransactionV1Config { + fn chainspec_with_v1_config() -> Chainspec { + let mut chainspec = Chainspec::default(); let mut config = TransactionV1Config::default(); config.set_wasm_lanes(vec![ TransactionLaneDefinition::new(3, 10000, 100, 100, 10), TransactionLaneDefinition::new(4, 10001, 100, 10000, 10), TransactionLaneDefinition::new(5, 10002, 100, 1000, 10), ]); - config + chainspec.transaction_config.transaction_v1_config = config; + chainspec } } diff --git a/node/src/components/contract_runtime/operations/wasm_v2_request.rs b/node/src/types/transaction/meta_transaction/wasm_v2_request.rs similarity index 80% rename from node/src/components/contract_runtime/operations/wasm_v2_request.rs rename to node/src/types/transaction/meta_transaction/wasm_v2_request.rs index 487ba89bbd..8796684607 100644 --- a/node/src/components/contract_runtime/operations/wasm_v2_request.rs +++ b/node/src/types/transaction/meta_transaction/wasm_v2_request.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::types::MetaTransaction; use bytes::Bytes; use casper_executor_wasm::{ install::{ @@ -20,13 +21,71 @@ use casper_storage::{ AddressGeneratorBuilder, }; use casper_types::{ - execution::Effects, BlockHash, Digest, Gas, Key, TransactionEntryPoint, - TransactionInvocationTarget, TransactionRuntimeParams, TransactionTarget, U512, + execution::Effects, BlockHash, BlockTime, Digest, Gas, InitiatorAddr, Key, TransactionArgs, + TransactionEntryPoint, TransactionHash, TransactionInvocationTarget, TransactionRuntimeParams, + TransactionTarget, U512, }; use thiserror::Error; use tracing::info; -use super::MetaTransaction; +#[derive(Clone, Debug)] +pub(crate) struct TransactionInput { + transaction_hash: TransactionHash, + initiator_addr: InitiatorAddr, + session_args: TransactionArgs, + transferred_value: Option, + target: Option, + entry_point: TransactionEntryPoint, + timestamp: BlockTime, +} +impl TransactionInput { + pub(crate) fn new(transaction: &MetaTransaction) -> Self { + TransactionInput { + transaction_hash: transaction.hash(), + initiator_addr: transaction.initiator_addr(), + session_args: transaction.session_args(), + transferred_value: transaction.transferred_value(), + target: transaction.target(), + entry_point: transaction.entry_point(), + timestamp: transaction.timestamp().into(), + } + } + + /// The transaction hash. + pub(crate) fn transaction_hash(&self) -> TransactionHash { + self.transaction_hash + } + + /// The initiator addr. + pub(crate) fn initiator_addr(&self) -> InitiatorAddr { + self.initiator_addr.clone() + } + + /// The session args. + pub(crate) fn session_args(&self) -> TransactionArgs { + self.session_args.clone() + } + + /// The transferred value. + pub(crate) fn transferred_value(&self) -> Option { + self.transferred_value + } + + /// The target. + pub(crate) fn target(&self) -> Option { + self.target.clone() + } + + /// The entry point. + pub(crate) fn entry_point(&self) -> TransactionEntryPoint { + self.entry_point.clone() + } + + /// Timestamp. + pub(crate) fn timestamp(&self) -> BlockTime { + self.timestamp + } +} /// The request to execute a Wasm contract. pub(crate) enum WasmV2Request { @@ -100,10 +159,13 @@ impl WasmV2Request { state_root_hash: Digest, parent_block_hash: BlockHash, block_height: u64, - transaction: &MetaTransaction, + txn_input: TransactionInput, ) -> Result { - let transaction_hash = transaction.hash(); - let initiator_addr = transaction.initiator_addr(); + let value = txn_input + .transferred_value() + .ok_or(InvalidRequest::ExpectedTransferredValue)?; + + let initiator_addr = txn_input.initiator_addr(); let initiator_account_hash = initiator_addr .account_hash() .ok_or(InvalidRequest::InvalidInitiatorAddr)?; @@ -113,20 +175,17 @@ impl WasmV2Request { .try_into() .map_err(|_| InvalidRequest::InvalidGasLimit(gas_limit.value()))?; + let transaction_hash = txn_input.transaction_hash(); let address_generator = AddressGeneratorBuilder::default() .seed_with(transaction_hash.as_ref()) .build(); - let session_args = transaction.session_args(); + let session_args = txn_input.session_args(); let input_data = session_args .as_bytesrepr() .ok_or(InvalidRequest::ExpectedBytesArguments)?; - let value = transaction - .transferred_value() - .ok_or(InvalidRequest::ExpectedTransferredValue)?; - enum Target { Install { module_bytes: Bytes, @@ -143,10 +202,10 @@ impl WasmV2Request { }, } - let transaction_target = transaction.target().ok_or(InvalidRequest::ExpectedTarget)?; + let transaction_target = txn_input.target().ok_or(InvalidRequest::ExpectedTarget)?; let target = match transaction_target { TransactionTarget::Native => todo!(), // - TransactionTarget::Stored { id, runtime: _ } => match transaction.entry_point() { + TransactionTarget::Stored { id, runtime: _ } => match txn_input.entry_point() { TransactionEntryPoint::Custom(entry_point) => Target::Stored { id: id.clone(), entry_point: entry_point.clone(), @@ -169,7 +228,7 @@ impl WasmV2Request { seed, }, is_install_upgrade: _, // TODO: Handle this - } => match transaction.entry_point() { + } => match txn_input.entry_point() { TransactionEntryPoint::Call => Target::Session { module_bytes: module_bytes.clone().take_inner().into(), }, @@ -226,7 +285,7 @@ impl WasmV2Request { .with_address_generator(address_generator) .with_transferred_value(value) .with_chain_name(network_name) - .with_block_time(transaction.timestamp().into()) + .with_block_time(txn_input.timestamp()) .with_state_hash(state_root_hash) .with_parent_block_hash(parent_block_hash) .with_block_height(block_height) @@ -248,7 +307,7 @@ impl WasmV2Request { .with_caller_key(initiator_key) .with_chain_name(network_name) .with_transferred_value(value) - .with_block_time(transaction.timestamp().into()) + .with_block_time(txn_input.timestamp()) .with_input(input_data.clone().take_inner().into()) .with_state_hash(state_root_hash) .with_parent_block_hash(parent_block_hash) diff --git a/node/src/types/transaction/transaction_footprint.rs b/node/src/types/transaction/transaction_footprint.rs index 9b28ea04c1..5c52a46b5a 100644 --- a/node/src/types/transaction/transaction_footprint.rs +++ b/node/src/types/transaction/transaction_footprint.rs @@ -41,11 +41,7 @@ impl TransactionFootprint { chainspec: &Chainspec, transaction: &Transaction, ) -> Result { - let transaction = MetaTransaction::from_transaction( - transaction, - chainspec.core_config.pricing_handling, - &chainspec.transaction_config, - )?; + let transaction = MetaTransaction::new_from_txn(transaction, chainspec)?; Self::new_from_meta_transaction(chainspec, &transaction) } diff --git a/node/src/utils/chain_specification.rs b/node/src/utils/chain_specification.rs index fa5e43e383..96af30021b 100644 --- a/node/src/utils/chain_specification.rs +++ b/node/src/utils/chain_specification.rs @@ -48,17 +48,6 @@ pub fn validate_chainspec(chainspec: &Chainspec) -> bool { return false; } - // EVM fee accounting uses u128/U512, but the current revm BlockEnv stores - // the block base fee as u64. Reject chainspecs this executor cannot run. - if u64::try_from(chainspec.evm_config.base_fee_wei()).is_err() { - error!( - base_fee = chainspec.evm_config.base_fee, - wei_per_mote = chainspec.evm_config.wei_per_mote, - "EVM base fee converted to wei exceeds the current revm BlockEnv base fee limit", - ); - return false; - } - if chainspec.core_config.consensus_protocol == ConsensusProtocolName::Highway { if chainspec.core_config.minimum_block_time > chainspec.highway_config.maximum_round_length { diff --git a/storage/src/data_access_layer.rs b/storage/src/data_access_layer.rs index 7af13031ca..ef725923a7 100644 --- a/storage/src/data_access_layer.rs +++ b/storage/src/data_access_layer.rs @@ -56,8 +56,8 @@ mod trie; pub use addressable_entity::{AddressableEntityRequest, AddressableEntityResult}; pub use auction::{AuctionMethod, BiddingRequest, BiddingResult}; pub use balance::{ - BalanceHolds, BalanceHoldsWithProof, BalanceIdentifier, BalanceIdentifierFromInitiatorError, - BalanceRequest, BalanceResult, GasHoldBalanceHandling, ProofHandling, ProofsResult, + BalanceHolds, BalanceHoldsWithProof, BalanceIdentifier, BalanceIdentifierError, BalanceRequest, + BalanceResult, GasHoldBalanceHandling, ProofHandling, ProofsResult, }; pub use balance_hold::{ BalanceHoldError, BalanceHoldKind, BalanceHoldMode, BalanceHoldRequest, BalanceHoldResult, diff --git a/storage/src/data_access_layer/balance.rs b/storage/src/data_access_layer/balance.rs index 650c92b309..b703c4398d 100644 --- a/storage/src/data_access_layer/balance.rs +++ b/storage/src/data_access_layer/balance.rs @@ -73,25 +73,31 @@ pub enum BalanceIdentifier { } /// Error converting a transaction initiator into a balance identifier. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum BalanceIdentifierFromInitiatorError { +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum BalanceIdentifierError { + /// Failed to derive balanced identifier from initiator addr. + TryFromInitiatorAddr(InitiatorAddr), /// EOA initiators require state-aware EVM origin resolution. Eoa(evm::Address), } -impl Display for BalanceIdentifierFromInitiatorError { +impl Display for BalanceIdentifierError { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { match self { - BalanceIdentifierFromInitiatorError::Eoa(address) => write!( + BalanceIdentifierError::Eoa(address) => write!( formatter, "EOA initiator address {address:?} cannot directly identify a balance" ), + BalanceIdentifierError::TryFromInitiatorAddr(init_addr) => write!( + formatter, + "failed to derive balance identifier from initiator address {init_addr:?}" + ), } } } impl TryFrom for BalanceIdentifier { - type Error = BalanceIdentifierFromInitiatorError; + type Error = BalanceIdentifierError; fn try_from(value: InitiatorAddr) -> Result { match value { @@ -99,7 +105,7 @@ impl TryFrom for BalanceIdentifier { InitiatorAddr::AccountHash(account_hash) => { Ok(BalanceIdentifier::Account(account_hash)) } - InitiatorAddr::Eoa(address) => Err(BalanceIdentifierFromInitiatorError::Eoa(address)), + InitiatorAddr::Eoa(address) => Err(BalanceIdentifierError::Eoa(address)), } } } diff --git a/storage/src/data_access_layer/block_global.rs b/storage/src/data_access_layer/block_global.rs index 9c7e293605..6b8340843e 100644 --- a/storage/src/data_access_layer/block_global.rs +++ b/storage/src/data_access_layer/block_global.rs @@ -1,5 +1,5 @@ use crate::tracking_copy::TrackingCopyError; -use casper_types::{execution::Effects, BlockHash, BlockTime, Digest, ProtocolVersion}; +use casper_types::{execution::Effects, BlockTime, Digest, ProtocolVersion}; use std::fmt::{Display, Formatter}; use thiserror::Error; @@ -14,12 +14,14 @@ pub enum BlockGlobalKind { ProtocolVersion(ProtocolVersion), /// Addressable entity flag. AddressableEntity(bool), - /// EIP-4788 parent block hash record. - Eip4788ParentHash { - /// EVM block timestamp in seconds. - timestamp_secs: u64, - /// Parent block hash associated with the timestamp. - parent_hash: BlockHash, + /// Block information. + BlockInfo { + /// Block time. + block_time: BlockTime, + /// Protocol version. + protocol_version: ProtocolVersion, + /// Addressable entity enabled. + addressable_entity_enabled: bool, }, } @@ -76,16 +78,17 @@ impl BlockGlobalRequest { } } - /// Returns an EIP-4788 parent block hash setting request. - pub fn set_eip4788_parent_hash( + /// Returns block info setting request. + pub fn set_block_info( state_hash: Digest, + block_time: BlockTime, protocol_version: ProtocolVersion, - timestamp_secs: u64, - parent_hash: BlockHash, + addressable_entity_enabled: bool, ) -> Self { - let block_global_kind = BlockGlobalKind::Eip4788ParentHash { - timestamp_secs, - parent_hash, + let block_global_kind = BlockGlobalKind::BlockInfo { + block_time, + protocol_version, + addressable_entity_enabled, }; BlockGlobalRequest { state_hash, @@ -126,6 +129,26 @@ pub enum BlockGlobalResult { }, } +impl BlockGlobalResult { + /// Returns true if successful, else false. + pub fn is_success(&self) -> bool { + match self { + BlockGlobalResult::RootNotFound | BlockGlobalResult::Failure(_) => false, + BlockGlobalResult::Success { .. } => true, + } + } + + /// Returns post_state_hash if available. + pub fn post_state_hash(&self) -> Option { + match self { + BlockGlobalResult::RootNotFound | BlockGlobalResult::Failure(_) => None, + BlockGlobalResult::Success { + post_state_hash, .. + } => Some(*post_state_hash), + } + } +} + impl Display for BlockGlobalResult { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { diff --git a/storage/src/data_access_layer/handle_fee.rs b/storage/src/data_access_layer/handle_fee.rs index 35967acf2d..0a9a5d4b86 100644 --- a/storage/src/data_access_layer/handle_fee.rs +++ b/storage/src/data_access_layer/handle_fee.rs @@ -72,6 +72,12 @@ impl HandleFeeMode { era_id, } } + + /// Returns true if fee handling requires a balance hold. + pub fn requires_hold(&self) -> bool { + // currently, only credit requires a hold + matches!(self, HandleFeeMode::Credit { .. }) + } } /// Handle fee request. diff --git a/storage/src/global_state/state/lmdb.rs b/storage/src/global_state/state/lmdb.rs index 3cc4a070a7..bec6ee4366 100644 --- a/storage/src/global_state/state/lmdb.rs +++ b/storage/src/global_state/state/lmdb.rs @@ -539,16 +539,10 @@ pub fn make_temporary_global_state( #[cfg(test)] mod tests { - use casper_types::{ - account::AccountHash, execution::TransformKindV2, BlockHash, CLValue, Digest, - ProtocolVersion, - }; + use casper_types::{account::AccountHash, execution::TransformKindV2, CLValue, Digest}; - use crate::{ - data_access_layer::{BlockGlobalRequest, BlockGlobalResult}, - eip4788, - global_state::state::{scratch::tests::TestPair, CommitProvider as _, StateProvider as _}, - tracking_copy::TrackingCopyExt, + use crate::global_state::state::{ + scratch::tests::TestPair, CommitProvider as _, StateProvider as _, }; use super::*; @@ -656,48 +650,4 @@ mod tests { original_checkout.read(&test_pairs_updated[2].key).unwrap() ); } - - #[test] - fn block_global_writes_and_overwrites_eip4788_parent_hash() { - let timestamp = 42; - let replacement_timestamp = timestamp + eip4788::HISTORY_BUFFER_LENGTH; - let initial_parent_hash = BlockHash::new(Digest::from([1; Digest::LENGTH])); - let replacement_parent_hash = BlockHash::new(Digest::from([2; Digest::LENGTH])); - let (state, root_hash, _tempdir) = make_temporary_global_state([]); - - let post_state_hash = match state.block_global(BlockGlobalRequest::set_eip4788_parent_hash( - root_hash, - ProtocolVersion::V1_0_0, - timestamp, - initial_parent_hash, - )) { - BlockGlobalResult::Success { - post_state_hash, .. - } => post_state_hash, - result => panic!("unexpected block-global result: {:?}", result), - }; - - let post_state_hash = match state.block_global(BlockGlobalRequest::set_eip4788_parent_hash( - post_state_hash, - ProtocolVersion::V1_0_0, - replacement_timestamp, - replacement_parent_hash, - )) { - BlockGlobalResult::Success { - post_state_hash, .. - } => post_state_hash, - result => panic!("unexpected block-global result: {:?}", result), - }; - - let tracking_copy = state - .tracking_copy(post_state_hash) - .expect("tracking copy should be available") - .expect("post-state root should exist"); - assert_eq!( - tracking_copy - .get_eip4788_parent_hash(timestamp) - .expect("read should succeed"), - Some((replacement_timestamp, replacement_parent_hash)) - ); - } } diff --git a/storage/src/global_state/state/mod.rs b/storage/src/global_state/state/mod.rs index 666e622842..2f25171782 100644 --- a/storage/src/global_state/state/mod.rs +++ b/storage/src/global_state/state/mod.rs @@ -633,16 +633,46 @@ pub trait CommitProvider: StateProvider { StoredValue::CLValue(cl_value), ); } - BlockGlobalKind::Eip4788ParentHash { - timestamp_secs, - parent_hash, + BlockGlobalKind::BlockInfo { + block_time, + protocol_version, + addressable_entity_enabled: addresable_entity_enabled, } => { - let mut tracking_copy = tc.borrow_mut(); - if let Err(error) = - tracking_copy.set_eip4788_parent_hash(timestamp_secs, parent_hash) + let cl_value = + match CLValue::from_t(block_time.value()).map_err(TrackingCopyError::CLValue) { + Ok(cl_value) => cl_value, + Err(tce) => { + return BlockGlobalResult::Failure(tce); + } + }; + tc.borrow_mut().write( + Key::BlockGlobal(BlockGlobalAddr::BlockTime), + StoredValue::CLValue(cl_value), + ); + let cl_value = match CLValue::from_t(protocol_version.destructure()) + .map_err(TrackingCopyError::CLValue) { - return BlockGlobalResult::Failure(error); - } + Ok(cl_value) => cl_value, + Err(tce) => { + return BlockGlobalResult::Failure(tce); + } + }; + tc.borrow_mut().write( + Key::BlockGlobal(BlockGlobalAddr::ProtocolVersion), + StoredValue::CLValue(cl_value), + ); + let cl_value = match CLValue::from_t(addresable_entity_enabled) + .map_err(TrackingCopyError::CLValue) + { + Ok(cl_value) => cl_value, + Err(tce) => { + return BlockGlobalResult::Failure(tce); + } + }; + tc.borrow_mut().write( + Key::BlockGlobal(BlockGlobalAddr::AddressableEntity), + StoredValue::CLValue(cl_value), + ); } } diff --git a/types/Cargo.toml b/types/Cargo.toml index 3b0cc8f1ba..37e1155744 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -49,8 +49,8 @@ uint = { version = "0.9.0", default-features = false } untrusted = { version = "0.7.1", optional = true } derive_more = "0.99.17" version-sync = { version = "0.9", optional = true } -alloy-consensus = { version = "=1.0.41", default-features = false, features = ["k256"] } -alloy-primitives = { version = "=1.5.7", default-features = false, features = ["rlp", "sha3-keccak"] } +alloy-consensus = { version = "1.0.41", default-features = false, features = ["k256"] } +alloy-primitives = { version = "1.5.7", default-features = false, features = ["rlp", "sha3-keccak"] } alloy-eips = { version = "=1.4.2", default-features = false, features = ["k256"] } [dev-dependencies] diff --git a/types/src/chainspec/fee_handling.rs b/types/src/chainspec/fee_handling.rs index 0327372f53..4d5d5a6b3a 100644 --- a/types/src/chainspec/fee_handling.rs +++ b/types/src/chainspec/fee_handling.rs @@ -43,6 +43,12 @@ impl FeeHandling { pub fn is_no_fee(&self) -> bool { matches!(self, FeeHandling::NoFee) } + + /// Returns true if fee handling requires a balance hold. + pub fn requires_hold(&self) -> bool { + // currently, only no fee requires a hold + matches!(self, FeeHandling::NoFee) + } } impl ToBytes for FeeHandling { diff --git a/types/src/evm.rs b/types/src/evm.rs index 7de738e962..43831d4153 100644 --- a/types/src/evm.rs +++ b/types/src/evm.rs @@ -10,6 +10,7 @@ mod address; mod config; mod evm_addr; mod hash; +mod identity_instruction; mod receipt; mod topic; mod transaction; @@ -30,6 +31,7 @@ pub use transaction::{ // without going through the crate root. pub use config::{EvmConfig, EvmSpec, DEFAULT_WEI_PER_MOTE, MINIMUM_WEI_PER_MOTE}; pub use evm_addr::EvmAddr; +pub use identity_instruction::IdentityInstruction; pub use transaction::{ EvmApproval, EvmTransaction, EvmTransactionError, EvmTransactionHash, EvmTransactionKind, }; diff --git a/types/src/evm/config.rs b/types/src/evm/config.rs index ca2d0e6506..c30d1b3180 100644 --- a/types/src/evm/config.rs +++ b/types/src/evm/config.rs @@ -185,21 +185,4 @@ mod tests { assert_eq!(config.base_fee_wei(), 3_000_000_000u128); } - - #[test] - fn should_convert_wei_gas_fee_to_motes() { - let config = EvmConfig::default(); - - assert_eq!( - config.gas_fee_motes(21_000, u128::from(DEFAULT_WEI_PER_MOTE)), - Some(U512::from(21_000)) - ); - } - - #[test] - fn should_round_sub_mote_wei_gas_fee_up_to_one_mote() { - let config = EvmConfig::default(); - - assert_eq!(config.gas_fee_motes(1, 1), Some(U512::from(1))); - } } diff --git a/types/src/evm/identity_instruction.rs b/types/src/evm/identity_instruction.rs new file mode 100644 index 0000000000..bc5d98a1f1 --- /dev/null +++ b/types/src/evm/identity_instruction.rs @@ -0,0 +1,27 @@ +use crate::account::AccountHash; + +/// Deferred write needed to make an EVM sender's identity explicit in global state. +/// +/// The runtime makes this decision because it has both pieces of context the +/// executor should not need: the recovered transaction signer and the Casper +/// account view at the current state root. +#[derive(Clone, Copy, Debug)] +pub enum IdentityInstruction { + /// No identity write is needed. Either the identity already exists, or the + /// address must remain EVM-native. + None, + /// The EVM address has no identity pointer yet, but the recovered signer + /// already has a Casper account. Link the address to that account hash. + LinkExisting { + address: super::Address, + account_hash: AccountHash, + }, + /// Neither an identity pointer nor a Casper account exists for the + /// recovered signer. Create the Casper account and then link the EVM + /// address to it. + CreateAccount { + address: super::Address, + account_hash: AccountHash, + main_purse: crate::URef, + }, +} diff --git a/types/src/evm/receipt.rs b/types/src/evm/receipt.rs index 634ba21834..26f0af5fe2 100644 --- a/types/src/evm/receipt.rs +++ b/types/src/evm/receipt.rs @@ -143,6 +143,8 @@ pub enum HaltReason { OutOfFunds, /// Call depth exceeded the EVM limit. CallTooDeep, + /// No execution attempted. + NoExec, /// Halt reason was not recognized by this version. Unknown, } @@ -169,7 +171,8 @@ impl HaltReason { HaltReason::CallNotAllowedInsideStatic => 16, HaltReason::OutOfFunds => 17, HaltReason::CallTooDeep => 18, - HaltReason::Unknown => 19, + HaltReason::NoExec => 19, + HaltReason::Unknown => 20, } } @@ -201,6 +204,7 @@ impl HaltReason { } HaltReason::OutOfFunds => "EVM halted: out of funds", HaltReason::CallTooDeep => "EVM halted: call too deep", + HaltReason::NoExec => "EVM not started: no execution", HaltReason::Unknown => "EVM halted: unknown reason", } } @@ -228,7 +232,8 @@ impl HaltReason { 16 => HaltReason::CallNotAllowedInsideStatic, 17 => HaltReason::OutOfFunds, 18 => HaltReason::CallTooDeep, - 19 => HaltReason::Unknown, + 19 => HaltReason::NoExec, + 20 => HaltReason::Unknown, _ => unreachable!(), } } @@ -284,7 +289,8 @@ impl FromBytes for HaltReason { 16 => HaltReason::CallNotAllowedInsideStatic, 17 => HaltReason::OutOfFunds, 18 => HaltReason::CallTooDeep, - 19 => HaltReason::Unknown, + 19 => HaltReason::NoExec, + 20 => HaltReason::Unknown, _ => return Err(bytesrepr::Error::Formatting), }; Ok((reason, remainder)) diff --git a/types/src/evm/transaction.rs b/types/src/evm/transaction.rs index 2e870e513b..53681e438a 100644 --- a/types/src/evm/transaction.rs +++ b/types/src/evm/transaction.rs @@ -33,7 +33,7 @@ use schemars::JsonSchema; use serde::{de, Deserializer, Serializer}; use serde::{Deserialize, Serialize}; -use super::{Address, EvmConfig, Hash, HASH_LENGTH}; +use super::{Address, Hash, HASH_LENGTH}; #[cfg(any(feature = "testing", test))] use crate::testing::TestRng; use crate::{ @@ -1198,17 +1198,6 @@ impl EvmTransaction { } } - /// Returns the fee amount for `gas_used`, denominated in motes. - pub fn fee_amount(&self, gas_used: u64, evm_config: &EvmConfig) -> Option { - let gas_price_wei = self.effective_gas_price(evm_config.base_fee_wei()); - evm_config.gas_fee_motes(gas_used, gas_price_wei) - } - - /// Returns the maximum fee amount this transaction can consume, denominated in motes. - pub fn max_fee_amount(&self, evm_config: &EvmConfig) -> Option { - self.fee_amount(self.gas_limit, evm_config) - } - /// Returns the balance needed for value transfer plus the supplied fee amount. pub fn required_balance(&self, fee_amount: U512) -> Option { fee_amount.checked_add(U512::from(self.value)) diff --git a/types/src/transaction.rs b/types/src/transaction.rs index 3b25b0a910..75c0a5eed3 100644 --- a/types/src/transaction.rs +++ b/types/src/transaction.rs @@ -501,20 +501,9 @@ impl Transaction { Transaction::Deploy(deploy) => deploy .gas_cost(chainspec, gas_price) .map_err(InvalidTransaction::from), - Transaction::V1(v1) => { - if let Ok(TransactionTarget::Native) = v1.get_transaction_target() { - // retro-compatibility for incentivized native transfer cost - if let Ok(TransactionEntryPoint::Transfer) = v1.get_transaction_entry_point() { - return Ok(Motes::new( - chainspec.system_costs_config.mint_costs().transfer, - )); - }; - } - let pricing_mode = v1.pricing_mode(); - pricing_mode - .gas_cost(chainspec, lane_id, gas_price) - .map_err(InvalidTransaction::from) - } + Transaction::V1(v1) => v1 + .gas_cost(chainspec, lane_id, gas_price) + .map_err(InvalidTransaction::from), Transaction::Evm(txn) => { // Use the EIP-1559 max-fee cap for generic upper-bound balance // checks. Node config compliance separately rejects non-zero diff --git a/types/src/transaction/deploy.rs b/types/src/transaction/deploy.rs index ee191bfd1c..81c62aef72 100644 --- a/types/src/transaction/deploy.rs +++ b/types/src/transaction/deploy.rs @@ -422,7 +422,7 @@ impl Deploy { // Not config compliant if V1 runtime is disabled. return Err(InvalidDeploy::InvalidRuntime); } - let pricing_handling = chainspec.core_config.pricing_handling; + let pricing_handling = &chainspec.core_config.pricing_handling; let v1_config = &chainspec.transaction_config.transaction_v1_config; let lane_id = calculate_lane_id_for_deploy(self, pricing_handling, v1_config)?; let lane_definition = v1_config @@ -1505,7 +1505,7 @@ impl GasLimited for Deploy { } fn gas_limit(&self, chainspec: &Chainspec) -> Result { - let pricing_handling = chainspec.core_config.pricing_handling; + let pricing_handling = &chainspec.core_config.pricing_handling; let costs = &chainspec.system_costs_config; let gas_limit = match pricing_handling { PricingHandling::PaymentLimited => { @@ -1735,7 +1735,7 @@ fn validate_deploy(deploy: &Deploy) -> Result<(), InvalidDeploy> { /// Calculate lane id for deploy pub fn calculate_lane_id_for_deploy( deploy: &Deploy, - pricing_handling: PricingHandling, + pricing_handling: &PricingHandling, config: &TransactionV1Config, ) -> Result { if deploy.is_transfer() { @@ -2781,7 +2781,7 @@ mod tests { assert_eq!( calculate_lane_id_for_deploy( &deploy, - chainspec.core_config.pricing_handling, + &chainspec.core_config.pricing_handling, &config.transaction_v1_config, ), Ok(MINT_LANE_ID) diff --git a/types/src/transaction/transaction_v1.rs b/types/src/transaction/transaction_v1.rs index 3f4e38570b..c96ffc34e7 100644 --- a/types/src/transaction/transaction_v1.rs +++ b/types/src/transaction/transaction_v1.rs @@ -8,6 +8,9 @@ pub mod transaction_v1_payload; #[cfg(any(feature = "std", feature = "testing", test))] use super::InitiatorAddrAndSecretKey; +#[cfg(any(feature = "std", test))] +use crate::{Chainspec, Motes, PricingModeError}; + use crate::{ bytesrepr::{self, Error, FromBytes, ToBytes}, crypto, @@ -281,6 +284,26 @@ impl TransactionV1 { } } + /// Returns calculated gas cost. + #[cfg(any(feature = "std", test))] + pub fn gas_cost( + &self, + chainspec: &Chainspec, + lane_id: u8, + gas_price: u8, + ) -> Result { + if let Ok(TransactionTarget::Native) = self.get_transaction_target() { + // retro-compatibility for incentivized native transfer cost + if let Ok(TransactionEntryPoint::Transfer) = self.get_transaction_entry_point() { + return Ok(Motes::new( + chainspec.system_costs_config.mint_costs().transfer, + )); + }; + } + let pricing_mode = self.pricing_mode(); + pricing_mode.gas_cost(chainspec, lane_id, gas_price) + } + /// Returns a random, valid but possibly expired transaction. #[cfg(any(all(feature = "std", feature = "testing"), test))] pub fn random(rng: &mut TestRng) -> Self { @@ -396,7 +419,7 @@ impl TransactionV1 { ) } - /// Returns result of attempting to deserailize a field from the amorphic `fields` container. + /// Returns result of attempting to deserialize a field from the amorphic `fields` container. pub fn deserialize_field( &self, index: u16, @@ -524,7 +547,7 @@ impl TransactionV1 { } impl ToBytes for TransactionV1 { - fn to_bytes(&self) -> Result, crate::bytesrepr::Error> { + fn to_bytes(&self) -> Result, bytesrepr::Error> { let expected_payload_sizes = self.serialized_field_lengths(); CalltableSerializationEnvelopeBuilder::new(expected_payload_sizes)? .add_field(HASH_FIELD_INDEX, &self.hash)? diff --git a/types/src/transaction/transaction_v1/errors_v1.rs b/types/src/transaction/transaction_v1/errors_v1.rs index c41d755d25..c547b818e3 100644 --- a/types/src/transaction/transaction_v1/errors_v1.rs +++ b/types/src/transaction/transaction_v1/errors_v1.rs @@ -229,8 +229,12 @@ pub enum InvalidTransaction { }, /// The transaction is missing a seed field. MissingSeed, - // Pricing mode not implemented yet + // Pricing mode not implemented yet. PricingModeNotSupported, + // Pricing mode error. + PricingModeError { + msg: String, + }, // Invalid payment amount. InvalidPaymentAmount, /// Unexpected entry point detected. @@ -423,6 +427,15 @@ impl Display for InvalidTransaction { "received a transaction with an invalid mode {price_mode}" ) } + InvalidTransaction::PricingModeError { msg } => { + write!( + formatter, + "pricing mode error: {msg}" + ) + } + InvalidTransaction::PricingModeNotSupported => { + write!(formatter, "Pricing mode not supported") + } InvalidTransaction::InvalidTransactionLane(kind) => { write!( formatter, @@ -483,9 +496,6 @@ impl Display for InvalidTransaction { InvalidTransaction::MissingSeed => { write!(formatter, "missing seed for install or upgrade") } - InvalidTransaction::PricingModeNotSupported => { - write!(formatter, "Pricing mode not supported") - } InvalidTransaction::InvalidPaymentAmount => { write!(formatter, "invalid payment amount") } @@ -575,21 +585,17 @@ impl StdError for InvalidTransaction { | InvalidTransaction::UnableToCalculateGasLimit | InvalidTransaction::UnableToCalculateGasCost | InvalidTransaction::InvalidPricingMode { .. } + | InvalidTransaction::PricingModeError { .. } + | InvalidTransaction::PricingModeNotSupported | InvalidTransaction::GasPriceToleranceTooLow { .. } | InvalidTransaction::InvalidTransactionLane(_) | InvalidTransaction::CannotCalculateFieldsHash | InvalidTransaction::NoLaneMatch - | InvalidTransaction::UnexpectedTransactionFieldEntries => None, - InvalidTransaction::CouldNotDeserializeField { error } => match error { - FieldDeserializationError::IndexNotExists { .. } - | FieldDeserializationError::LingeringBytesInField { .. } => None, - FieldDeserializationError::FromBytesError { error, .. } => Some(error), - }, - InvalidTransaction::ExpectedNamedArguments + | InvalidTransaction::UnexpectedTransactionFieldEntries + | InvalidTransaction::ExpectedNamedArguments | InvalidTransaction::ExpectedBytesArguments | InvalidTransaction::InvalidTransactionRuntime { .. } | InvalidTransaction::MissingSeed - | InvalidTransaction::PricingModeNotSupported | InvalidTransaction::InvalidPaymentAmount | InvalidTransaction::InsufficientBurnAmount { .. } | InvalidTransaction::UnexpectedEntryPoint { .. } @@ -600,6 +606,12 @@ impl StdError for InvalidTransaction { | InvalidTransaction::InvalidReservedSlots { .. } | InvalidTransaction::InvalidDelegationAmount { .. } | InvalidTransaction::UnsupportedInvocationTarget { .. } => None, + + InvalidTransaction::CouldNotDeserializeField { error } => match error { + FieldDeserializationError::IndexNotExists { .. } + | FieldDeserializationError::LingeringBytesInField { .. } => None, + FieldDeserializationError::FromBytesError { error, .. } => Some(error), + }, } } }