mirror of
https://github.com/akula-bft/akula.git
synced 2026-04-19 03:00:13 -04:00
Synchronous execution, unify tracer, misc optimize
This commit is contained in:
517
Cargo.lock
generated
517
Cargo.lock
generated
@@ -2,6 +2,21 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
@@ -28,7 +43,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"arrayvec 0.7.2",
|
||||
"async-recursion",
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -37,7 +52,8 @@ dependencies = [
|
||||
"bytes",
|
||||
"bytes-literal",
|
||||
"bytesize",
|
||||
"clap",
|
||||
"clap 3.0.14",
|
||||
"criterion",
|
||||
"croaring",
|
||||
"crossterm",
|
||||
"derive_more",
|
||||
@@ -60,6 +76,7 @@ dependencies = [
|
||||
"hex-literal",
|
||||
"http",
|
||||
"i256",
|
||||
"include_dir",
|
||||
"itertools",
|
||||
"jsonrpsee",
|
||||
"libmdbx",
|
||||
@@ -72,6 +89,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"parity-scale-codec 3.0.0",
|
||||
"parking_lot 0.12.0",
|
||||
"pprof",
|
||||
"proptest",
|
||||
"rand 0.8.4",
|
||||
"rayon",
|
||||
@@ -127,9 +145,17 @@ checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
source = "git+https://github.com/vorot93/arrayvec?branch=pop-unchecked#9e430a94359435dfe8e21467f160708ebcc01226"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -223,6 +249,21 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
@@ -314,7 +355,10 @@ version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -323,6 +367,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "byte-slice-cast"
|
||||
version = "1.2.0"
|
||||
@@ -338,6 +388,12 @@ dependencies = [
|
||||
"utf8-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -374,6 +430,15 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.72"
|
||||
@@ -422,6 +487,17 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"textwrap 0.11.0",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.14"
|
||||
@@ -436,7 +512,7 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
"textwrap 0.14.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -491,6 +567,15 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "cpp_demangle"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.1"
|
||||
@@ -509,6 +594,42 @@ dependencies = [
|
||||
"build_const",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"cast",
|
||||
"clap 2.34.0",
|
||||
"criterion-plot",
|
||||
"csv",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "croaring"
|
||||
version = "0.5.1"
|
||||
@@ -613,6 +734,28 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"csv-core",
|
||||
"itoa 0.4.8",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.1"
|
||||
@@ -648,6 +791,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debugid"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91cf5a8c2f2097e2a32627123508635d47ce10563d999ec1a95addf08b502ba"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
@@ -881,6 +1033,18 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "findshlibs"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed-hash"
|
||||
version = "0.7.0"
|
||||
@@ -1043,6 +1207,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.13.25"
|
||||
@@ -1094,6 +1264,12 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "hash-db"
|
||||
version = "0.15.2"
|
||||
@@ -1227,7 +1403,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "i256"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vorot93/rust-i256?branch=ethnum#e64fab8a4f9eed4b0acd25b0c83b290e6b633a3e"
|
||||
source = "git+https://github.com/vorot93/rust-i256?branch=ethnum-2#5e1b234879cb29ad0a753d2da4aea1dd6d9adc43"
|
||||
dependencies = [
|
||||
"ethnum",
|
||||
]
|
||||
@@ -1287,6 +1463,25 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "482a2e29200b7eed25d7fdbd14423326760b7f6658d21a4cf12d55a50713c69f"
|
||||
dependencies = [
|
||||
"include_dir_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "include_dir_macros"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e074c19deab2501407c91ba1860fa3d6820bfde307db6d8cb851b55a10be89b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.0"
|
||||
@@ -1297,6 +1492,24 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inferno"
|
||||
version = "0.10.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3886428c6400486522cf44b8626e7b94ad794c14390290f2a274dcf728a58f"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"atty",
|
||||
"indexmap",
|
||||
"itoa 1.0.1",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"num-format",
|
||||
"quick-xml",
|
||||
"rgb",
|
||||
"str_stack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@@ -1336,6 +1549,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpsee"
|
||||
version = "0.9.0"
|
||||
@@ -1354,7 +1576,7 @@ version = "0.9.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpsee#f66ef5517d7936d87cd72141963659fa6f3e4328"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
"arrayvec 0.7.2",
|
||||
"async-channel",
|
||||
"async-trait",
|
||||
"beef",
|
||||
@@ -1576,6 +1798,15 @@ version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
@@ -1591,6 +1822,16 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.7.14"
|
||||
@@ -1640,6 +1881,25 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.0"
|
||||
@@ -1671,6 +1931,16 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-format"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.12",
|
||||
"itoa 0.4.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
@@ -1700,12 +1970,27 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
@@ -1727,7 +2012,7 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"arrayvec 0.7.2",
|
||||
"byte-slice-cast",
|
||||
"impl-trait-for-tuples",
|
||||
"parity-scale-codec-derive 2.3.1",
|
||||
@@ -1739,7 +2024,7 @@ version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a7f3fcf5e45fc28b84dcdab6b983e77f197ec01f325a33f404ba6855afd1070"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"arrayvec 0.7.2",
|
||||
"bitvec",
|
||||
"byte-slice-cast",
|
||||
"bytes",
|
||||
@@ -1880,6 +2165,55 @@ version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
"plotters-svg",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9"
|
||||
dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pprof"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55f35f865aa964be21fcde114cbd1cfbd9bf8a471460ed965b0f84f96c711401"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"cfg-if",
|
||||
"findshlibs",
|
||||
"inferno",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"parking_lot 0.11.2",
|
||||
"smallvec",
|
||||
"symbolic-demangle",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
@@ -2028,6 +2362,15 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.15"
|
||||
@@ -2286,6 +2629,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd"
|
||||
version = "0.1.0"
|
||||
@@ -2327,6 +2679,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -2445,6 +2803,16 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||
dependencies = [
|
||||
"half",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
@@ -2612,12 +2980,24 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "str_stack"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb"
|
||||
|
||||
[[package]]
|
||||
name = "string"
|
||||
version = "0.3.0"
|
||||
@@ -2667,6 +3047,29 @@ dependencies = [
|
||||
"rustc-hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symbolic-common"
|
||||
version = "8.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e92a52f07eed9afba3d6f883652cde7cd75fcf327dd44e84f210958379158737"
|
||||
dependencies = [
|
||||
"debugid",
|
||||
"memmap2",
|
||||
"stable_deref_trait",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symbolic-demangle"
|
||||
version = "8.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9abc81544d9964975269165bfe5ad198d8b9e2e809c46527323f95588a57693"
|
||||
dependencies = [
|
||||
"cpp_demangle",
|
||||
"rustc-demangle",
|
||||
"symbolic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
@@ -2721,6 +3124,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.14.2"
|
||||
@@ -2775,6 +3187,16 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.5.1"
|
||||
@@ -2876,6 +3298,7 @@ version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -3092,6 +3515,12 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
@@ -3116,6 +3545,12 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
@@ -3188,6 +3623,70 @@ version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.2.4"
|
||||
|
||||
19
Cargo.toml
19
Cargo.toml
@@ -47,7 +47,7 @@ hash256-std-hasher = "0.15"
|
||||
hex = "0.4"
|
||||
hex-literal = "0.3"
|
||||
http = "0.2"
|
||||
i256 = { git = "https://github.com/vorot93/rust-i256", branch = "ethnum" }
|
||||
i256 = { git = "https://github.com/vorot93/rust-i256", branch = "ethnum-2" }
|
||||
itertools = "0.10"
|
||||
jsonrpsee = { git = "https://github.com/paritytech/jsonrpsee", features = [
|
||||
"server",
|
||||
@@ -83,7 +83,7 @@ tempfile = "3"
|
||||
thiserror = "1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = { version = "0.1", features = ["sync"] }
|
||||
toml = "0.5"
|
||||
toml = { version = "0.5", features = ["preserve_order"] }
|
||||
tonic = { version = "0.6", default-features = false, features = [
|
||||
"codegen",
|
||||
"prost",
|
||||
@@ -101,13 +101,18 @@ vergen = "6"
|
||||
|
||||
[dev-dependencies]
|
||||
bytes-literal = { git = "https://github.com/vorot93/bytes-literal" }
|
||||
criterion = { version = "0.3", default-features = false }
|
||||
include_dir = "0.7"
|
||||
pprof = { version = "0.6", features = ["flamegraph"] }
|
||||
proptest = "1.0.0"
|
||||
rand = { version = "0.8", features = ["std"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-test = "0.4.2"
|
||||
fdlimit = "0.2"
|
||||
|
||||
[patch.crates-io]
|
||||
ethnum = { git = "https://github.com/vorot93/ethnum-rs", branch = "staging" }
|
||||
arrayvec = { git = "https://github.com/vorot93/arrayvec", branch = "pop-unchecked" }
|
||||
|
||||
[[bin]]
|
||||
path = "bin/akula.rs"
|
||||
@@ -125,7 +130,17 @@ name = "akula-toolbox"
|
||||
path = "bin/consensus-tests.rs"
|
||||
name = "consensus-tests"
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
path = "./src/execution/evm/benches/bench.rs"
|
||||
harness = false
|
||||
|
||||
[profile.production]
|
||||
inherits = "release"
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[profile.bench]
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
consensus::*,
|
||||
execution::{analysis_cache::AnalysisCache, processor::ExecutionProcessor},
|
||||
execution::{analysis_cache::AnalysisCache, processor::ExecutionProcessor, tracer::NoopTracer},
|
||||
models::*,
|
||||
state::*,
|
||||
};
|
||||
@@ -135,9 +135,10 @@ impl<'state> Blockchain<'state> {
|
||||
let block_spec = self.config.collect_block_spec(block.header.number);
|
||||
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut tracer = NoopTracer;
|
||||
let processor = ExecutionProcessor::new(
|
||||
self.state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *self.engine,
|
||||
&block.header,
|
||||
|
||||
@@ -29,7 +29,7 @@ const IV: [u64; 8] = [
|
||||
0x5be0cd19137e2179,
|
||||
];
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1
|
||||
fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) {
|
||||
v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
|
||||
|
||||
@@ -20,8 +20,8 @@ impl AnalysisCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&mut self, code_hash: H256) -> Option<&AnalyzedCode> {
|
||||
self.inner.get(&code_hash)
|
||||
pub fn get(&mut self, code_hash: &H256) -> Option<&AnalyzedCode> {
|
||||
self.inner.get(code_hash)
|
||||
}
|
||||
|
||||
pub fn put(&mut self, code_hash: H256, code: AnalyzedCode) {
|
||||
|
||||
570
src/execution/evm/benches/bench.rs
Normal file
570
src/execution/evm/benches/bench.rs
Normal file
@@ -0,0 +1,570 @@
|
||||
use akula::{
|
||||
execution::evm::{
|
||||
instructions::{instruction_table::get_instruction_table, PROPERTIES},
|
||||
util::{mocked_host::MockedHost, Bytecode},
|
||||
AnalyzedCode, CallKind, InterpreterMessage, OpCode, Output, StatusCode,
|
||||
},
|
||||
models::Revision,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use criterion::{criterion_group, criterion_main, profiler::Profiler, BatchSize, Criterion};
|
||||
use ethereum_types::Address;
|
||||
use ethnum::*;
|
||||
use hex_literal::hex;
|
||||
use pprof::{flamegraph::Options, ProfilerGuard};
|
||||
use serde::Deserialize;
|
||||
use std::{fmt::Display, fs::File, os::raw::c_int, path::Path};
|
||||
|
||||
/// Small custom profiler that can be used with Criterion to create a flamegraph for benchmarks.
|
||||
/// Also see [the Criterion documentation on this][custom-profiler].
|
||||
///
|
||||
/// ## Example on how to enable the custom profiler:
|
||||
///
|
||||
/// ```
|
||||
/// mod perf;
|
||||
/// use perf::FlamegraphProfiler;
|
||||
///
|
||||
/// fn fibonacci_profiled(criterion: &mut Criterion) {
|
||||
/// // Use the criterion struct as normal here.
|
||||
/// }
|
||||
///
|
||||
/// fn custom() -> Criterion {
|
||||
/// Criterion::default().with_profiler(FlamegraphProfiler::new())
|
||||
/// }
|
||||
///
|
||||
/// criterion_group! {
|
||||
/// name = benches;
|
||||
/// config = custom();
|
||||
/// targets = fibonacci_profiled
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The neat thing about this is that it will sample _only_ the benchmark, and not other stuff like
|
||||
/// the setup process.
|
||||
///
|
||||
/// Further, it will only kick in if `--profile-time <time>` is passed to the benchmark binary.
|
||||
/// A flamegraph will be created for each individual benchmark in its report directory under
|
||||
/// `profile/flamegraph.svg`.
|
||||
///
|
||||
/// [custom-profiler]: https://bheisler.github.io/criterion.rs/book/user_guide/profiling.html#implementing-in-process-profiling-hooks
|
||||
pub struct FlamegraphProfiler<'a> {
|
||||
frequency: c_int,
|
||||
active_profiler: Option<ProfilerGuard<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FlamegraphProfiler<'a> {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(frequency: c_int) -> Self {
|
||||
FlamegraphProfiler {
|
||||
frequency,
|
||||
active_profiler: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Profiler for FlamegraphProfiler<'a> {
|
||||
fn start_profiling(&mut self, _benchmark_id: &str, _benchmark_dir: &Path) {
|
||||
self.active_profiler = Some(ProfilerGuard::new(self.frequency).unwrap());
|
||||
}
|
||||
|
||||
fn stop_profiling(&mut self, _benchmark_id: &str, benchmark_dir: &Path) {
|
||||
std::fs::create_dir_all(benchmark_dir).unwrap();
|
||||
let flamegraph_path = benchmark_dir.join("flamegraph.svg");
|
||||
let flamegraph_file = File::create(&flamegraph_path)
|
||||
.expect("File system error while creating flamegraph.svg");
|
||||
if let Some(profiler) = self.active_profiler.take() {
|
||||
let mut options = Options::default();
|
||||
options.reverse_stack_order = true;
|
||||
profiler
|
||||
.report()
|
||||
.build()
|
||||
.unwrap()
|
||||
.flamegraph_with_options(flamegraph_file, &mut options)
|
||||
.expect("Error writing flamegraph");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stack limit inside the EVM benchmark loop (one stack item is used for the loop counter).
|
||||
const STACK_LIMIT: usize = 1023;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Mode {
|
||||
/// The code uses as minimal stack as possible.
|
||||
MinStack = 0,
|
||||
/// The code fills the stack up to its limit.
|
||||
FullStack = 1,
|
||||
}
|
||||
|
||||
/// The instruction grouping by EVM stack requirements.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum InstructionCategory {
|
||||
/// No-op instruction.
|
||||
Nop,
|
||||
/// Nullary operator - produces a result without any stack input.
|
||||
Nullop,
|
||||
/// Unary operator.
|
||||
Unop,
|
||||
/// Binary operator.
|
||||
Binop,
|
||||
/// PUSH instruction.
|
||||
Push,
|
||||
/// DUP instruction.
|
||||
Dup,
|
||||
/// SWAP instruction.
|
||||
Swap,
|
||||
/// Not any of the categories above.
|
||||
Other,
|
||||
}
|
||||
|
||||
impl InstructionCategory {
|
||||
fn from_opcode(opcode: OpCode) -> Self {
|
||||
let t = PROPERTIES[opcode.to_usize()].unwrap();
|
||||
if opcode.to_u8() >= OpCode::PUSH1.to_u8() && opcode.to_u8() <= OpCode::PUSH32.to_u8() {
|
||||
Self::Push
|
||||
} else if opcode.to_u8() >= OpCode::SWAP1.to_u8()
|
||||
&& opcode.to_u8() <= OpCode::SWAP16.to_u8()
|
||||
{
|
||||
Self::Swap
|
||||
} else if opcode.to_u8() >= OpCode::DUP1.to_u8() && opcode.to_u8() <= OpCode::DUP16.to_u8()
|
||||
{
|
||||
Self::Dup
|
||||
} else if t.stack_height_required == 0 && t.stack_height_change == 0 {
|
||||
Self::Nop
|
||||
} else if t.stack_height_required == 0 && t.stack_height_change == 1 {
|
||||
Self::Nullop
|
||||
} else if t.stack_height_required == 1 && t.stack_height_change == 0 {
|
||||
Self::Unop
|
||||
} else if t.stack_height_required == 2 && t.stack_height_change == -1 {
|
||||
Self::Binop
|
||||
} else {
|
||||
Self::Other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for InstructionCategory {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Nop => 'n',
|
||||
Self::Nullop => 'a',
|
||||
Self::Unop => 'u',
|
||||
Self::Binop => 'b',
|
||||
Self::Push => 'p',
|
||||
Self::Dup => 'd',
|
||||
Self::Swap => 's',
|
||||
Self::Other => 'X',
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct CodeParams {
|
||||
opcode: OpCode,
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
impl Display for CodeParams {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}/{}{}",
|
||||
self.opcode.name(),
|
||||
self.opcode,
|
||||
self.mode as u8
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_loop_inner_code(params: &CodeParams) -> Bytecode {
|
||||
let &CodeParams { opcode, mode } = params;
|
||||
|
||||
let category = InstructionCategory::from_opcode(opcode);
|
||||
|
||||
match (mode, category) {
|
||||
(Mode::MinStack, InstructionCategory::Nop) => {
|
||||
// JUMPDEST JUMPDEST ...
|
||||
STACK_LIMIT * Bytecode::new().opcode(opcode).opcode(opcode)
|
||||
}
|
||||
(Mode::MinStack, InstructionCategory::Nullop) => {
|
||||
// CALLER POP CALLER POP ...
|
||||
STACK_LIMIT * Bytecode::new().opcode(opcode).opcode(OpCode::POP)
|
||||
}
|
||||
(Mode::MinStack, InstructionCategory::Unop) => {
|
||||
// DUP1 NOT NOT ... POP
|
||||
Bytecode::new()
|
||||
.opcode(OpCode::DUP1)
|
||||
.append_bc(STACK_LIMIT * Bytecode::new().opcode(opcode).opcode(opcode))
|
||||
.opcode(OpCode::POP)
|
||||
}
|
||||
(Mode::MinStack, InstructionCategory::Binop) => {
|
||||
// DUP1 DUP1 ADD DUP1 ADD DUP1 ADD ... POP
|
||||
Bytecode::new()
|
||||
.opcode(OpCode::DUP1)
|
||||
.append_bc((STACK_LIMIT - 1) * Bytecode::new().opcode(OpCode::DUP1).opcode(opcode))
|
||||
.opcode(OpCode::POP)
|
||||
}
|
||||
(Mode::MinStack, InstructionCategory::Push) => {
|
||||
// PUSH1 POP PUSH1 POP ...
|
||||
STACK_LIMIT
|
||||
* (Bytecode::new()
|
||||
.pushb(vec![0; (opcode.0 - OpCode::PUSH1.0) as usize + 1])
|
||||
.opcode(OpCode::POP))
|
||||
}
|
||||
(Mode::MinStack, InstructionCategory::Dup) => {
|
||||
// The required n stack height for DUPn is provided by
|
||||
// duplicating the loop counter n-1 times with DUP1.
|
||||
let n = opcode.to_usize() - OpCode::DUP1.to_usize() + 1;
|
||||
// DUP1 ... DUPn POP DUPn POP ... POP ...
|
||||
// \ n-1 / \ n-1 /
|
||||
(n - 1) * Bytecode::from(OpCode::DUP1) + // Required n stack height.
|
||||
(STACK_LIMIT - (n - 1)) * //
|
||||
(Bytecode::from(opcode) + OpCode::POP) + // Multiple DUPn POP pairs.
|
||||
(n - 1) * Bytecode::from(OpCode::POP) // Pop initially duplicated values.
|
||||
}
|
||||
(Mode::MinStack, InstructionCategory::Swap) => {
|
||||
// The required n+1 stack height for SWAPn is provided by duplicating the loop counter
|
||||
// n times with DUP1. This also guarantees the loop counter remains unchanged because
|
||||
// it is always going to be swapped to the same value.
|
||||
let n = opcode.to_usize() - OpCode::SWAP1.to_usize() + 1;
|
||||
// DUP1 ... SWAPn SWAPn ... POP ...
|
||||
// \ n / \ n /
|
||||
n * Bytecode::from(OpCode::DUP1) + // Required n+1 stack height.
|
||||
STACK_LIMIT * 2 * Bytecode::from(opcode) + // Multiple SWAPns.
|
||||
n * Bytecode::from(OpCode::POP) // Pop initially duplicated values.
|
||||
}
|
||||
(Mode::FullStack, InstructionCategory::Nullop) => {
|
||||
// CALLER CALLER ... POP POP ...
|
||||
STACK_LIMIT * Bytecode::from(opcode) + STACK_LIMIT * Bytecode::from(OpCode::POP)
|
||||
}
|
||||
(Mode::FullStack, InstructionCategory::Binop) => {
|
||||
// DUP1 DUP1 DUP1 ... ADD ADD ADD ... POP
|
||||
STACK_LIMIT * Bytecode::from(OpCode::DUP1)
|
||||
+ (STACK_LIMIT - 1) * Bytecode::new().opcode(opcode).opcode(OpCode::POP)
|
||||
}
|
||||
(Mode::FullStack, InstructionCategory::Push) => {
|
||||
// PUSH1 PUSH1 PUSH1 ... POP POP POP ...
|
||||
STACK_LIMIT * Bytecode::new().pushb(vec![0; (opcode.0 - OpCode::PUSH1.0) as usize + 1])
|
||||
+ STACK_LIMIT * Bytecode::from(OpCode::POP)
|
||||
}
|
||||
(Mode::FullStack, InstructionCategory::Dup) => {
|
||||
// The required initial n stack height for DUPn is provided by
|
||||
// duplicating the loop counter n-1 times with DUP1.
|
||||
let n = opcode.to_usize() - OpCode::DUP1.to_usize() + 1;
|
||||
// DUP1 ... DUPn DUPn ... POP POP ...
|
||||
// \ n-1 / \ S-(n-1) / \ S /
|
||||
(n - 1) * Bytecode::from(OpCode::DUP1) + // Required n stack height.
|
||||
(STACK_LIMIT - (n - 1)) * Bytecode::from(opcode) + // Fill the stack with DUPn.
|
||||
STACK_LIMIT * Bytecode::from(OpCode::POP) // Clear whole stack.
|
||||
}
|
||||
_ => Bytecode::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a benchmark loop with given inner code.
|
||||
///
|
||||
/// This generates do-while loop with 255 iterations and it starts with PUSH1 of 255 as the loop
|
||||
/// counter. The while check is done as `(counter += -1) != 0`. The SUB is avoided because it
|
||||
/// consumes arguments in unnatural order and additional SWAP would be required.
|
||||
///
|
||||
/// The loop counter stays on the stack top. The inner code is allowed to duplicate it, but must not
|
||||
/// modify it.
|
||||
fn generate_loop_v1(inner_code: Bytecode) -> Bytecode {
|
||||
let counter = Bytecode::new().pushv(255);
|
||||
let jumpdest_offset = counter.len();
|
||||
Bytecode::new()
|
||||
.append_bc(counter)
|
||||
// loop label + inner code
|
||||
.opcode(OpCode::JUMPDEST)
|
||||
.append_bc(inner_code)
|
||||
// -1
|
||||
.pushb(hex!(
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
))
|
||||
// counter += (-1)
|
||||
.opcode(OpCode::ADD)
|
||||
.opcode(OpCode::DUP1)
|
||||
.pushv(jumpdest_offset)
|
||||
// jump to jumpdest_offset if counter != 0
|
||||
.opcode(OpCode::JUMPI)
|
||||
}
|
||||
|
||||
/// Generates a benchmark loop with given inner code.
|
||||
///
|
||||
/// This is improved variant of v1. It has exactly the same instructions and consumes the same
|
||||
/// amount of gas, but according to performed benchmarks (see "loop_v1" and "loop_v2") it runs
|
||||
/// faster. And we want the lowest possible loop overhead.
|
||||
/// The change is to set the loop counter to -255 and check `(counter += 1) != 0`.
|
||||
fn generate_loop_v2(inner_code: Bytecode) -> Bytecode {
|
||||
// -255
|
||||
let counter = Bytecode::new().pushb(hex!(
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01"
|
||||
));
|
||||
let jumpdest_offset = counter.len();
|
||||
Bytecode::new()
|
||||
.append_bc(counter)
|
||||
// loop label + inner code
|
||||
.opcode(OpCode::JUMPDEST)
|
||||
.append_bc(inner_code)
|
||||
// counter += 1
|
||||
.pushv(1)
|
||||
.opcode(OpCode::ADD)
|
||||
.opcode(OpCode::DUP1)
|
||||
// jump to jumpdest_offset if counter != 0
|
||||
.pushv(jumpdest_offset)
|
||||
.opcode(OpCode::JUMPI)
|
||||
}
|
||||
|
||||
fn generate_code(params: &CodeParams) -> Bytecode {
|
||||
generate_loop_v2(generate_loop_inner_code(params))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn execute(
|
||||
(code, mut host, msg, rev): (AnalyzedCode, MockedHost, InterpreterMessage, Revision),
|
||||
) -> Output {
|
||||
code.execute(&mut host, &msg, rev)
|
||||
}
|
||||
|
||||
fn synthetic_benchmarks(c: &mut Criterion) {
|
||||
let mut params_list = vec![];
|
||||
|
||||
// Nops & unops.
|
||||
for opcode in [OpCode::JUMPDEST, OpCode::ISZERO, OpCode::NOT] {
|
||||
params_list.push(CodeParams {
|
||||
opcode,
|
||||
mode: Mode::MinStack,
|
||||
});
|
||||
}
|
||||
|
||||
// Binops.
|
||||
for opcode in [
|
||||
OpCode::ADD,
|
||||
OpCode::MUL,
|
||||
OpCode::SUB,
|
||||
OpCode::SIGNEXTEND,
|
||||
OpCode::LT,
|
||||
OpCode::GT,
|
||||
OpCode::SLT,
|
||||
OpCode::SGT,
|
||||
OpCode::EQ,
|
||||
OpCode::AND,
|
||||
OpCode::OR,
|
||||
OpCode::XOR,
|
||||
OpCode::BYTE,
|
||||
OpCode::SHL,
|
||||
OpCode::SHR,
|
||||
OpCode::SAR,
|
||||
] {
|
||||
for mode in [Mode::MinStack, Mode::FullStack] {
|
||||
params_list.push(CodeParams { opcode, mode });
|
||||
}
|
||||
}
|
||||
|
||||
// Nullops.
|
||||
for opcode in [
|
||||
OpCode::ADDRESS,
|
||||
OpCode::CALLER,
|
||||
OpCode::CALLVALUE,
|
||||
OpCode::CALLDATASIZE,
|
||||
OpCode::CODESIZE,
|
||||
OpCode::RETURNDATASIZE,
|
||||
OpCode::PC,
|
||||
OpCode::MSIZE,
|
||||
OpCode::GAS,
|
||||
] {
|
||||
for mode in [Mode::MinStack, Mode::FullStack] {
|
||||
params_list.push(CodeParams { opcode, mode });
|
||||
}
|
||||
}
|
||||
|
||||
// PUSH.
|
||||
for opcode in OpCode::PUSH1.to_u8()..OpCode::PUSH32.to_u8() {
|
||||
for mode in [Mode::MinStack, Mode::FullStack] {
|
||||
params_list.push(CodeParams {
|
||||
opcode: OpCode(opcode),
|
||||
mode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// SWAP.
|
||||
for opcode in OpCode::SWAP1.to_u8()..OpCode::SWAP16.to_u8() {
|
||||
params_list.push(CodeParams {
|
||||
opcode: OpCode(opcode),
|
||||
mode: Mode::MinStack,
|
||||
});
|
||||
}
|
||||
|
||||
// DUP.
|
||||
for opcode in OpCode::DUP1.to_u8()..OpCode::DUP16.to_u8() {
|
||||
for mode in [Mode::MinStack, Mode::FullStack] {
|
||||
params_list.push(CodeParams {
|
||||
opcode: OpCode(opcode),
|
||||
mode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
c.bench_function("/total/synth/loop_v1", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
prepare(
|
||||
AnalyzedCode::analyze(&generate_loop_v1(Bytecode::new()).build()),
|
||||
Bytes::new(),
|
||||
)
|
||||
},
|
||||
execute,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
c.bench_function("/total/synth/loop_v2", |b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
prepare(
|
||||
AnalyzedCode::analyze(&generate_loop_v2(Bytecode::new()).build()),
|
||||
Bytes::new(),
|
||||
)
|
||||
},
|
||||
execute,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
for params in params_list {
|
||||
c.bench_function(
|
||||
&format!(
|
||||
"/total/synth/{}/{}{}",
|
||||
params.opcode,
|
||||
InstructionCategory::from_opcode(params.opcode),
|
||||
params.mode as usize
|
||||
),
|
||||
|b| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
prepare(
|
||||
AnalyzedCode::analyze(&generate_code(¶ms).build()),
|
||||
Bytes::new(),
|
||||
)
|
||||
},
|
||||
execute,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare(
|
||||
code: AnalyzedCode,
|
||||
input_data: Bytes,
|
||||
) -> (AnalyzedCode, MockedHost, InterpreterMessage, Revision) {
|
||||
get_instruction_table(Revision::Istanbul);
|
||||
(
|
||||
code,
|
||||
MockedHost::default(),
|
||||
InterpreterMessage {
|
||||
kind: CallKind::Call,
|
||||
gas: i64::MAX,
|
||||
is_static: false,
|
||||
depth: 0,
|
||||
recipient: Address::zero(),
|
||||
code_address: Address::zero(),
|
||||
sender: Address::zero(),
|
||||
input_data,
|
||||
value: U256::ZERO,
|
||||
},
|
||||
Revision::Istanbul,
|
||||
)
|
||||
}
|
||||
|
||||
fn main_benchmarks(c: &mut Criterion) {
|
||||
#[derive(Deserialize)]
|
||||
struct Input {
|
||||
x: Option<usize>,
|
||||
c: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct BenchParams {
|
||||
args: Vec<Input>,
|
||||
out: String,
|
||||
}
|
||||
|
||||
for (name, code, args) in [
|
||||
(
|
||||
"snailtracer",
|
||||
include_str!("inputs/benchmarks/main/snailtracer.evm"),
|
||||
include_str!("inputs/benchmarks/main/snailtracer.toml"),
|
||||
),
|
||||
(
|
||||
"blake2b_huff",
|
||||
include_str!("inputs/benchmarks/main/blake2b_huff.evm"),
|
||||
include_str!("inputs/benchmarks/main/blake2b_huff.toml"),
|
||||
),
|
||||
(
|
||||
"blake2b_shifts",
|
||||
include_str!("inputs/benchmarks/main/blake2b_shifts.evm"),
|
||||
include_str!("inputs/benchmarks/main/blake2b_shifts.toml"),
|
||||
),
|
||||
(
|
||||
"sha1_divs",
|
||||
include_str!("inputs/benchmarks/main/sha1_divs.evm"),
|
||||
include_str!("inputs/benchmarks/main/sha1_divs.toml"),
|
||||
),
|
||||
(
|
||||
"sha1_shifts",
|
||||
include_str!("inputs/benchmarks/main/sha1_shifts.evm"),
|
||||
include_str!("inputs/benchmarks/main/sha1_shifts.toml"),
|
||||
),
|
||||
(
|
||||
"weierstrudel",
|
||||
include_str!("inputs/benchmarks/main/weierstrudel.evm"),
|
||||
include_str!("inputs/benchmarks/main/weierstrudel.toml"),
|
||||
),
|
||||
] {
|
||||
let code = hex::decode(code).unwrap();
|
||||
|
||||
let bench_vars = toml::from_str::<toml::value::Table>(args).unwrap();
|
||||
for (bench, params) in bench_vars {
|
||||
let params = params.try_into::<BenchParams>().unwrap();
|
||||
let mut input_data = Vec::new();
|
||||
for arg in params.args {
|
||||
let b = hex::decode(arg.c).unwrap();
|
||||
for _ in 0..arg.x.unwrap_or(1) {
|
||||
input_data.extend_from_slice(&b);
|
||||
}
|
||||
}
|
||||
|
||||
let expected_output = hex::decode(¶ms.out).unwrap();
|
||||
let analyzed_code = AnalyzedCode::analyze(&*code);
|
||||
let input_data = Bytes::from(input_data);
|
||||
|
||||
let res = execute(prepare(analyzed_code.clone(), input_data.clone()));
|
||||
|
||||
assert_eq!(res.status_code, StatusCode::Success);
|
||||
assert_eq!(res.output_data, expected_output);
|
||||
|
||||
c.bench_function(&format!("{}/{}", name, bench), move |b| {
|
||||
let analyzed_code = analyzed_code.clone();
|
||||
let input_data = input_data.clone();
|
||||
b.iter_batched(
|
||||
move || prepare(analyzed_code.clone(), input_data.clone()),
|
||||
execute,
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
name = benches;
|
||||
config = Criterion::default().with_profiler(FlamegraphProfiler::new(100));
|
||||
targets = main_benchmarks, synthetic_benchmarks
|
||||
);
|
||||
|
||||
criterion_main!(benches);
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,33 @@
|
||||
[empty]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000" },
|
||||
]
|
||||
out = "9ab7a73a97a1a3031406b6c169634a9c06cfb81dec3323bb4de5ce6f4b7ca107de534442a7eaeafbaf366ccfdde1cb97d7c884e4344cd0a23039de71a56d630a"
|
||||
|
||||
[2805nulls]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000af5" },
|
||||
{ c = "00", x = 2805 },
|
||||
]
|
||||
out = "2a715625dd8e3b86dc5c3f88d229968e8db4ce2e2c257cf40c6cabbc9aa3e17dddf22739fd79e4bef38c8c2166ff374e407ab25000cba307dc160b6cbd49f1c5"
|
||||
|
||||
[5610nulls]
|
||||
args = [
|
||||
{ c = "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000015ea" },
|
||||
{ c = "00", x = 5610 },
|
||||
]
|
||||
out = "f6f72a7f2dfc944429a4a6ec4d1a1c7998c5dd1de8a0c6d21918787ff6651c979bc0d35e4b66d7005f0417b37a13f8669ecdd2897b2f30d85cd91a307e247825"
|
||||
|
||||
[8415nulls]
|
||||
args = [
|
||||
{ c = "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000020df" },
|
||||
{ c = "00", x = 8415 },
|
||||
]
|
||||
out = "ee058d30512191b6ddbf27cef0b8e582fb25c4f9cb0f5b87ae321e0487566d9a32f7e4a94af0578cecf7d5b976dccc7fa74214262aae14094216158957b81002"
|
||||
|
||||
[65536nulls]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000" },
|
||||
{ c = "00", x = 65536 },
|
||||
]
|
||||
out = "528b404cf7d409a0656aba45a037eb685a5e1c58c28dae8b15d8f29edea2d896928a5b0df2e72b9a8e6133f72002cb9e0a79407d0a28b857b1b0ad8e68774b14"
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,27 @@
|
||||
[2805nulls]
|
||||
args = [
|
||||
{ c = "d299dac00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000af5" },
|
||||
{ c = "00", x = 2805 },
|
||||
]
|
||||
out = "000000000000000000000000000000000000000000000000610f78eb767b35d60000000000000000000000000000000000000000000000008acb88982dd67191000000000000000000000000000000000000000000000000d96d8b16cbf058cb00000000000000000000000000000000000000000000000075e58196f211891e000000000000000000000000000000000000000000000000b86aeafcaeb2344600000000000000000000000000000000000000000000000088074269d31f0c89000000000000000000000000000000000000000000000000b8d0461382136a330000000000000000000000000000000000000000000000005246a9d5610d58c2"
|
||||
|
||||
[5610nulls]
|
||||
args = [
|
||||
{ c = "d299dac000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000015ea" },
|
||||
{ c = "00", x = 5610 },
|
||||
]
|
||||
out = "000000000000000000000000000000000000000000000000e24b661901100a9f000000000000000000000000000000000000000000000000e86c19106fc93aa3000000000000000000000000000000000000000000000000f3f772ede793c73c000000000000000000000000000000000000000000000000e2a620fb9c2542ce00000000000000000000000000000000000000000000000027dd4f1b786e9b6a00000000000000000000000000000000000000000000000072ee3bd30df788c4000000000000000000000000000000000000000000000000bbfdee69798ce72e0000000000000000000000000000000000000000000000001d2c903a1d9e92c7"
|
||||
|
||||
[8415nulls]
|
||||
args = [
|
||||
{ c = "d299dac000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000020df" },
|
||||
{ c = "00", x = 8415 },
|
||||
]
|
||||
out = "000000000000000000000000000000000000000000000000ff7720faeac09953000000000000000000000000000000000000000000000000a5ebd368781c3ddb000000000000000000000000000000000000000000000000626c172d3892a6dc000000000000000000000000000000000000000000000000768456d18531f95e000000000000000000000000000000000000000000000000e5504c9f0fa9a153000000000000000000000000000000000000000000000000a5f665b4bee1301f0000000000000000000000000000000000000000000000009358198b0f89274200000000000000000000000000000000000000000000000074b191b954d547e1"
|
||||
|
||||
[65536nulls]
|
||||
args = [
|
||||
{ c = "d299dac00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000" },
|
||||
{ c = "00", x = 65536 },
|
||||
]
|
||||
out = "000000000000000000000000000000000000000000000000224bf59a700688ba000000000000000000000000000000000000000000000000a36d6ae418d9314d000000000000000000000000000000000000000000000000880adeb791ab854e000000000000000000000000000000000000000000000000bb697bc853d6f4c2000000000000000000000000000000000000000000000000faf9797b9902103f000000000000000000000000000000000000000000000000f8aa7b92b7340ec900000000000000000000000000000000000000000000000095e64259dcdb2be7000000000000000000000000000000000000000000000000ab45d93d04b14cd5"
|
||||
@@ -0,0 +1 @@
|
||||
608060405234801561001057600080fd5b5060043610610047577c010000000000000000000000000000000000000000000000000000000060003504631605782b811461004c575b600080fd5b6100f26004803603602081101561006257600080fd5b81019060208101813564010000000081111561007d57600080fd5b82018360208201111561008f57600080fd5b803590602001918460018302840111640100000000831117156100b157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550610127945050505050565b604080517fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009092168252519081900360200190f35b60006040518251602084019350604067ffffffffffffffc0600183011601600982820310600181146101585761015f565b6040820191505b50776745230100efcdab890098badcfe001032547600c3d2e1f06101d0565b6000838310156101c9575080820151928290039260208410156101c9577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60208590036101000a0119165b9392505050565b60005b82811015610686576101e684828961017e565b85526101f684602083018961017e565b60208601526040818503106001811461020e57610217565b60808286038701535b506040830381146001811461022b57610239565b602086018051600887021790525b5060405b6080811015610339578581017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc88201517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08301517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff48401516002911891909218189081027ffffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffe1663800000009091047c010000000100000001000000010000000100000001000000010000000116179052600c0161023d565b5060805b61014081101561043a578581017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908201517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08301517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe88401516004911891909218189081027ffffffffcfffffffcfffffffcfffffffcfffffffcfffffffcfffffffcfffffffc1663400000009091047c03000000030000000300000003000000030000000300000003000000031617905260180161033d565b508160008060005b605081101561065c5760148104801561047257600181146104ae57600281146104e857600381146105275761055d565b6501000000000085046a0100000000000000000000860481186f01000000000000000000000000000000870416189350635a827999925061055d565b6501000000000085046f0100000000000000000000000000000086046a0100000000000000000000870418189350636ed9eba1925061055d565b6a010000000000000000000085046f010000000000000000000000000000008604818117650100000000008804169116179350638f1bbcdc925061055d565b6501000000000085046f0100000000000000000000000000000086046a010000000000000000000087041818935063ca62c1d692505b50601f770800000000000000000000000000000000000000000000008504168063ffffffe073080000000000000000000000000000000000000087041617905080840190508063ffffffff86160190508083019050807c0100000000000000000000000000000000000000000000000000000000600484028c0151040190507401000000000000000000000000000000000000000081026501000000000086041794506a0100000000000000000000633fffffff6a040000000000000000000087041663c00000006604000000000000880416170277ffffffff00ffffffff000000000000ffffffff00ffffffff861617945050600181019050610442565b5050509190910177ffffffff00ffffffff00ffffffff00ffffffff00ffffffff16906040016101d3565b506c0100000000000000000000000063ffffffff821667ffffffff000000006101008404166bffffffff0000000000000000620100008504166fffffffff000000000000000000000000630100000086041673ffffffff00000000000000000000000000000000640100000000870416171717170294505050505091905056fea165627a7a7230582083396642a98f6018c81ca24dc0c2af8e842bd33a6b8d7f08632dc1bc372e466a0029
|
||||
@@ -0,0 +1,33 @@
|
||||
[empty]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" },
|
||||
]
|
||||
out = "da39a3ee5e6b4b0d3255bfef95601890afd80709000000000000000000000000"
|
||||
|
||||
[1351]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000547" },
|
||||
{ c = "00", x = 1351 },
|
||||
]
|
||||
out = "c150bb7410841ea9231d01f36b504c7484c46317000000000000000000000000"
|
||||
|
||||
[2737]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000ab1" },
|
||||
{ c = "00", x = 2737 },
|
||||
]
|
||||
out = "22170b5185363658b8f74d5f4409064df25c2efa000000000000000000000000"
|
||||
|
||||
[5311]
|
||||
args = [
|
||||
{ c = "1605782b000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000014bf" },
|
||||
{ c = "00", x = 5311 },
|
||||
]
|
||||
out = "86c9614de84df44ba01bfd1dd68e869dc730af09000000000000000000000000"
|
||||
|
||||
[65536]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000010000" },
|
||||
{ c = "00", x = 65536 },
|
||||
]
|
||||
out = "1adc95bebe9eea8c112d40cd04ab7a8d75c4f961000000000000000000000000"
|
||||
@@ -0,0 +1 @@
|
||||
608060405234801561001057600080fd5b506004361061002b5760003560e01c80631605782b14610030575b600080fd5b6100d66004803603602081101561004657600080fd5b81019060208101813564010000000081111561006157600080fd5b82018360208201111561007357600080fd5b8035906020019184600183028401116401000000008311171561009557600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506100f8945050505050565b604080516bffffffffffffffffffffffff199092168252519081900360200190f35b60006040518251602084019350604067ffffffffffffffc06001830116016009828203106001811461012957610130565b6040820191505b50776745230100efcdab890098badcfe001032547600c3d2e1f0610183565b60008383101561017c5750808201519282900392602084101561017c5760001960208590036101000a0119165b9392505050565b60005b8281101561045c5761019984828961014f565b85526101a984602083018961014f565b6020860152604081850310600181146101c1576101ca565b60808286038701535b50604083038114600181146101de576101ee565b8460031b60208701511760208701525b5060405b608081101561027157858101603f19810151603719820151601f19830151600b1984015118911818600181901b7ffffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffe16601f9190911c7c010000000100000001000000010000000100000001000000010000000116179052600c016101f2565b5060805b6101408110156102f557858101607f19810151606f19820151603f1983015160171984015118911818600281901b7ffffffffcfffffffcfffffffcfffffffcfffffffcfffffffcfffffffcfffffffc16601e9190911c7c030000000300000003000000030000000300000003000000030000000316179052601801610275565b508160008060005b60508110156104325760148104801561032d576001811461034e576002811461036d5760038114610391576103ac565b602885901c605086901c8118607887901c16189350635a82799992506103ac565b8460501c8560781c189350838560281c189350636ed9eba192506103ac565b605085901c607886901c818117602888901c169116179350638f1bbcdc92506103ac565b8460501c8560781c189350838560281c18935063ca62c1d692505b50601f8460bb1c168063ffffffe086609b1c1617905080840190508063ffffffff86160190508083019050808260021b8b015160e01c0190508060a01b8560281c179450633fffffff8560521c1663c00000008660321c161760501b77ffffffff00ffffffff000000000000ffffffff00ffffffff8616179450506001810190506102fd565b5050509190910177ffffffff00ffffffff00ffffffff00ffffffff00ffffffff1690604001610186565b5063ffffffff811667ffffffff000000008260081c166bffffffff00000000000000008360101c166fffffffff0000000000000000000000008460181c1673ffffffff000000000000000000000000000000008560201c161717171760601b94505050505091905056fea165627a7a72305820227af8b272b9b0e3d345f580ebcde55f50e3e8b7ecafabffcadb92e55e4de68e0029
|
||||
@@ -0,0 +1,33 @@
|
||||
[empty]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" },
|
||||
]
|
||||
out = "da39a3ee5e6b4b0d3255bfef95601890afd80709000000000000000000000000"
|
||||
|
||||
[1351]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000547" },
|
||||
{ c = "00", x = 1351 },
|
||||
]
|
||||
out = "c150bb7410841ea9231d01f36b504c7484c46317000000000000000000000000"
|
||||
|
||||
[2737]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000ab1" },
|
||||
{ c = "00", x = 2737 },
|
||||
]
|
||||
out = "22170b5185363658b8f74d5f4409064df25c2efa000000000000000000000000"
|
||||
|
||||
[5311]
|
||||
args = [
|
||||
{ c = "1605782b000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000014bf" },
|
||||
{ c = "00", x = 5311 },
|
||||
]
|
||||
out = "86c9614de84df44ba01bfd1dd68e869dc730af09000000000000000000000000"
|
||||
|
||||
[65536]
|
||||
args = [
|
||||
{ c = "1605782b00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000010000" },
|
||||
{ c = "00", x = 65536 },
|
||||
]
|
||||
out = "1adc95bebe9eea8c112d40cd04ab7a8d75c4f961000000000000000000000000"
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
[benchmark]
|
||||
args = [{ c = "30627b7c" }]
|
||||
out = "190000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000006300000000000000000000000000000000000000000000000000000000000000"
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,34 @@
|
||||
[0]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" },
|
||||
{ c = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" },
|
||||
]
|
||||
out = "0267044364fafcc4179b3d9841976c5f9751085391a8d62ea2d85f3add49df212fd05e78e4d6069841dca813b82c477c70ff014564c72add2f3258355ae3900e1b237dbf02109b56a5d2e9bd7ac6d5613b4b685f39689ba531cc4383128a151f"
|
||||
|
||||
[1]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", x = 2 },
|
||||
{ c = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", x = 2 },
|
||||
]
|
||||
out = "23386b694c363dc9e2e2eee49aa5bf940e847fc66a54058e42b780c97d7e53630dbf2d60869bd617de2578e9ccd52c903ec669efd4219228e91c72407568ae2618b263f94f331dfebd495772e61d3517b404ce327c504b23f3b15af8f44bdfad"
|
||||
|
||||
[3]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", x = 4 },
|
||||
{ c = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", x = 4 },
|
||||
]
|
||||
out = "124f4bf2511c4ae2770b50b1ab956174bf87260d6ce8255c36ac44d8c354ceb3251e057af103a5b54eef7986550787979eb30384b77dffd8e57891c771553f4400c87e7d58a5ffc110788895ed1569b8b21bfac97502da2f38bd273c5046250a"
|
||||
|
||||
[9]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", x = 10 },
|
||||
{ c = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", x = 10 },
|
||||
]
|
||||
out = "1c895346881918d3b79f69a13cf38ba4f4377b3225edb88a93135f3b0ec986c20dc6f1ddba9b2573209c7f1165bf5eb03786ecadc30e130ff67c17fda6c4fb700fe7b15aad333f3b985a54c2aa523c9e9c4a2e4f53eb8abaa49668289b43147b"
|
||||
|
||||
[14]
|
||||
args = [
|
||||
{ c = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", x = 15 },
|
||||
{ c = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", x = 15 },
|
||||
]
|
||||
out = "0f7f8f15d02f4e47ac51fe38826c20f511253fe504557da98f0aa915cb21db9b1fbfa0a8ccd10005eba9e5f7d575e530baa14c9711e9dbedca27a79f01c0ddac16989a70b325764d40bb22072b810f8606c35e6af9116fbdac4bc53c081109d3"
|
||||
@@ -6,7 +6,7 @@ use strum_macros::Display;
|
||||
|
||||
/// Message status code.
|
||||
#[must_use]
|
||||
#[derive(Clone, Debug, Display, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Display, PartialEq)]
|
||||
pub enum StatusCode {
|
||||
/// Execution finished with success.
|
||||
#[strum(serialize = "success")]
|
||||
@@ -88,7 +88,7 @@ pub enum StatusCode {
|
||||
|
||||
/// EVM implementation generic internal error.
|
||||
#[strum(serialize = "internal error")]
|
||||
InternalError(String),
|
||||
InternalError(&'static str),
|
||||
}
|
||||
|
||||
/// The kind of call-like instruction.
|
||||
@@ -211,17 +211,17 @@ impl From<SuccessfulOutput> for Output {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn u256_to_address(v: U256) -> Address {
|
||||
H256(v.to_be_bytes()).into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn address_to_u256(v: Address) -> U256 {
|
||||
U256::from_be_bytes(H256::from(v).0)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn u256_from_slice(v: &[u8]) -> U256 {
|
||||
debug_assert!(v.len() <= 32, "invalid len");
|
||||
match v.len() {
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
macro_rules! interrupt {
|
||||
( $(#[$outer:meta])* $name:ident => $resume_with:ty) => {
|
||||
$(#[$outer])*
|
||||
pub struct $name {
|
||||
pub(crate) inner: InnerCoroutine,
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn resume(self, resume_data: $resume_with) -> Interrupt {
|
||||
resume_interrupt(self.inner, resume_data.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interrupt! {
|
||||
/// EVM has just been created. Resume this interrupt to start execution.
|
||||
StartedInterrupt => ()
|
||||
}
|
||||
interrupt! {
|
||||
/// New instruction has been encountered.
|
||||
InstructionStartInterrupt => StateModifier
|
||||
}
|
||||
interrupt! {
|
||||
/// Does this account exist?
|
||||
AccountExistsInterrupt => AccountExistsStatus
|
||||
}
|
||||
interrupt! {
|
||||
/// Need this storage key.
|
||||
GetStorageInterrupt => StorageValue
|
||||
}
|
||||
interrupt! {
|
||||
/// Set this storage key.
|
||||
SetStorageInterrupt => StorageStatusInfo
|
||||
}
|
||||
interrupt! {
|
||||
/// Get balance of this account.
|
||||
GetBalanceInterrupt => Balance
|
||||
}
|
||||
interrupt! {
|
||||
/// Get code size of this account.
|
||||
GetCodeSizeInterrupt => CodeSize
|
||||
}
|
||||
interrupt! {
|
||||
/// Get code hash of this account.
|
||||
GetCodeHashInterrupt => CodeHash
|
||||
}
|
||||
interrupt! {
|
||||
/// Get code of this account.
|
||||
CopyCodeInterrupt => Code
|
||||
}
|
||||
interrupt! {
|
||||
/// Selfdestruct this account.
|
||||
SelfdestructInterrupt => ()
|
||||
}
|
||||
interrupt! {
|
||||
/// Execute this message as a new call.
|
||||
CallInterrupt => CallOutput
|
||||
}
|
||||
interrupt! {
|
||||
/// Get `TxContext` for this call.
|
||||
GetTxContextInterrupt => TxContextData
|
||||
}
|
||||
interrupt! {
|
||||
/// Get block hash for this account.
|
||||
GetBlockHashInterrupt => BlockHash
|
||||
}
|
||||
interrupt! {
|
||||
/// Emit log message.
|
||||
EmitLogInterrupt => ()
|
||||
}
|
||||
interrupt! {
|
||||
/// Access this account and return its status.
|
||||
AccessAccountInterrupt => AccessAccountStatus
|
||||
}
|
||||
interrupt! {
|
||||
/// Access this storage key and return its status.
|
||||
AccessStorageInterrupt => AccessStorageStatus
|
||||
}
|
||||
|
||||
/// Execution complete, this interrupt cannot be resumed.
|
||||
pub struct ExecutionComplete(pub(crate) InnerCoroutine);
|
||||
|
||||
/// Collection of all possible interrupts. Match on this to get the specific interrupt returned.
|
||||
#[derive(From)]
|
||||
pub enum Interrupt {
|
||||
InstructionStart {
|
||||
interrupt: InstructionStartInterrupt,
|
||||
pc: usize,
|
||||
opcode: OpCode,
|
||||
state: Box<ExecutionState>,
|
||||
},
|
||||
AccountExists {
|
||||
interrupt: AccountExistsInterrupt,
|
||||
address: Address,
|
||||
},
|
||||
GetStorage {
|
||||
interrupt: GetStorageInterrupt,
|
||||
address: Address,
|
||||
location: U256,
|
||||
},
|
||||
SetStorage {
|
||||
interrupt: SetStorageInterrupt,
|
||||
address: Address,
|
||||
location: U256,
|
||||
value: U256,
|
||||
},
|
||||
GetBalance {
|
||||
interrupt: GetBalanceInterrupt,
|
||||
address: Address,
|
||||
},
|
||||
GetCodeSize {
|
||||
interrupt: GetCodeSizeInterrupt,
|
||||
address: Address,
|
||||
},
|
||||
GetCodeHash {
|
||||
interrupt: GetCodeHashInterrupt,
|
||||
address: Address,
|
||||
},
|
||||
CopyCode {
|
||||
interrupt: CopyCodeInterrupt,
|
||||
address: Address,
|
||||
offset: usize,
|
||||
max_size: usize,
|
||||
},
|
||||
Selfdestruct {
|
||||
interrupt: SelfdestructInterrupt,
|
||||
address: Address,
|
||||
beneficiary: Address,
|
||||
},
|
||||
Call {
|
||||
interrupt: CallInterrupt,
|
||||
call_data: Call,
|
||||
},
|
||||
GetTxContext {
|
||||
interrupt: GetTxContextInterrupt,
|
||||
},
|
||||
GetBlockHash {
|
||||
interrupt: GetBlockHashInterrupt,
|
||||
block_number: u64,
|
||||
},
|
||||
EmitLog {
|
||||
interrupt: EmitLogInterrupt,
|
||||
address: Address,
|
||||
data: Bytes,
|
||||
topics: ArrayVec<U256, 4>,
|
||||
},
|
||||
AccessAccount {
|
||||
interrupt: AccessAccountInterrupt,
|
||||
address: Address,
|
||||
},
|
||||
AccessStorage {
|
||||
interrupt: AccessStorageInterrupt,
|
||||
address: Address,
|
||||
location: U256,
|
||||
},
|
||||
Complete {
|
||||
interrupt: ExecutionComplete,
|
||||
result: Result<SuccessfulOutput, StatusCode>,
|
||||
},
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Call {
|
||||
Call(InterpreterMessage),
|
||||
Create(CreateMessage),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InterruptData {
|
||||
InstructionStart {
|
||||
pc: usize,
|
||||
opcode: OpCode,
|
||||
state: Box<ExecutionState>,
|
||||
},
|
||||
AccountExists {
|
||||
address: Address,
|
||||
},
|
||||
GetStorage {
|
||||
address: Address,
|
||||
location: U256,
|
||||
},
|
||||
SetStorage {
|
||||
address: Address,
|
||||
location: U256,
|
||||
value: U256,
|
||||
},
|
||||
GetBalance {
|
||||
address: Address,
|
||||
},
|
||||
GetCodeSize {
|
||||
address: Address,
|
||||
},
|
||||
GetCodeHash {
|
||||
address: Address,
|
||||
},
|
||||
CopyCode {
|
||||
address: Address,
|
||||
offset: usize,
|
||||
max_size: usize,
|
||||
},
|
||||
Selfdestruct {
|
||||
address: Address,
|
||||
beneficiary: Address,
|
||||
},
|
||||
Call(Call),
|
||||
GetTxContext,
|
||||
GetBlockHash {
|
||||
block_number: u64,
|
||||
},
|
||||
EmitLog {
|
||||
address: Address,
|
||||
data: Bytes,
|
||||
topics: ArrayVec<U256, 4>,
|
||||
},
|
||||
AccessAccount {
|
||||
address: Address,
|
||||
},
|
||||
AccessStorage {
|
||||
address: Address,
|
||||
location: U256,
|
||||
},
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
use self::{interrupt::*, interrupt_data::*, resume_data::*};
|
||||
use super::{
|
||||
common::*,
|
||||
host::{AccessStatus, StorageStatus, TxContext},
|
||||
state::ExecutionState,
|
||||
*,
|
||||
};
|
||||
use arrayvec::ArrayVec;
|
||||
use derive_more::From;
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use ethereum_types::Address;
|
||||
use ethnum::U256;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
ops::{Generator, GeneratorState},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
/// Interrupts.
|
||||
pub mod interrupt;
|
||||
/// Data attached to interrupts.
|
||||
pub mod interrupt_data;
|
||||
/// Data required for resume.
|
||||
pub mod resume_data;
|
||||
|
||||
pub(crate) type InnerCoroutine = Pin<
|
||||
Box<
|
||||
dyn Generator<
|
||||
ResumeData,
|
||||
Yield = InterruptData,
|
||||
Return = Result<SuccessfulOutput, StatusCode>,
|
||||
> + Send
|
||||
+ Sync,
|
||||
>,
|
||||
>;
|
||||
|
||||
fn resume_interrupt(mut inner: InnerCoroutine, resume_data: ResumeData) -> Interrupt {
|
||||
match inner.as_mut().resume(resume_data) {
|
||||
GeneratorState::Yielded(interrupt) => match interrupt {
|
||||
InterruptData::InstructionStart { pc, opcode, state } => Interrupt::InstructionStart {
|
||||
interrupt: InstructionStartInterrupt { inner },
|
||||
pc,
|
||||
opcode,
|
||||
state,
|
||||
},
|
||||
InterruptData::AccountExists { address } => Interrupt::AccountExists {
|
||||
interrupt: AccountExistsInterrupt { inner },
|
||||
address,
|
||||
},
|
||||
InterruptData::GetStorage { address, location } => Interrupt::GetStorage {
|
||||
interrupt: GetStorageInterrupt { inner },
|
||||
address,
|
||||
location,
|
||||
},
|
||||
InterruptData::SetStorage {
|
||||
address,
|
||||
location,
|
||||
value,
|
||||
} => Interrupt::SetStorage {
|
||||
interrupt: SetStorageInterrupt { inner },
|
||||
address,
|
||||
location,
|
||||
value,
|
||||
},
|
||||
InterruptData::GetBalance { address } => Interrupt::GetBalance {
|
||||
interrupt: GetBalanceInterrupt { inner },
|
||||
address,
|
||||
},
|
||||
InterruptData::GetCodeSize { address } => Interrupt::GetCodeSize {
|
||||
interrupt: GetCodeSizeInterrupt { inner },
|
||||
address,
|
||||
},
|
||||
InterruptData::GetCodeHash { address } => Interrupt::GetCodeHash {
|
||||
interrupt: GetCodeHashInterrupt { inner },
|
||||
address,
|
||||
},
|
||||
InterruptData::CopyCode {
|
||||
address,
|
||||
offset,
|
||||
max_size,
|
||||
} => Interrupt::CopyCode {
|
||||
interrupt: CopyCodeInterrupt { inner },
|
||||
address,
|
||||
offset,
|
||||
max_size,
|
||||
},
|
||||
InterruptData::Selfdestruct {
|
||||
address,
|
||||
beneficiary,
|
||||
} => Interrupt::Selfdestruct {
|
||||
interrupt: SelfdestructInterrupt { inner },
|
||||
address,
|
||||
beneficiary,
|
||||
},
|
||||
InterruptData::Call(call_data) => Interrupt::Call {
|
||||
interrupt: CallInterrupt { inner },
|
||||
call_data,
|
||||
},
|
||||
InterruptData::GetTxContext => Interrupt::GetTxContext {
|
||||
interrupt: GetTxContextInterrupt { inner },
|
||||
},
|
||||
InterruptData::GetBlockHash { block_number } => Interrupt::GetBlockHash {
|
||||
interrupt: GetBlockHashInterrupt { inner },
|
||||
block_number,
|
||||
},
|
||||
InterruptData::EmitLog {
|
||||
address,
|
||||
data,
|
||||
topics,
|
||||
} => Interrupt::EmitLog {
|
||||
interrupt: EmitLogInterrupt { inner },
|
||||
address,
|
||||
data,
|
||||
topics,
|
||||
},
|
||||
InterruptData::AccessAccount { address } => Interrupt::AccessAccount {
|
||||
interrupt: AccessAccountInterrupt { inner },
|
||||
address,
|
||||
},
|
||||
InterruptData::AccessStorage { address, location } => Interrupt::AccessStorage {
|
||||
interrupt: AccessStorageInterrupt { inner },
|
||||
address,
|
||||
location,
|
||||
},
|
||||
},
|
||||
GeneratorState::Complete(result) => Interrupt::Complete {
|
||||
interrupt: ExecutionComplete(inner),
|
||||
result,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
use super::*;
|
||||
use educe::Educe;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type StateModifier = Option<Arc<dyn Fn(&mut ExecutionState) + Send + Sync>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccountExistsStatus {
|
||||
pub exists: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Balance {
|
||||
pub balance: U256,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CodeSize {
|
||||
pub code_size: U256,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StorageValue {
|
||||
pub value: U256,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StorageStatusInfo {
|
||||
pub status: StorageStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CodeHash {
|
||||
pub hash: U256,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlockHash {
|
||||
pub hash: U256,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TxContextData {
|
||||
pub context: TxContext,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Code {
|
||||
pub code: Bytes,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CallOutput {
|
||||
pub output: Output,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccessAccountStatus {
|
||||
pub status: AccessStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AccessStorageStatus {
|
||||
pub status: AccessStatus,
|
||||
}
|
||||
|
||||
/// All resumed data variants.
|
||||
#[derive(Educe, EnumAsInner, From)]
|
||||
#[educe(Debug)]
|
||||
pub(crate) enum ResumeData {
|
||||
#[from(ignore)]
|
||||
Empty,
|
||||
StateModifier(#[educe(Debug(false))] StateModifier),
|
||||
AccountExistsStatus(AccountExistsStatus),
|
||||
Balance(Balance),
|
||||
CodeSize(CodeSize),
|
||||
StorageValue(StorageValue),
|
||||
StorageStatusInfo(StorageStatusInfo),
|
||||
CodeHash(CodeHash),
|
||||
BlockHash(BlockHash),
|
||||
TxContextData(TxContextData),
|
||||
Code(Code),
|
||||
CallOutput(CallOutput),
|
||||
AccessAccountStatus(AccessAccountStatus),
|
||||
AccessStorageStatus(AccessStorageStatus),
|
||||
Done(Infallible),
|
||||
}
|
||||
|
||||
impl From<()> for ResumeData {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Empty
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
use super::common::{InterpreterMessage, Output};
|
||||
use crate::execution::tracer::{NoopTracer, Tracer};
|
||||
|
||||
use super::{
|
||||
common::{InterpreterMessage, Output},
|
||||
CreateMessage,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use ethereum_types::Address;
|
||||
use ethnum::U256;
|
||||
|
||||
@@ -52,44 +58,56 @@ pub struct TxContext {
|
||||
pub block_base_fee: U256,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Call<'a> {
|
||||
Call(&'a InterpreterMessage),
|
||||
Create(&'a CreateMessage),
|
||||
}
|
||||
|
||||
/// Abstraction that exposes host context to EVM.
|
||||
pub trait Host {
|
||||
fn trace_instructions(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn tracer(&mut self, mut f: impl FnMut(&mut dyn Tracer)) {
|
||||
(f)(&mut NoopTracer)
|
||||
}
|
||||
/// Check if an account exists.
|
||||
fn account_exists(&self, address: Address) -> bool;
|
||||
fn account_exists(&mut self, address: Address) -> bool;
|
||||
/// Get value of a storage key.
|
||||
///
|
||||
/// Returns `Ok(U256::zero())` if does not exist.
|
||||
fn get_storage(&self, address: Address, key: U256) -> U256;
|
||||
fn get_storage(&mut self, address: Address, key: U256) -> U256;
|
||||
/// Set value of a storage key.
|
||||
fn set_storage(&mut self, address: Address, key: U256, value: U256) -> StorageStatus;
|
||||
/// Get balance of an account.
|
||||
///
|
||||
/// Returns `Ok(0)` if account does not exist.
|
||||
fn get_balance(&self, address: Address) -> U256;
|
||||
fn get_balance(&mut self, address: Address) -> U256;
|
||||
/// Get code size of an account.
|
||||
///
|
||||
/// Returns `Ok(0)` if account does not exist.
|
||||
fn get_code_size(&self, address: Address) -> U256;
|
||||
fn get_code_size(&mut self, address: Address) -> U256;
|
||||
/// Get code hash of an account.
|
||||
///
|
||||
/// Returns `Ok(0)` if account does not exist.
|
||||
fn get_code_hash(&self, address: Address) -> U256;
|
||||
fn get_code_hash(&mut self, address: Address) -> U256;
|
||||
/// Copy code of an account.
|
||||
///
|
||||
/// Returns `Ok(0)` if offset is invalid.
|
||||
fn copy_code(&self, address: Address, offset: usize, buffer: &mut [u8]) -> usize;
|
||||
fn copy_code(&mut self, address: Address, offset: usize, buffer: &mut [u8]) -> usize;
|
||||
/// Self-destruct account.
|
||||
fn selfdestruct(&mut self, address: Address, beneficiary: Address);
|
||||
/// Call to another account.
|
||||
fn call(&mut self, msg: &InterpreterMessage) -> Output;
|
||||
fn call(&mut self, msg: Call) -> Output;
|
||||
/// Retrieve transaction context.
|
||||
fn get_tx_context(&self) -> TxContext;
|
||||
fn get_tx_context(&mut self) -> TxContext;
|
||||
/// Get block hash.
|
||||
///
|
||||
/// Returns `Ok(U256::zero())` if block does not exist.
|
||||
fn get_block_hash(&self, block_number: u64) -> U256;
|
||||
fn get_block_hash(&mut self, block_number: u64) -> U256;
|
||||
/// Emit a log.
|
||||
fn emit_log(&mut self, address: Address, data: &[u8], topics: &[U256]);
|
||||
fn emit_log(&mut self, address: Address, data: Bytes, topics: &[U256]);
|
||||
/// Mark account as warm, return previous access status.
|
||||
///
|
||||
/// Returns `Ok(AccessStatus::Cold)` if account does not exist.
|
||||
@@ -104,11 +122,11 @@ pub trait Host {
|
||||
pub struct DummyHost;
|
||||
|
||||
impl Host for DummyHost {
|
||||
fn account_exists(&self, _: Address) -> bool {
|
||||
fn account_exists(&mut self, _: Address) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_storage(&self, _: Address, _: U256) -> U256 {
|
||||
fn get_storage(&mut self, _: Address, _: U256) -> U256 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -116,19 +134,19 @@ impl Host for DummyHost {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_balance(&self, _: Address) -> U256 {
|
||||
fn get_balance(&mut self, _: Address) -> U256 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_code_size(&self, _: Address) -> U256 {
|
||||
fn get_code_size(&mut self, _: Address) -> U256 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_code_hash(&self, _: Address) -> U256 {
|
||||
fn get_code_hash(&mut self, _: Address) -> U256 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn copy_code(&self, _: Address, _: usize, _: &mut [u8]) -> usize {
|
||||
fn copy_code(&mut self, _: Address, _: usize, _: &mut [u8]) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -136,19 +154,19 @@ impl Host for DummyHost {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn call(&mut self, _: &InterpreterMessage) -> Output {
|
||||
fn call(&mut self, _: Call) -> Output {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_tx_context(&self) -> TxContext {
|
||||
fn get_tx_context(&mut self) -> TxContext {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_block_hash(&self, _: u64) -> U256 {
|
||||
fn get_block_hash(&mut self, _: u64) -> U256 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn emit_log(&mut self, _: Address, _: &[u8], _: &[U256]) {
|
||||
fn emit_log(&mut self, _: Address, _: Bytes, _: &[U256]) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,52 +4,52 @@ use crate::{
|
||||
};
|
||||
use ethereum_types::U512;
|
||||
use ethnum::U256;
|
||||
use i256::I256;
|
||||
use i256::{i256_div, i256_mod};
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn add(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
stack.push(a.overflowing_add(b).0);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn mul(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
stack.push(a.overflowing_mul(b).0);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn sub(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
stack.push(a.overflowing_sub(b).0);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn div(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
*b = if *b == 0 { U256::ZERO } else { a / *b };
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn sdiv(stack: &mut Stack) {
|
||||
let a = I256::from(stack.pop());
|
||||
let b = I256::from(stack.pop());
|
||||
let v = a / b;
|
||||
stack.push(v.into());
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
let v = i256_div(a, b);
|
||||
stack.push(v);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn modulo(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
*b = if *b == 0 { U256::ZERO } else { a % *b };
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn smod(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
@@ -57,11 +57,11 @@ pub(crate) fn smod(stack: &mut Stack) {
|
||||
if *b == 0 {
|
||||
*b = U256::ZERO
|
||||
} else {
|
||||
let v = I256::from(a) % I256::from(*b);
|
||||
*b = v.into();
|
||||
*b = i256_mod(a, *b);
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn addmod(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
@@ -83,6 +83,7 @@ pub(crate) fn addmod(stack: &mut Stack) {
|
||||
stack.push(v);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn mulmod(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
@@ -104,6 +105,7 @@ pub(crate) fn mulmod(stack: &mut Stack) {
|
||||
stack.push(v);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn log2floor(value: U256) -> u64 {
|
||||
debug_assert!(value != 0);
|
||||
let mut l: u64 = 256;
|
||||
@@ -122,6 +124,7 @@ fn log2floor(value: U256) -> u64 {
|
||||
l
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn exp<const REVISION: Revision>(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
let mut base = state.stack.pop();
|
||||
let mut power = state.stack.pop();
|
||||
@@ -156,6 +159,7 @@ pub(crate) fn exp<const REVISION: Revision>(state: &mut ExecutionState) -> Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn signextend(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::execution::evm::state::Stack;
|
||||
use ethnum::U256;
|
||||
use i256::{Sign, I256};
|
||||
use i256::{i256_sign, two_compl, Sign};
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn byte(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.pop();
|
||||
@@ -21,7 +22,7 @@ pub(crate) fn byte(stack: &mut Stack) {
|
||||
stack.push(ret)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn shl(stack: &mut Stack) {
|
||||
let shift = stack.pop();
|
||||
let value = stack.get_mut(0);
|
||||
@@ -33,7 +34,7 @@ pub(crate) fn shl(stack: &mut Stack) {
|
||||
};
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn shr(stack: &mut Stack) {
|
||||
let shift = stack.pop();
|
||||
let value = stack.get_mut(0);
|
||||
@@ -45,30 +46,31 @@ pub(crate) fn shr(stack: &mut Stack) {
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn sar(stack: &mut Stack) {
|
||||
let shift = stack.pop();
|
||||
let value = I256::from(stack.pop());
|
||||
let mut value = stack.pop();
|
||||
|
||||
let ret = if value == I256::zero() || shift >= 256 {
|
||||
match value.0 {
|
||||
let value_sign = i256_sign::<true>(&mut value);
|
||||
|
||||
stack.push(if value == U256::ZERO || shift >= 256 {
|
||||
match value_sign {
|
||||
// value is 0 or >=1, pushing 0
|
||||
Sign::Plus | Sign::NoSign => U256::ZERO,
|
||||
Sign::Plus | Sign::Zero => U256::ZERO,
|
||||
// value is <0, pushing -1
|
||||
Sign::Minus => I256(Sign::Minus, U256::ONE).into(),
|
||||
Sign::Minus => two_compl(U256::ONE),
|
||||
}
|
||||
} else {
|
||||
let shift = shift.as_u8();
|
||||
let shift = shift.as_u128();
|
||||
|
||||
match value.0 {
|
||||
Sign::Plus | Sign::NoSign => value.1 >> shift,
|
||||
match value_sign {
|
||||
Sign::Plus | Sign::Zero => value >> shift,
|
||||
Sign::Minus => {
|
||||
let shifted = ((value.1.overflowing_sub(U256::ONE).0) >> shift)
|
||||
let shifted = ((value.overflowing_sub(U256::ONE).0) >> shift)
|
||||
.overflowing_add(U256::ONE)
|
||||
.0;
|
||||
I256(Sign::Minus, shifted).into()
|
||||
two_compl(shifted)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
stack.push(ret)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::execution::evm::state::*;
|
||||
use ethnum::U256;
|
||||
use i256::I256;
|
||||
use i256::i256_cmp;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn lt(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
@@ -10,7 +11,7 @@ pub(crate) fn lt(stack: &mut Stack) {
|
||||
*b = if a.lt(b) { U256::ONE } else { U256::ZERO }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn gt(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
@@ -18,23 +19,31 @@ pub(crate) fn gt(stack: &mut Stack) {
|
||||
*b = if a.gt(b) { U256::ONE } else { U256::ZERO }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn slt(stack: &mut Stack) {
|
||||
let a: I256 = stack.pop().into();
|
||||
let b: I256 = stack.pop().into();
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
|
||||
stack.push(if a.lt(&b) { U256::ONE } else { U256::ZERO })
|
||||
*b = if i256_cmp(a, *b) == Ordering::Less {
|
||||
U256::ONE
|
||||
} else {
|
||||
U256::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn sgt(stack: &mut Stack) {
|
||||
let a: I256 = stack.pop().into();
|
||||
let b: I256 = stack.pop().into();
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
|
||||
stack.push(if a.gt(&b) { U256::ONE } else { U256::ZERO })
|
||||
*b = if i256_cmp(a, *b) == Ordering::Greater {
|
||||
U256::ONE
|
||||
} else {
|
||||
U256::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn eq(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
@@ -42,34 +51,34 @@ pub(crate) fn eq(stack: &mut Stack) {
|
||||
*b = if a.eq(b) { U256::ONE } else { U256::ZERO }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn iszero(stack: &mut Stack) {
|
||||
let a = stack.get_mut(0);
|
||||
*a = if *a == 0 { U256::ONE } else { U256::ZERO }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn and(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
*b = a & *b;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn or(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
*b = a | *b;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn xor(stack: &mut Stack) {
|
||||
let a = stack.pop();
|
||||
let b = stack.get_mut(0);
|
||||
*b = a ^ *b;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn not(stack: &mut Stack) {
|
||||
let v = stack.get_mut(0);
|
||||
*v = !*v;
|
||||
|
||||
@@ -1,246 +1,223 @@
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! do_call {
|
||||
($state:expr, $rev:expr, $kind:expr, $is_static:expr) => {{
|
||||
use std::cmp::min;
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
common::u256_to_address,
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::AccessStatus,
|
||||
instructions::{memory::MemoryRegion, properties::*},
|
||||
CallKind, InterpreterMessage,
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
use crate::{
|
||||
execution::evm::{instructions::memory, CallKind, ExecutionState, Host, StatusCode},
|
||||
models::Revision,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
|
||||
let gas = $state.stack.pop();
|
||||
let dst = u256_to_address($state.stack.pop());
|
||||
let value = if $is_static || matches!($kind, CallKind::DelegateCall) {
|
||||
U256::ZERO
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn do_call<
|
||||
H: Host,
|
||||
const REVISION: Revision,
|
||||
const KIND: CallKind,
|
||||
const IS_STATIC: bool,
|
||||
>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{
|
||||
common::u256_to_address,
|
||||
host::*,
|
||||
instructions::{memory::MemoryRegion, properties::*},
|
||||
InterpreterMessage,
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
use std::cmp::min;
|
||||
|
||||
let gas = state.stack.pop();
|
||||
let dst = u256_to_address(state.stack.pop());
|
||||
let value = if IS_STATIC || matches!(KIND, CallKind::DelegateCall) {
|
||||
U256::ZERO
|
||||
} else {
|
||||
state.stack.pop()
|
||||
};
|
||||
let has_value = value != 0;
|
||||
let input_offset = state.stack.pop();
|
||||
let input_size = state.stack.pop();
|
||||
let output_offset = state.stack.pop();
|
||||
let output_size = state.stack.pop();
|
||||
|
||||
state.stack.push(U256::ZERO); // Assume failure.
|
||||
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_account(dst) == AccessStatus::Cold {
|
||||
state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let input_region = memory::get_memory_region(state, input_offset, input_size)
|
||||
.map_err(|_| StatusCode::OutOfGas)?;
|
||||
let output_region = memory::get_memory_region(state, output_offset, output_size)
|
||||
.map_err(|_| StatusCode::OutOfGas)?;
|
||||
|
||||
let mut msg = InterpreterMessage {
|
||||
kind: KIND,
|
||||
is_static: IS_STATIC || state.message.is_static,
|
||||
depth: state.message.depth + 1,
|
||||
recipient: if matches!(KIND, CallKind::Call) {
|
||||
dst
|
||||
} else {
|
||||
$state.stack.pop()
|
||||
};
|
||||
let has_value = value != 0;
|
||||
let input_offset = $state.stack.pop();
|
||||
let input_size = $state.stack.pop();
|
||||
let output_offset = $state.stack.pop();
|
||||
let output_size = $state.stack.pop();
|
||||
state.message.recipient
|
||||
},
|
||||
code_address: dst,
|
||||
sender: if matches!(KIND, CallKind::DelegateCall) {
|
||||
state.message.sender
|
||||
} else {
|
||||
state.message.recipient
|
||||
},
|
||||
gas: i64::MAX,
|
||||
value: if matches!(KIND, CallKind::DelegateCall) {
|
||||
state.message.value
|
||||
} else {
|
||||
value
|
||||
},
|
||||
input_data: input_region
|
||||
.map(|MemoryRegion { offset, size }| {
|
||||
state.memory[offset..offset + size.get()].to_vec().into()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
$state.stack.push(U256::ZERO); // Assume failure.
|
||||
let mut cost = if has_value { 9000 } else { 0 };
|
||||
|
||||
if $rev >= Revision::Berlin {
|
||||
if ResumeData::into_access_account_status(
|
||||
yield InterruptData::AccessAccount { address: dst },
|
||||
)
|
||||
.unwrap()
|
||||
.status
|
||||
== AccessStatus::Cold
|
||||
{
|
||||
$state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let input_region = memory::get_memory_region($state, input_offset, input_size)
|
||||
.map_err(|_| StatusCode::OutOfGas)?;
|
||||
let output_region = memory::get_memory_region($state, output_offset, output_size)
|
||||
.map_err(|_| StatusCode::OutOfGas)?;
|
||||
|
||||
let mut msg = InterpreterMessage {
|
||||
kind: $kind,
|
||||
is_static: $is_static || $state.message.is_static,
|
||||
depth: $state.message.depth + 1,
|
||||
recipient: if matches!($kind, CallKind::Call) {
|
||||
dst
|
||||
} else {
|
||||
$state.message.recipient
|
||||
},
|
||||
code_address: dst,
|
||||
sender: if matches!($kind, CallKind::DelegateCall) {
|
||||
$state.message.sender
|
||||
} else {
|
||||
$state.message.recipient
|
||||
},
|
||||
gas: i64::MAX,
|
||||
value: if matches!($kind, CallKind::DelegateCall) {
|
||||
$state.message.value
|
||||
} else {
|
||||
value
|
||||
},
|
||||
input_data: input_region
|
||||
.map(|MemoryRegion { offset, size }| {
|
||||
$state.memory[offset..offset + size.get()].to_vec().into()
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let mut cost = if has_value { 9000 } else { 0 };
|
||||
|
||||
if matches!($kind, CallKind::Call) {
|
||||
if has_value && $state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
|
||||
if (has_value || $rev < Revision::Spurious)
|
||||
&& !ResumeData::into_account_exists_status({
|
||||
yield InterruptData::AccountExists { address: dst }
|
||||
})
|
||||
.unwrap()
|
||||
.exists
|
||||
{
|
||||
cost += 25000;
|
||||
}
|
||||
}
|
||||
$state.gas_left -= cost;
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
|
||||
if gas < u128::try_from(msg.gas).unwrap() {
|
||||
msg.gas = gas.as_usize() as i64;
|
||||
}
|
||||
|
||||
if $rev >= Revision::Tangerine {
|
||||
// TODO: Always true for STATICCALL.
|
||||
msg.gas = min(msg.gas, $state.gas_left - $state.gas_left / 64);
|
||||
} else if msg.gas > $state.gas_left {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
|
||||
if has_value {
|
||||
msg.gas += 2300; // Add stipend.
|
||||
$state.gas_left += 2300;
|
||||
}
|
||||
|
||||
$state.return_data.clear();
|
||||
|
||||
if $state.message.depth < 1024
|
||||
&& !(has_value
|
||||
&& ResumeData::into_balance({
|
||||
yield InterruptData::GetBalance {
|
||||
address: $state.message.recipient,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.balance
|
||||
< value)
|
||||
{
|
||||
let msg_gas = msg.gas;
|
||||
let result =
|
||||
ResumeData::into_call_output({ yield InterruptData::Call(Call::Call(msg)) })
|
||||
.unwrap()
|
||||
.output;
|
||||
$state.return_data = result.output_data.clone();
|
||||
*$state.stack.get_mut(0) = if matches!(result.status_code, StatusCode::Success) {
|
||||
U256::ONE
|
||||
} else {
|
||||
U256::ZERO
|
||||
};
|
||||
|
||||
if let Some(MemoryRegion { offset, size }) = output_region {
|
||||
let copy_size = min(size.get(), result.output_data.len());
|
||||
if copy_size > 0 {
|
||||
$state.memory[offset..offset + copy_size]
|
||||
.copy_from_slice(&result.output_data[..copy_size]);
|
||||
}
|
||||
}
|
||||
|
||||
let gas_used = msg_gas - result.gas_left;
|
||||
$state.gas_left -= gas_used;
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! do_create {
|
||||
($state:expr, $rev:expr, $create2:expr) => {{
|
||||
use ethnum::U256;
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
common::*,
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
CreateMessage,
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
|
||||
if $state.message.is_static {
|
||||
if matches!(KIND, CallKind::Call) {
|
||||
if has_value && state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
|
||||
let endowment = $state.stack.pop();
|
||||
let init_code_offset = $state.stack.pop();
|
||||
let init_code_size = $state.stack.pop();
|
||||
if (has_value || REVISION < Revision::Spurious) && !host.account_exists(dst) {
|
||||
cost += 25000;
|
||||
}
|
||||
}
|
||||
state.gas_left -= cost;
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
|
||||
let region = memory::get_memory_region($state, init_code_offset, init_code_size)
|
||||
.map_err(|_| StatusCode::OutOfGas)?;
|
||||
if gas < u128::try_from(msg.gas).unwrap() {
|
||||
msg.gas = gas.as_usize() as i64;
|
||||
}
|
||||
|
||||
let salt = if $create2 {
|
||||
let salt = $state.stack.pop();
|
||||
if REVISION >= Revision::Tangerine {
|
||||
// TODO: Always true for STATICCALL.
|
||||
msg.gas = min(msg.gas, state.gas_left - state.gas_left / 64);
|
||||
} else if msg.gas > state.gas_left {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
|
||||
if let Some(region) = ®ion {
|
||||
let salt_cost = memory::num_words(region.size.get()) * 6;
|
||||
$state.gas_left -= salt_cost;
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
if has_value {
|
||||
msg.gas += 2300; // Add stipend.
|
||||
state.gas_left += 2300;
|
||||
}
|
||||
|
||||
Some(salt)
|
||||
state.return_data.clear();
|
||||
|
||||
if state.message.depth < 1024
|
||||
&& !(has_value && host.get_balance(state.message.recipient) < value)
|
||||
{
|
||||
let msg_gas = msg.gas;
|
||||
let result = host.call(Call::Call(&msg));
|
||||
state.return_data = result.output_data.clone();
|
||||
*state.stack.get_mut(0) = if matches!(result.status_code, StatusCode::Success) {
|
||||
U256::ONE
|
||||
} else {
|
||||
None
|
||||
U256::ZERO
|
||||
};
|
||||
|
||||
$state.stack.push(U256::ZERO);
|
||||
$state.return_data.clear();
|
||||
|
||||
if $state.message.depth < 1024
|
||||
&& !(endowment != 0
|
||||
&& ResumeData::into_balance({
|
||||
yield InterruptData::GetBalance {
|
||||
address: $state.message.recipient,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.balance
|
||||
< endowment)
|
||||
{
|
||||
let msg = CreateMessage {
|
||||
gas: if $rev >= Revision::Tangerine {
|
||||
$state.gas_left - $state.gas_left / 64
|
||||
} else {
|
||||
$state.gas_left
|
||||
},
|
||||
|
||||
salt,
|
||||
initcode: if init_code_size != 0 {
|
||||
$state.memory[init_code_offset.as_usize()
|
||||
..init_code_offset.as_usize() + init_code_size.as_usize()]
|
||||
.to_vec()
|
||||
.into()
|
||||
} else {
|
||||
Bytes::new()
|
||||
},
|
||||
sender: $state.message.recipient,
|
||||
depth: $state.message.depth + 1,
|
||||
endowment,
|
||||
};
|
||||
let msg_gas = msg.gas;
|
||||
let result =
|
||||
ResumeData::into_call_output({ yield InterruptData::Call(Call::Create(msg)) })
|
||||
.unwrap()
|
||||
.output;
|
||||
$state.gas_left -= msg_gas - result.gas_left;
|
||||
|
||||
$state.return_data = result.output_data;
|
||||
if result.status_code == StatusCode::Success {
|
||||
*$state.stack.get_mut(0) =
|
||||
address_to_u256(result.create_address.expect("expected create address"));
|
||||
if let Some(MemoryRegion { offset, size }) = output_region {
|
||||
let copy_size = min(size.get(), result.output_data.len());
|
||||
if copy_size > 0 {
|
||||
state.memory[offset..offset + copy_size]
|
||||
.copy_from_slice(&result.output_data[..copy_size]);
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
let gas_used = msg_gas - result.gas_left;
|
||||
state.gas_left -= gas_used;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn do_create<H: Host, const REVISION: Revision, const CREATE2: bool>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{common::*, host::*, CreateMessage},
|
||||
models::*,
|
||||
};
|
||||
use ethnum::U256;
|
||||
|
||||
if state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
|
||||
let endowment = state.stack.pop();
|
||||
let init_code_offset = state.stack.pop();
|
||||
let init_code_size = state.stack.pop();
|
||||
|
||||
let region = memory::get_memory_region(state, init_code_offset, init_code_size)
|
||||
.map_err(|_| StatusCode::OutOfGas)?;
|
||||
|
||||
let salt = if CREATE2 {
|
||||
let salt = state.stack.pop();
|
||||
|
||||
if let Some(region) = ®ion {
|
||||
let salt_cost = memory::num_words(region.size.get()) * 6;
|
||||
state.gas_left -= salt_cost;
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
|
||||
Some(salt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
state.stack.push(U256::ZERO);
|
||||
state.return_data.clear();
|
||||
|
||||
if state.message.depth < 1024
|
||||
&& !(endowment != 0 && host.get_balance(state.message.recipient) < endowment)
|
||||
{
|
||||
let msg = CreateMessage {
|
||||
gas: if REVISION >= Revision::Tangerine {
|
||||
state.gas_left - state.gas_left / 64
|
||||
} else {
|
||||
state.gas_left
|
||||
},
|
||||
|
||||
salt,
|
||||
initcode: if init_code_size != 0 {
|
||||
state.memory[init_code_offset.as_usize()
|
||||
..init_code_offset.as_usize() + init_code_size.as_usize()]
|
||||
.to_vec()
|
||||
.into()
|
||||
} else {
|
||||
Bytes::new()
|
||||
},
|
||||
sender: state.message.recipient,
|
||||
depth: state.message.depth + 1,
|
||||
endowment,
|
||||
};
|
||||
let msg_gas = msg.gas;
|
||||
let result = host.call(Call::Create(&msg));
|
||||
state.gas_left -= msg_gas - result.gas_left;
|
||||
|
||||
state.return_data = result.output_data;
|
||||
if result.status_code == StatusCode::Success {
|
||||
*state.stack.get_mut(0) =
|
||||
address_to_u256(result.create_address.expect("expected create address"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::execution::evm::{interpreter::JumpdestMap, state::ExecutionState, StatusCode};
|
||||
use ethnum::U256;
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
let offset = *state.stack.get(0);
|
||||
let size = *state.stack.get(1);
|
||||
@@ -17,7 +17,7 @@ pub(crate) fn ret(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn op_jump(
|
||||
state: &mut ExecutionState,
|
||||
jumpdest_map: &JumpdestMap,
|
||||
@@ -30,7 +30,7 @@ pub(crate) fn op_jump(
|
||||
Ok(dst.as_usize())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn calldataload(state: &mut ExecutionState) {
|
||||
let index = state.stack.pop();
|
||||
|
||||
@@ -51,7 +51,7 @@ pub(crate) fn calldataload(state: &mut ExecutionState) {
|
||||
});
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn calldatasize(state: &mut ExecutionState) {
|
||||
state.stack.push(
|
||||
u128::try_from(state.message.input_data.len())
|
||||
|
||||
@@ -1,442 +1,337 @@
|
||||
use crate::execution::evm::{common::address_to_u256, host::*, state::ExecutionState};
|
||||
use crate::{
|
||||
execution::evm::{
|
||||
common::address_to_u256, host::*, instructions::memory, state::ExecutionState, StatusCode,
|
||||
},
|
||||
models::Revision,
|
||||
};
|
||||
use ethnum::U256;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn address(state: &mut ExecutionState) {
|
||||
state.stack.push(address_to_u256(state.message.recipient));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn caller(state: &mut ExecutionState) {
|
||||
state.stack.push(address_to_u256(state.message.sender));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn callvalue(state: &mut ExecutionState) {
|
||||
state.stack.push(state.message.value);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! balance {
|
||||
($state:expr,$rev:expr) => {
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
common::*,
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
instructions::properties::*,
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn balance<H: Host, const REVISION: Revision>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{common::*, host::*, instructions::properties::*},
|
||||
models::*,
|
||||
};
|
||||
|
||||
let address = u256_to_address($state.stack.pop());
|
||||
let address = u256_to_address(state.stack.pop());
|
||||
|
||||
if $rev >= Revision::Berlin {
|
||||
let access_status = ResumeData::into_access_account_status({
|
||||
yield InterruptData::AccessAccount { address }
|
||||
})
|
||||
.unwrap()
|
||||
.status;
|
||||
if access_status == AccessStatus::Cold {
|
||||
$state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_account(address) == AccessStatus::Cold {
|
||||
state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let balance = ResumeData::into_balance({ yield InterruptData::GetBalance { address } })
|
||||
.unwrap()
|
||||
.balance;
|
||||
state.stack.push(host.get_balance(address));
|
||||
|
||||
$state.stack.push(balance);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! extcodesize {
|
||||
($state:expr,$rev:expr) => {
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
common::*,
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
instructions::properties::*,
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn extcodesize<H: Host, const REVISION: Revision>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{common::*, host::*, instructions::properties::*},
|
||||
models::*,
|
||||
};
|
||||
|
||||
let address = u256_to_address($state.stack.pop());
|
||||
let address = u256_to_address(state.stack.pop());
|
||||
|
||||
if $rev >= Revision::Berlin {
|
||||
let access_account = ResumeData::into_access_account_status({
|
||||
yield InterruptData::AccessAccount { address }
|
||||
})
|
||||
.unwrap()
|
||||
.status;
|
||||
if access_account == AccessStatus::Cold {
|
||||
$state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_account(address) == AccessStatus::Cold {
|
||||
state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let code_size =
|
||||
ResumeData::into_code_size({ yield InterruptData::GetCodeSize { address } })
|
||||
.unwrap()
|
||||
.code_size;
|
||||
$state.stack.push(code_size);
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! push_txcontext {
|
||||
($state:expr, $accessor:expr) => {
|
||||
use $crate::execution::evm::continuation::{interrupt_data::*, resume_data::*};
|
||||
|
||||
let tx_context = ResumeData::into_tx_context_data({ yield InterruptData::GetTxContext })
|
||||
.unwrap()
|
||||
.context;
|
||||
|
||||
$state.stack.push($accessor(tx_context));
|
||||
};
|
||||
state.stack.push(host.get_code_size(address));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn origin_accessor(tx_context: TxContext) -> U256 {
|
||||
address_to_u256(tx_context.tx_origin)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn coinbase_accessor(tx_context: TxContext) -> U256 {
|
||||
address_to_u256(tx_context.block_coinbase)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn gasprice_accessor(tx_context: TxContext) -> U256 {
|
||||
tx_context.tx_gas_price
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn timestamp_accessor(tx_context: TxContext) -> U256 {
|
||||
tx_context.block_timestamp.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn number_accessor(tx_context: TxContext) -> U256 {
|
||||
tx_context.block_number.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn gaslimit_accessor(tx_context: TxContext) -> U256 {
|
||||
tx_context.block_gas_limit.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn difficulty_accessor(tx_context: TxContext) -> U256 {
|
||||
tx_context.block_difficulty
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn chainid_accessor(tx_context: TxContext) -> U256 {
|
||||
tx_context.chain_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn basefee_accessor(tx_context: TxContext) -> U256 {
|
||||
tx_context.block_base_fee
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! selfbalance {
|
||||
($state:expr) => {{
|
||||
use $crate::execution::evm::continuation::{interrupt_data::*, resume_data::*};
|
||||
|
||||
let balance = ResumeData::into_balance({
|
||||
yield InterruptData::GetBalance {
|
||||
address: $state.message.recipient,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.balance;
|
||||
|
||||
$state.stack.push(balance);
|
||||
}};
|
||||
#[inline]
|
||||
pub(crate) fn selfbalance<H: Host>(state: &mut ExecutionState, host: &mut H) {
|
||||
state.stack.push(host.get_balance(state.message.recipient));
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! blockhash {
|
||||
($state:expr) => {
|
||||
use $crate::execution::evm::continuation::{interrupt_data::*, resume_data::*};
|
||||
#[inline]
|
||||
pub(crate) fn blockhash<H: Host>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
let number = state.stack.pop();
|
||||
|
||||
let number = $state.stack.pop();
|
||||
let upper_bound = host.get_tx_context().block_number;
|
||||
let lower_bound = upper_bound.saturating_sub(256);
|
||||
|
||||
let upper_bound = ResumeData::into_tx_context_data({ yield InterruptData::GetTxContext })
|
||||
.unwrap()
|
||||
.context
|
||||
.block_number;
|
||||
let lower_bound = upper_bound.saturating_sub(256);
|
||||
|
||||
let mut header = U256::ZERO;
|
||||
if number <= u128::from(u64::MAX) {
|
||||
let n = number.as_u64();
|
||||
if (lower_bound..upper_bound).contains(&n) {
|
||||
header = ResumeData::into_block_hash({
|
||||
yield InterruptData::GetBlockHash { block_number: n }
|
||||
})
|
||||
.unwrap()
|
||||
.hash;
|
||||
}
|
||||
let mut header = U256::ZERO;
|
||||
if number <= u128::from(u64::MAX) {
|
||||
let n = number.as_u64();
|
||||
if (lower_bound..upper_bound).contains(&n) {
|
||||
header = host.get_block_hash(n);
|
||||
}
|
||||
}
|
||||
|
||||
$state.stack.push(header);
|
||||
};
|
||||
state.stack.push(header);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! do_log {
|
||||
($state:expr, $num_topics:expr) => {{
|
||||
use arrayvec::ArrayVec;
|
||||
use $crate::execution::evm::continuation::{interrupt_data::*, resume_data::*};
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn do_log<H: Host, const NUM_TOPICS: usize>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use arrayvec::ArrayVec;
|
||||
|
||||
if $state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
if state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
|
||||
let offset = $state.stack.pop();
|
||||
let size = $state.stack.pop();
|
||||
let offset = state.stack.pop();
|
||||
let size = state.stack.pop();
|
||||
|
||||
let region =
|
||||
memory::get_memory_region($state, offset, size).map_err(|_| StatusCode::OutOfGas)?;
|
||||
let region =
|
||||
memory::get_memory_region(state, offset, size).map_err(|_| StatusCode::OutOfGas)?;
|
||||
|
||||
if let Some(region) = ®ion {
|
||||
let cost = region.size.get() as i64 * 8;
|
||||
$state.gas_left -= cost;
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
|
||||
let mut topics = ArrayVec::new();
|
||||
for _ in 0..$num_topics {
|
||||
topics.push($state.stack.pop());
|
||||
}
|
||||
|
||||
let data = if let Some(region) = region {
|
||||
&$state.memory[region.offset..region.offset + region.size.get()]
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
||||
let data = data.to_vec().into();
|
||||
let r = {
|
||||
yield InterruptData::EmitLog {
|
||||
address: $state.message.recipient,
|
||||
data,
|
||||
topics,
|
||||
}
|
||||
};
|
||||
|
||||
debug_assert!(matches!(r, ResumeData::Empty));
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! sload {
|
||||
($state:expr,$rev:expr) => {{
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
|
||||
let location = $state.stack.pop();
|
||||
|
||||
if $rev >= Revision::Berlin {
|
||||
let access_status = ResumeData::into_access_storage_status({
|
||||
yield InterruptData::AccessStorage {
|
||||
address: $state.message.recipient,
|
||||
location,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.status;
|
||||
if access_status == AccessStatus::Cold {
|
||||
// The warm storage access cost is already applied (from the cost table).
|
||||
// Here we need to apply additional cold storage access cost.
|
||||
const ADDITIONAL_COLD_SLOAD_COST: u16 = COLD_SLOAD_COST - WARM_STORAGE_READ_COST;
|
||||
$state.gas_left -= i64::from(ADDITIONAL_COLD_SLOAD_COST);
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let storage = ResumeData::into_storage_value({
|
||||
yield InterruptData::GetStorage {
|
||||
address: $state.message.recipient,
|
||||
location,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.value;
|
||||
|
||||
$state.stack.push(storage);
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! sstore {
|
||||
($state:expr,$rev:expr) => {{
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
|
||||
if $state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
|
||||
if $rev >= Revision::Istanbul {
|
||||
if $state.gas_left <= 2300 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
|
||||
let location = $state.stack.pop();
|
||||
let value = $state.stack.pop();
|
||||
|
||||
let mut cost = 0;
|
||||
if $rev >= Revision::Berlin {
|
||||
let access_status = ResumeData::into_access_storage_status({
|
||||
yield InterruptData::AccessStorage {
|
||||
address: $state.message.recipient,
|
||||
location,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.status;
|
||||
|
||||
if access_status == AccessStatus::Cold {
|
||||
cost = COLD_SLOAD_COST;
|
||||
}
|
||||
}
|
||||
|
||||
let status = ResumeData::into_storage_status_info({
|
||||
yield InterruptData::SetStorage {
|
||||
address: $state.message.recipient,
|
||||
location,
|
||||
value,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.status;
|
||||
|
||||
cost = match status {
|
||||
StorageStatus::Unchanged | StorageStatus::ModifiedAgain => {
|
||||
if $rev >= Revision::Berlin {
|
||||
cost + WARM_STORAGE_READ_COST
|
||||
} else if $rev == Revision::Istanbul {
|
||||
800
|
||||
} else if $rev == Revision::Constantinople {
|
||||
200
|
||||
} else {
|
||||
5000
|
||||
}
|
||||
}
|
||||
StorageStatus::Modified | StorageStatus::Deleted => {
|
||||
if $rev >= Revision::Berlin {
|
||||
cost + 5000 - COLD_SLOAD_COST
|
||||
} else {
|
||||
5000
|
||||
}
|
||||
}
|
||||
StorageStatus::Added => cost + 20000,
|
||||
};
|
||||
$state.gas_left -= i64::from(cost);
|
||||
if $state.gas_left < 0 {
|
||||
if let Some(region) = ®ion {
|
||||
let cost = region.size.get() as i64 * 8;
|
||||
state.gas_left -= cost;
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
let mut topics = ArrayVec::<U256, 4>::new();
|
||||
for _ in 0..NUM_TOPICS {
|
||||
topics.push(state.stack.pop());
|
||||
}
|
||||
|
||||
let data = if let Some(region) = region {
|
||||
&state.memory[region.offset..region.offset + region.size.get()]
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
.to_vec()
|
||||
.into();
|
||||
|
||||
host.emit_log(state.message.recipient, data, &*topics);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! selfdestruct {
|
||||
($state:expr,$rev:expr) => {{
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
common::*,
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
instructions::properties::*,
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn sload<H: Host, const REVISION: Revision>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{
|
||||
host::*,
|
||||
instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
|
||||
if $state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
let location = state.stack.pop();
|
||||
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_storage(state.message.recipient, location) == AccessStatus::Cold {
|
||||
// The warm storage access cost is already applied (from the cost table).
|
||||
// Here we need to apply additional cold storage access cost.
|
||||
const ADDITIONAL_COLD_SLOAD_COST: u16 = COLD_SLOAD_COST - WARM_STORAGE_READ_COST;
|
||||
state.gas_left -= i64::from(ADDITIONAL_COLD_SLOAD_COST);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let beneficiary = u256_to_address($state.stack.pop());
|
||||
state
|
||||
.stack
|
||||
.push(host.get_storage(state.message.recipient, location));
|
||||
|
||||
if $rev >= Revision::Berlin {
|
||||
let access_status = ResumeData::into_access_account_status({
|
||||
yield InterruptData::AccessAccount {
|
||||
address: beneficiary,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.status;
|
||||
if access_status == AccessStatus::Cold {
|
||||
$state.gas_left -= i64::from(COLD_ACCOUNT_ACCESS_COST);
|
||||
if $state.gas_left < 0 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn sstore<H: Host, const REVISION: Revision>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{
|
||||
host::*,
|
||||
instructions::properties::{COLD_SLOAD_COST, WARM_STORAGE_READ_COST},
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
|
||||
if state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
|
||||
if REVISION >= Revision::Istanbul {
|
||||
if state.gas_left <= 2300 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
|
||||
let location = state.stack.pop();
|
||||
let value = state.stack.pop();
|
||||
|
||||
let mut cost = 0;
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_storage(state.message.recipient, location) == AccessStatus::Cold {
|
||||
cost = COLD_SLOAD_COST;
|
||||
}
|
||||
}
|
||||
|
||||
cost = match host.set_storage(state.message.recipient, location, value) {
|
||||
StorageStatus::Unchanged | StorageStatus::ModifiedAgain => {
|
||||
if REVISION >= Revision::Berlin {
|
||||
cost + WARM_STORAGE_READ_COST
|
||||
} else if REVISION == Revision::Istanbul {
|
||||
800
|
||||
} else if REVISION == Revision::Constantinople {
|
||||
200
|
||||
} else {
|
||||
5000
|
||||
}
|
||||
}
|
||||
StorageStatus::Modified | StorageStatus::Deleted => {
|
||||
if REVISION >= Revision::Berlin {
|
||||
cost + 5000 - COLD_SLOAD_COST
|
||||
} else {
|
||||
5000
|
||||
}
|
||||
}
|
||||
StorageStatus::Added => cost + 20000,
|
||||
};
|
||||
state.gas_left -= i64::from(cost);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn selfdestruct<H: Host, const REVISION: Revision>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{common::*, host::*, instructions::properties::*},
|
||||
models::*,
|
||||
};
|
||||
|
||||
if state.message.is_static {
|
||||
return Err(StatusCode::StaticModeViolation);
|
||||
}
|
||||
|
||||
let beneficiary = u256_to_address(state.stack.pop());
|
||||
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_account(beneficiary) == AccessStatus::Cold {
|
||||
state.gas_left -= i64::from(COLD_ACCOUNT_ACCESS_COST);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if REVISION >= Revision::Tangerine {
|
||||
if REVISION == Revision::Tangerine || host.get_balance(state.message.recipient) != 0 {
|
||||
// After TANGERINE_WHISTLE apply additional cost of
|
||||
// sending value to a non-existing account.
|
||||
if !host.account_exists(beneficiary) {
|
||||
state.gas_left -= 25000;
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if $rev >= Revision::Tangerine {
|
||||
if ($rev == Revision::Tangerine
|
||||
|| !{
|
||||
ResumeData::into_balance({
|
||||
yield InterruptData::GetBalance {
|
||||
address: $state.message.recipient,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.balance
|
||||
== 0
|
||||
})
|
||||
{
|
||||
// After TANGERINE_WHISTLE apply additional cost of
|
||||
// sending value to a non-existing account.
|
||||
if !ResumeData::into_account_exists_status({
|
||||
yield InterruptData::AccountExists {
|
||||
address: beneficiary,
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.exists
|
||||
{
|
||||
$state.gas_left -= 25000;
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
host.selfdestruct(state.message.recipient, beneficiary);
|
||||
|
||||
let r = yield InterruptData::Selfdestruct {
|
||||
address: $state.message.recipient,
|
||||
beneficiary,
|
||||
};
|
||||
debug_assert!(matches!(r, ResumeData::Empty));
|
||||
}};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,42 +1,64 @@
|
||||
use super::properties::GAS_COSTS;
|
||||
use crate::{execution::evm::instructions::properties, models::*};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct InstructionTableEntry {
|
||||
pub gas_cost: u16,
|
||||
pub gas_cost: i16,
|
||||
pub stack_height_required: u8,
|
||||
pub can_overflow_stack: bool,
|
||||
}
|
||||
|
||||
pub type InstructionTable = [Option<InstructionTableEntry>; 256];
|
||||
pub type InstructionTable = [InstructionTableEntry; 256];
|
||||
pub type InstructionTables = [InstructionTable; Revision::len()];
|
||||
|
||||
pub static INSTRUCTION_TABLES: Lazy<InstructionTables> = Lazy::new(|| {
|
||||
let mut table = [[None; 256]; Revision::len()];
|
||||
const fn instruction_tables() -> InstructionTables {
|
||||
let mut table = [[InstructionTableEntry {
|
||||
gas_cost: -1,
|
||||
stack_height_required: 0,
|
||||
can_overflow_stack: false,
|
||||
}; 256]; Revision::len()];
|
||||
|
||||
for revision in Revision::iter() {
|
||||
for (opcode, &cost) in properties::gas_costs(revision).iter().enumerate() {
|
||||
if let Some(cost) = cost {
|
||||
let stack_height_required = properties::PROPERTIES[opcode]
|
||||
.unwrap()
|
||||
.stack_height_required;
|
||||
const LATEST: Revision = Revision::latest();
|
||||
|
||||
// Because any instruction can increase stack height at most of 1,
|
||||
// stack overflow can only happen if stack height is already at the limit.
|
||||
debug_assert!(properties::PROPERTIES[opcode].unwrap().stack_height_change <= 1);
|
||||
let revtable = Revision::iter();
|
||||
let mut reviter = 0_usize;
|
||||
loop {
|
||||
let revision = revtable[reviter];
|
||||
|
||||
table[revision as usize][opcode] = Some(InstructionTableEntry {
|
||||
gas_cost: cost,
|
||||
stack_height_required,
|
||||
can_overflow_stack: properties::PROPERTIES[opcode].unwrap().stack_height_change
|
||||
> 0,
|
||||
});
|
||||
let mut opcode = 0;
|
||||
loop {
|
||||
let (stack_height_required, can_overflow_stack) =
|
||||
if let Some(p) = &properties::PROPERTIES[opcode] {
|
||||
(p.stack_height_required, p.stack_height_change > 0)
|
||||
} else {
|
||||
(0, false)
|
||||
};
|
||||
|
||||
table[revision as usize][opcode] = InstructionTableEntry {
|
||||
gas_cost: GAS_COSTS[revision as usize][opcode],
|
||||
stack_height_required,
|
||||
can_overflow_stack,
|
||||
};
|
||||
|
||||
if opcode == u8::MAX as usize {
|
||||
break;
|
||||
} else {
|
||||
opcode += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(revision, LATEST) {
|
||||
break;
|
||||
} else {
|
||||
reviter += 1;
|
||||
}
|
||||
}
|
||||
table
|
||||
});
|
||||
}
|
||||
|
||||
pub const INSTRUCTION_TABLES: InstructionTables = instruction_tables();
|
||||
|
||||
#[inline]
|
||||
pub fn get_instruction_table(revision: Revision) -> &'static InstructionTable {
|
||||
&INSTRUCTION_TABLES[revision as usize]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::execution::evm::{common::*, state::*};
|
||||
use crate::{
|
||||
execution::evm::{common::*, state::*, Host},
|
||||
models::Revision,
|
||||
};
|
||||
use ethnum::U256;
|
||||
use sha3::{Digest, Keccak256};
|
||||
use std::{cmp::min, num::NonZeroUsize};
|
||||
@@ -10,12 +13,12 @@ const WORD_SIZE: i64 = 32;
|
||||
|
||||
/// Returns number of words what would fit to provided number of bytes,
|
||||
/// i.e. it rounds up the number bytes to number of words.
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn num_words(size_in_bytes: usize) -> i64 {
|
||||
((size_in_bytes as i64) + (WORD_SIZE - 1)) / WORD_SIZE
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
let index = state.stack.pop();
|
||||
|
||||
@@ -29,7 +32,7 @@ pub(crate) fn mload(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
let index = state.stack.pop();
|
||||
let value = state.stack.pop();
|
||||
@@ -42,7 +45,7 @@ pub(crate) fn mstore(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
let index = state.stack.pop();
|
||||
let value = state.stack.pop();
|
||||
@@ -57,14 +60,14 @@ pub(crate) fn mstore8(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn msize(state: &mut ExecutionState) {
|
||||
state
|
||||
.stack
|
||||
.push(u64::try_from(state.memory.len()).unwrap().into());
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[inline]
|
||||
fn grow_memory(state: &mut ExecutionState, new_size: usize) -> Result<(), ()> {
|
||||
let new_words = num_words(new_size);
|
||||
let current_words = (state.memory.len() / 32) as i64;
|
||||
@@ -83,7 +86,7 @@ fn grow_memory(state: &mut ExecutionState, new_size: usize) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn get_memory_region_u64(
|
||||
state: &mut ExecutionState,
|
||||
offset: U256,
|
||||
@@ -110,7 +113,7 @@ pub(crate) struct MemoryRegion {
|
||||
pub size: NonZeroUsize,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn get_memory_region(
|
||||
state: &mut ExecutionState,
|
||||
offset: U256,
|
||||
@@ -127,6 +130,7 @@ pub(crate) fn get_memory_region(
|
||||
get_memory_region_u64(state, offset, NonZeroUsize::new(size.as_usize()).unwrap()).map(Some)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn calldatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
let mem_index = state.stack.pop();
|
||||
let input_index = state.stack.pop();
|
||||
@@ -186,11 +190,12 @@ pub(crate) fn keccak256(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn codesize(stack: &mut Stack, code: &[u8]) {
|
||||
stack.push(u128::try_from(code.len()).unwrap().into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), StatusCode> {
|
||||
// TODO: Similar to calldatacopy().
|
||||
|
||||
@@ -224,80 +229,70 @@ pub(crate) fn codecopy(state: &mut ExecutionState, code: &[u8]) -> Result<(), St
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! extcodecopy {
|
||||
($state:expr,$rev:expr) => {
|
||||
use core::cmp::min;
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
common::*,
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
instructions::{memory::*, properties::*},
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn extcodecopy<H: Host, const REVISION: Revision>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{
|
||||
common::*,
|
||||
host::*,
|
||||
instructions::{memory::*, properties::*},
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
|
||||
let addr = u256_to_address($state.stack.pop());
|
||||
let mem_index = $state.stack.pop();
|
||||
let input_index = $state.stack.pop();
|
||||
let size = $state.stack.pop();
|
||||
let addr = u256_to_address(state.stack.pop());
|
||||
let mem_index = state.stack.pop();
|
||||
let input_index = state.stack.pop();
|
||||
let size = state.stack.pop();
|
||||
|
||||
let region =
|
||||
get_memory_region(&mut $state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;
|
||||
let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;
|
||||
|
||||
if let Some(region) = ®ion {
|
||||
let copy_cost = num_words(region.size.get()) * 3;
|
||||
$state.gas_left -= copy_cost;
|
||||
if $state.gas_left < 0 {
|
||||
if let Some(region) = ®ion {
|
||||
let copy_cost = num_words(region.size.get()) * 3;
|
||||
state.gas_left -= copy_cost;
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_account(addr) == AccessStatus::Cold {
|
||||
state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if $rev >= Revision::Berlin {
|
||||
if ResumeData::into_access_account_status({
|
||||
yield InterruptData::AccessAccount { address: addr }
|
||||
})
|
||||
.unwrap()
|
||||
.status
|
||||
== AccessStatus::Cold
|
||||
{
|
||||
$state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
if let Some(region) = region {
|
||||
let src = min(U256::from(MAX_BUFFER_SIZE), input_index).as_usize();
|
||||
|
||||
let mut code = vec![0; region.size.get()];
|
||||
let copied = host.copy_code(addr, src, &mut code[..]);
|
||||
debug_assert!(copied <= code.len());
|
||||
code.truncate(copied);
|
||||
|
||||
state.memory[region.offset..region.offset + code.len()].copy_from_slice(&code);
|
||||
if region.size.get() > code.len() {
|
||||
state.memory[region.offset + code.len()..region.offset + region.size.get()].fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(region) = region {
|
||||
let src = min(U256::from(MAX_BUFFER_SIZE), input_index).as_usize();
|
||||
|
||||
let code = ResumeData::into_code({
|
||||
yield InterruptData::CopyCode {
|
||||
address: addr,
|
||||
offset: src,
|
||||
max_size: region.size.get(),
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
.code;
|
||||
|
||||
$state.memory[region.offset..region.offset + code.len()].copy_from_slice(&code);
|
||||
if region.size.get() > code.len() {
|
||||
$state.memory[region.offset + code.len()..region.offset + region.size.get()]
|
||||
.fill(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn returndatasize(state: &mut ExecutionState) {
|
||||
state
|
||||
.stack
|
||||
.push(u128::try_from(state.return_data.len()).unwrap().into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCode> {
|
||||
let mem_index = state.stack.pop();
|
||||
let input_index = state.stack.pop();
|
||||
@@ -305,7 +300,7 @@ pub(crate) fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCod
|
||||
|
||||
let region = get_memory_region(state, mem_index, size).map_err(|_| StatusCode::OutOfGas)?;
|
||||
|
||||
if input_index > u128::try_from(state.return_data.len()).unwrap() {
|
||||
if input_index > state.return_data.len() as u128 {
|
||||
return Err(StatusCode::InvalidMemoryAccess);
|
||||
}
|
||||
let src = input_index.as_usize();
|
||||
@@ -328,41 +323,29 @@ pub(crate) fn returndatacopy(state: &mut ExecutionState) -> Result<(), StatusCod
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! extcodehash {
|
||||
($state:expr,$rev:expr) => {
|
||||
use $crate::{
|
||||
execution::evm::{
|
||||
common::*,
|
||||
continuation::{interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
instructions::properties::*,
|
||||
},
|
||||
models::*,
|
||||
};
|
||||
#[inline]
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn extcodehash<H: Host, const REVISION: Revision>(
|
||||
state: &mut ExecutionState,
|
||||
host: &mut H,
|
||||
) -> Result<(), StatusCode> {
|
||||
use crate::{
|
||||
execution::evm::{common::*, host::*, instructions::properties::*},
|
||||
models::*,
|
||||
};
|
||||
|
||||
let addr = u256_to_address($state.stack.pop());
|
||||
let addr = u256_to_address(state.stack.pop());
|
||||
|
||||
if $rev >= Revision::Berlin {
|
||||
if ResumeData::into_access_account_status({
|
||||
yield InterruptData::AccessAccount { address: addr }
|
||||
})
|
||||
.unwrap()
|
||||
.status
|
||||
== AccessStatus::Cold
|
||||
{
|
||||
$state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if $state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
if REVISION >= Revision::Berlin {
|
||||
if host.access_account(addr) == AccessStatus::Cold {
|
||||
state.gas_left -= i64::from(ADDITIONAL_COLD_ACCOUNT_ACCESS_COST);
|
||||
if state.gas_left < 0 {
|
||||
return Err(StatusCode::OutOfGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let code_hash =
|
||||
ResumeData::into_code_hash({ yield InterruptData::GetCodeHash { address: addr } })
|
||||
.unwrap()
|
||||
.hash;
|
||||
$state.stack.push(code_hash);
|
||||
};
|
||||
state.stack.push(host.get_code_hash(addr));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::{execution::evm::opcode::*, models::*};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub(crate) const COLD_SLOAD_COST: u16 = 2100;
|
||||
pub(crate) const COLD_ACCOUNT_ACCESS_COST: u16 = 2600;
|
||||
@@ -22,7 +21,7 @@ pub struct Properties {
|
||||
}
|
||||
|
||||
impl Properties {
|
||||
fn new(stack_height_required: u8, stack_height_change: i8) -> Self {
|
||||
const fn new(stack_height_required: u8, stack_height_change: i8) -> Self {
|
||||
Self {
|
||||
stack_height_required,
|
||||
stack_height_change,
|
||||
@@ -30,7 +29,395 @@ impl Properties {
|
||||
}
|
||||
}
|
||||
|
||||
pub static PROPERTIES: Lazy<[Option<Properties>; 256]> = Lazy::new(|| {
|
||||
pub const fn properties<const OPCODE: OpCode>() -> Properties {
|
||||
match OPCODE {
|
||||
OpCode::STOP => Properties::new(0, 0),
|
||||
OpCode::ADD => Properties::new(2, -1),
|
||||
OpCode::MUL => Properties::new(2, -1),
|
||||
OpCode::SUB => Properties::new(2, -1),
|
||||
OpCode::DIV => Properties::new(2, -1),
|
||||
OpCode::SDIV => Properties::new(2, -1),
|
||||
OpCode::MOD => Properties::new(2, -1),
|
||||
OpCode::SMOD => Properties::new(2, -1),
|
||||
OpCode::ADDMOD => Properties::new(3, -2),
|
||||
OpCode::MULMOD => Properties::new(3, -2),
|
||||
OpCode::EXP => Properties::new(2, -1),
|
||||
OpCode::SIGNEXTEND => Properties::new(2, -1),
|
||||
|
||||
OpCode::LT => Properties::new(2, -1),
|
||||
OpCode::GT => Properties::new(2, -1),
|
||||
OpCode::SLT => Properties::new(2, -1),
|
||||
OpCode::SGT => Properties::new(2, -1),
|
||||
OpCode::EQ => Properties::new(2, -1),
|
||||
OpCode::ISZERO => Properties::new(1, 0),
|
||||
OpCode::AND => Properties::new(2, -1),
|
||||
OpCode::OR => Properties::new(2, -1),
|
||||
OpCode::XOR => Properties::new(2, -1),
|
||||
OpCode::NOT => Properties::new(1, 0),
|
||||
OpCode::BYTE => Properties::new(2, -1),
|
||||
OpCode::SHL => Properties::new(2, -1),
|
||||
OpCode::SHR => Properties::new(2, -1),
|
||||
OpCode::SAR => Properties::new(2, -1),
|
||||
|
||||
OpCode::KECCAK256 => Properties::new(2, -1),
|
||||
|
||||
OpCode::ADDRESS => Properties::new(0, 1),
|
||||
OpCode::BALANCE => Properties::new(1, 0),
|
||||
OpCode::ORIGIN => Properties::new(0, 1),
|
||||
OpCode::CALLER => Properties::new(0, 1),
|
||||
OpCode::CALLVALUE => Properties::new(0, 1),
|
||||
OpCode::CALLDATALOAD => Properties::new(1, 0),
|
||||
OpCode::CALLDATASIZE => Properties::new(0, 1),
|
||||
OpCode::CALLDATACOPY => Properties::new(3, -3),
|
||||
OpCode::CODESIZE => Properties::new(0, 1),
|
||||
OpCode::CODECOPY => Properties::new(3, -3),
|
||||
OpCode::GASPRICE => Properties::new(0, 1),
|
||||
OpCode::EXTCODESIZE => Properties::new(1, 0),
|
||||
OpCode::EXTCODECOPY => Properties::new(4, -4),
|
||||
OpCode::RETURNDATASIZE => Properties::new(0, 1),
|
||||
OpCode::RETURNDATACOPY => Properties::new(3, -3),
|
||||
OpCode::EXTCODEHASH => Properties::new(1, 0),
|
||||
|
||||
OpCode::BLOCKHASH => Properties::new(1, 0),
|
||||
OpCode::COINBASE => Properties::new(0, 1),
|
||||
OpCode::TIMESTAMP => Properties::new(0, 1),
|
||||
OpCode::NUMBER => Properties::new(0, 1),
|
||||
OpCode::DIFFICULTY => Properties::new(0, 1),
|
||||
OpCode::GASLIMIT => Properties::new(0, 1),
|
||||
OpCode::CHAINID => Properties::new(0, 1),
|
||||
OpCode::SELFBALANCE => Properties::new(0, 1),
|
||||
OpCode::BASEFEE => Properties::new(0, 1),
|
||||
|
||||
OpCode::POP => Properties::new(1, -1),
|
||||
OpCode::MLOAD => Properties::new(1, 0),
|
||||
OpCode::MSTORE => Properties::new(2, -2),
|
||||
OpCode::MSTORE8 => Properties::new(2, -2),
|
||||
OpCode::SLOAD => Properties::new(1, 0),
|
||||
OpCode::SSTORE => Properties::new(2, -2),
|
||||
OpCode::JUMP => Properties::new(1, -1),
|
||||
OpCode::JUMPI => Properties::new(2, -2),
|
||||
OpCode::PC => Properties::new(0, 1),
|
||||
OpCode::MSIZE => Properties::new(0, 1),
|
||||
OpCode::GAS => Properties::new(0, 1),
|
||||
OpCode::JUMPDEST => Properties::new(0, 0),
|
||||
|
||||
OpCode::PUSH1 => Properties::new(0, 1),
|
||||
OpCode::PUSH2 => Properties::new(0, 1),
|
||||
OpCode::PUSH3 => Properties::new(0, 1),
|
||||
OpCode::PUSH4 => Properties::new(0, 1),
|
||||
OpCode::PUSH5 => Properties::new(0, 1),
|
||||
OpCode::PUSH6 => Properties::new(0, 1),
|
||||
OpCode::PUSH7 => Properties::new(0, 1),
|
||||
OpCode::PUSH8 => Properties::new(0, 1),
|
||||
OpCode::PUSH9 => Properties::new(0, 1),
|
||||
OpCode::PUSH10 => Properties::new(0, 1),
|
||||
OpCode::PUSH11 => Properties::new(0, 1),
|
||||
OpCode::PUSH12 => Properties::new(0, 1),
|
||||
OpCode::PUSH13 => Properties::new(0, 1),
|
||||
OpCode::PUSH14 => Properties::new(0, 1),
|
||||
OpCode::PUSH15 => Properties::new(0, 1),
|
||||
OpCode::PUSH16 => Properties::new(0, 1),
|
||||
OpCode::PUSH17 => Properties::new(0, 1),
|
||||
OpCode::PUSH18 => Properties::new(0, 1),
|
||||
OpCode::PUSH19 => Properties::new(0, 1),
|
||||
OpCode::PUSH20 => Properties::new(0, 1),
|
||||
OpCode::PUSH21 => Properties::new(0, 1),
|
||||
OpCode::PUSH22 => Properties::new(0, 1),
|
||||
OpCode::PUSH23 => Properties::new(0, 1),
|
||||
OpCode::PUSH24 => Properties::new(0, 1),
|
||||
OpCode::PUSH25 => Properties::new(0, 1),
|
||||
OpCode::PUSH26 => Properties::new(0, 1),
|
||||
OpCode::PUSH27 => Properties::new(0, 1),
|
||||
OpCode::PUSH28 => Properties::new(0, 1),
|
||||
OpCode::PUSH29 => Properties::new(0, 1),
|
||||
OpCode::PUSH30 => Properties::new(0, 1),
|
||||
OpCode::PUSH31 => Properties::new(0, 1),
|
||||
OpCode::PUSH32 => Properties::new(0, 1),
|
||||
|
||||
OpCode::DUP1 => Properties::new(1, 1),
|
||||
OpCode::DUP2 => Properties::new(2, 1),
|
||||
OpCode::DUP3 => Properties::new(3, 1),
|
||||
OpCode::DUP4 => Properties::new(4, 1),
|
||||
OpCode::DUP5 => Properties::new(5, 1),
|
||||
OpCode::DUP6 => Properties::new(6, 1),
|
||||
OpCode::DUP7 => Properties::new(7, 1),
|
||||
OpCode::DUP8 => Properties::new(8, 1),
|
||||
OpCode::DUP9 => Properties::new(9, 1),
|
||||
OpCode::DUP10 => Properties::new(10, 1),
|
||||
OpCode::DUP11 => Properties::new(11, 1),
|
||||
OpCode::DUP12 => Properties::new(12, 1),
|
||||
OpCode::DUP13 => Properties::new(13, 1),
|
||||
OpCode::DUP14 => Properties::new(14, 1),
|
||||
OpCode::DUP15 => Properties::new(15, 1),
|
||||
OpCode::DUP16 => Properties::new(16, 1),
|
||||
|
||||
OpCode::SWAP1 => Properties::new(2, 0),
|
||||
OpCode::SWAP2 => Properties::new(3, 0),
|
||||
OpCode::SWAP3 => Properties::new(4, 0),
|
||||
OpCode::SWAP4 => Properties::new(5, 0),
|
||||
OpCode::SWAP5 => Properties::new(6, 0),
|
||||
OpCode::SWAP6 => Properties::new(7, 0),
|
||||
OpCode::SWAP7 => Properties::new(8, 0),
|
||||
OpCode::SWAP8 => Properties::new(9, 0),
|
||||
OpCode::SWAP9 => Properties::new(10, 0),
|
||||
OpCode::SWAP10 => Properties::new(11, 0),
|
||||
OpCode::SWAP11 => Properties::new(12, 0),
|
||||
OpCode::SWAP12 => Properties::new(13, 0),
|
||||
OpCode::SWAP13 => Properties::new(14, 0),
|
||||
OpCode::SWAP14 => Properties::new(15, 0),
|
||||
OpCode::SWAP15 => Properties::new(16, 0),
|
||||
OpCode::SWAP16 => Properties::new(17, 0),
|
||||
|
||||
OpCode::LOG0 => Properties::new(2, -2),
|
||||
OpCode::LOG1 => Properties::new(3, -3),
|
||||
OpCode::LOG2 => Properties::new(4, -4),
|
||||
OpCode::LOG3 => Properties::new(5, -5),
|
||||
OpCode::LOG4 => Properties::new(6, -6),
|
||||
|
||||
OpCode::CREATE => Properties::new(3, -2),
|
||||
OpCode::CALL => Properties::new(7, -6),
|
||||
OpCode::CALLCODE => Properties::new(7, -6),
|
||||
OpCode::RETURN => Properties::new(2, -2),
|
||||
OpCode::DELEGATECALL => Properties::new(6, -5),
|
||||
OpCode::CREATE2 => Properties::new(4, -3),
|
||||
OpCode::STATICCALL => Properties::new(6, -5),
|
||||
OpCode::REVERT => Properties::new(2, -2),
|
||||
OpCode::INVALID => Properties::new(0, 0),
|
||||
OpCode::SELFDESTRUCT => Properties::new(1, -1),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub type GasCostTable = [[i16; 256]; Revision::len()];
|
||||
|
||||
const fn gas_costs() -> GasCostTable {
|
||||
let mut table = [[-1; 256]; Revision::len()];
|
||||
|
||||
table[Revision::Frontier as usize][OpCode::STOP.to_usize()] = 0;
|
||||
table[Revision::Frontier as usize][OpCode::ADD.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::MUL.to_usize()] = 5;
|
||||
table[Revision::Frontier as usize][OpCode::SUB.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DIV.to_usize()] = 5;
|
||||
table[Revision::Frontier as usize][OpCode::SDIV.to_usize()] = 5;
|
||||
table[Revision::Frontier as usize][OpCode::MOD.to_usize()] = 5;
|
||||
table[Revision::Frontier as usize][OpCode::SMOD.to_usize()] = 5;
|
||||
table[Revision::Frontier as usize][OpCode::ADDMOD.to_usize()] = 8;
|
||||
table[Revision::Frontier as usize][OpCode::MULMOD.to_usize()] = 8;
|
||||
table[Revision::Frontier as usize][OpCode::EXP.to_usize()] = 10;
|
||||
table[Revision::Frontier as usize][OpCode::SIGNEXTEND.to_usize()] = 5;
|
||||
table[Revision::Frontier as usize][OpCode::LT.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::GT.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SLT.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SGT.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::EQ.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::ISZERO.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::AND.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::OR.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::XOR.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::NOT.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::BYTE.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::KECCAK256.to_usize()] = 30;
|
||||
table[Revision::Frontier as usize][OpCode::ADDRESS.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::BALANCE.to_usize()] = 20;
|
||||
table[Revision::Frontier as usize][OpCode::ORIGIN.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::CALLER.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::CALLVALUE.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::CALLDATALOAD.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::CALLDATASIZE.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::CALLDATACOPY.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::CODESIZE.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::CODECOPY.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::GASPRICE.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::EXTCODESIZE.to_usize()] = 20;
|
||||
table[Revision::Frontier as usize][OpCode::EXTCODECOPY.to_usize()] = 20;
|
||||
table[Revision::Frontier as usize][OpCode::BLOCKHASH.to_usize()] = 20;
|
||||
table[Revision::Frontier as usize][OpCode::COINBASE.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::TIMESTAMP.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::NUMBER.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::DIFFICULTY.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::GASLIMIT.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::POP.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::MLOAD.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::MSTORE.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::MSTORE8.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SLOAD.to_usize()] = 50;
|
||||
table[Revision::Frontier as usize][OpCode::SSTORE.to_usize()] = 0;
|
||||
table[Revision::Frontier as usize][OpCode::JUMP.to_usize()] = 8;
|
||||
table[Revision::Frontier as usize][OpCode::JUMPI.to_usize()] = 10;
|
||||
table[Revision::Frontier as usize][OpCode::PC.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::MSIZE.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::GAS.to_usize()] = 2;
|
||||
table[Revision::Frontier as usize][OpCode::JUMPDEST.to_usize()] = 1;
|
||||
|
||||
table[Revision::Frontier as usize][OpCode::PUSH1.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH2.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH3.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH4.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH5.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH6.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH7.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH8.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH9.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH10.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH11.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH12.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH13.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH14.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH15.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH16.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH17.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH18.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH19.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH20.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH21.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH22.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH23.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH24.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH25.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH26.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH27.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH28.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH29.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH30.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH31.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::PUSH32.to_usize()] = 3;
|
||||
|
||||
table[Revision::Frontier as usize][OpCode::DUP1.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP2.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP3.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP4.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP5.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP6.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP7.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP8.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP9.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP10.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP11.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP12.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP13.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP14.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP15.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::DUP16.to_usize()] = 3;
|
||||
|
||||
table[Revision::Frontier as usize][OpCode::SWAP1.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP2.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP3.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP4.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP5.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP6.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP7.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP8.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP9.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP10.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP11.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP12.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP13.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP14.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP15.to_usize()] = 3;
|
||||
table[Revision::Frontier as usize][OpCode::SWAP16.to_usize()] = 3;
|
||||
|
||||
table[Revision::Frontier as usize][OpCode::LOG0.to_usize()] = 375;
|
||||
table[Revision::Frontier as usize][OpCode::LOG1.to_usize()] = 2 * 375;
|
||||
table[Revision::Frontier as usize][OpCode::LOG2.to_usize()] = 3 * 375;
|
||||
table[Revision::Frontier as usize][OpCode::LOG3.to_usize()] = 4 * 375;
|
||||
table[Revision::Frontier as usize][OpCode::LOG4.to_usize()] = 5 * 375;
|
||||
|
||||
table[Revision::Frontier as usize][OpCode::CREATE.to_usize()] = 32000;
|
||||
table[Revision::Frontier as usize][OpCode::CALL.to_usize()] = 40;
|
||||
table[Revision::Frontier as usize][OpCode::CALLCODE.to_usize()] = 40;
|
||||
table[Revision::Frontier as usize][OpCode::RETURN.to_usize()] = 0;
|
||||
table[Revision::Frontier as usize][OpCode::INVALID.to_usize()] = 0;
|
||||
table[Revision::Frontier as usize][OpCode::SELFDESTRUCT.to_usize()] = 0;
|
||||
|
||||
table[Revision::Homestead as usize] = table[Revision::Frontier as usize];
|
||||
table[Revision::Homestead as usize][OpCode::DELEGATECALL.to_usize()] = 40;
|
||||
|
||||
table[Revision::Tangerine as usize] = table[Revision::Homestead as usize];
|
||||
table[Revision::Tangerine as usize][OpCode::BALANCE.to_usize()] = 400;
|
||||
table[Revision::Tangerine as usize][OpCode::EXTCODESIZE.to_usize()] = 700;
|
||||
table[Revision::Tangerine as usize][OpCode::EXTCODECOPY.to_usize()] = 700;
|
||||
table[Revision::Tangerine as usize][OpCode::SLOAD.to_usize()] = 200;
|
||||
table[Revision::Tangerine as usize][OpCode::CALL.to_usize()] = 700;
|
||||
table[Revision::Tangerine as usize][OpCode::CALLCODE.to_usize()] = 700;
|
||||
table[Revision::Tangerine as usize][OpCode::DELEGATECALL.to_usize()] = 700;
|
||||
table[Revision::Tangerine as usize][OpCode::SELFDESTRUCT.to_usize()] = 5000;
|
||||
|
||||
table[Revision::Spurious as usize] = table[Revision::Tangerine as usize];
|
||||
|
||||
table[Revision::Byzantium as usize] = table[Revision::Spurious as usize];
|
||||
table[Revision::Byzantium as usize][OpCode::RETURNDATASIZE.to_usize()] = 2;
|
||||
table[Revision::Byzantium as usize][OpCode::RETURNDATACOPY.to_usize()] = 3;
|
||||
table[Revision::Byzantium as usize][OpCode::STATICCALL.to_usize()] = 700;
|
||||
table[Revision::Byzantium as usize][OpCode::REVERT.to_usize()] = 0;
|
||||
|
||||
table[Revision::Constantinople as usize] = table[Revision::Byzantium as usize];
|
||||
table[Revision::Constantinople as usize][OpCode::SHL.to_usize()] = 3;
|
||||
table[Revision::Constantinople as usize][OpCode::SHR.to_usize()] = 3;
|
||||
table[Revision::Constantinople as usize][OpCode::SAR.to_usize()] = 3;
|
||||
table[Revision::Constantinople as usize][OpCode::EXTCODEHASH.to_usize()] = 400;
|
||||
table[Revision::Constantinople as usize][OpCode::CREATE2.to_usize()] = 32000;
|
||||
|
||||
table[Revision::Petersburg as usize] = table[Revision::Constantinople as usize];
|
||||
|
||||
table[Revision::Istanbul as usize] = table[Revision::Petersburg as usize];
|
||||
table[Revision::Istanbul as usize][OpCode::BALANCE.to_usize()] = 700;
|
||||
table[Revision::Istanbul as usize][OpCode::CHAINID.to_usize()] = 2;
|
||||
table[Revision::Istanbul as usize][OpCode::EXTCODEHASH.to_usize()] = 700;
|
||||
table[Revision::Istanbul as usize][OpCode::SELFBALANCE.to_usize()] = 5;
|
||||
table[Revision::Istanbul as usize][OpCode::SLOAD.to_usize()] = 800;
|
||||
|
||||
table[Revision::Berlin as usize] = table[Revision::Istanbul as usize];
|
||||
table[Revision::Berlin as usize][OpCode::EXTCODESIZE.to_usize()] =
|
||||
WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::EXTCODECOPY.to_usize()] =
|
||||
WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::EXTCODEHASH.to_usize()] =
|
||||
WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::BALANCE.to_usize()] = WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::CALL.to_usize()] = WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::CALLCODE.to_usize()] = WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::DELEGATECALL.to_usize()] =
|
||||
WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::STATICCALL.to_usize()] = WARM_STORAGE_READ_COST as i16;
|
||||
table[Revision::Berlin as usize][OpCode::SLOAD.to_usize()] = WARM_STORAGE_READ_COST as i16;
|
||||
|
||||
table[Revision::London as usize] = table[Revision::Berlin as usize];
|
||||
table[Revision::London as usize][OpCode::BASEFEE.to_usize()] = 2;
|
||||
|
||||
table[Revision::Shanghai as usize] = table[Revision::London as usize];
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
pub const GAS_COSTS: GasCostTable = gas_costs();
|
||||
|
||||
pub const fn has_const_gas_cost<const OPCODE: OpCode>() -> bool {
|
||||
const LATEST: Revision = Revision::latest();
|
||||
|
||||
let g = GAS_COSTS[Revision::Frontier as usize][OPCODE.to_usize()];
|
||||
let revtable = Revision::iter();
|
||||
let mut iter = 0;
|
||||
loop {
|
||||
let rev = revtable[iter];
|
||||
|
||||
if GAS_COSTS[rev as usize][OPCODE.to_usize()] != g {
|
||||
return false;
|
||||
}
|
||||
|
||||
if matches!(rev, LATEST) {
|
||||
break;
|
||||
} else {
|
||||
iter += 1;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub const fn opcode_gas_cost<const REVISION: Revision, const OPCODE: OpCode>() -> i16 {
|
||||
GAS_COSTS[REVISION as usize][OPCODE.to_usize()]
|
||||
}
|
||||
|
||||
const fn property_table() -> [Option<Properties>; 256] {
|
||||
let mut table = [None; 256];
|
||||
|
||||
table[OpCode::STOP.to_usize()] = Some(Properties::new(0, 0));
|
||||
@@ -188,184 +575,21 @@ pub static PROPERTIES: Lazy<[Option<Properties>; 256]> = Lazy::new(|| {
|
||||
table[OpCode::SELFDESTRUCT.to_usize()] = Some(Properties::new(1, -1));
|
||||
|
||||
table
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
static FRONTIER_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = [None; 256];
|
||||
pub const PROPERTIES: [Option<Properties>; 256] = property_table();
|
||||
|
||||
table[OpCode::STOP.to_usize()] = Some(0);
|
||||
table[OpCode::ADD.to_usize()] = Some(3);
|
||||
table[OpCode::MUL.to_usize()] = Some(5);
|
||||
table[OpCode::SUB.to_usize()] = Some(3);
|
||||
table[OpCode::DIV.to_usize()] = Some(5);
|
||||
table[OpCode::SDIV.to_usize()] = Some(5);
|
||||
table[OpCode::MOD.to_usize()] = Some(5);
|
||||
table[OpCode::SMOD.to_usize()] = Some(5);
|
||||
table[OpCode::ADDMOD.to_usize()] = Some(8);
|
||||
table[OpCode::MULMOD.to_usize()] = Some(8);
|
||||
table[OpCode::EXP.to_usize()] = Some(10);
|
||||
table[OpCode::SIGNEXTEND.to_usize()] = Some(5);
|
||||
table[OpCode::LT.to_usize()] = Some(3);
|
||||
table[OpCode::GT.to_usize()] = Some(3);
|
||||
table[OpCode::SLT.to_usize()] = Some(3);
|
||||
table[OpCode::SGT.to_usize()] = Some(3);
|
||||
table[OpCode::EQ.to_usize()] = Some(3);
|
||||
table[OpCode::ISZERO.to_usize()] = Some(3);
|
||||
table[OpCode::AND.to_usize()] = Some(3);
|
||||
table[OpCode::OR.to_usize()] = Some(3);
|
||||
table[OpCode::XOR.to_usize()] = Some(3);
|
||||
table[OpCode::NOT.to_usize()] = Some(3);
|
||||
table[OpCode::BYTE.to_usize()] = Some(3);
|
||||
table[OpCode::KECCAK256.to_usize()] = Some(30);
|
||||
table[OpCode::ADDRESS.to_usize()] = Some(2);
|
||||
table[OpCode::BALANCE.to_usize()] = Some(20);
|
||||
table[OpCode::ORIGIN.to_usize()] = Some(2);
|
||||
table[OpCode::CALLER.to_usize()] = Some(2);
|
||||
table[OpCode::CALLVALUE.to_usize()] = Some(2);
|
||||
table[OpCode::CALLDATALOAD.to_usize()] = Some(3);
|
||||
table[OpCode::CALLDATASIZE.to_usize()] = Some(2);
|
||||
table[OpCode::CALLDATACOPY.to_usize()] = Some(3);
|
||||
table[OpCode::CODESIZE.to_usize()] = Some(2);
|
||||
table[OpCode::CODECOPY.to_usize()] = Some(3);
|
||||
table[OpCode::GASPRICE.to_usize()] = Some(2);
|
||||
table[OpCode::EXTCODESIZE.to_usize()] = Some(20);
|
||||
table[OpCode::EXTCODECOPY.to_usize()] = Some(20);
|
||||
table[OpCode::BLOCKHASH.to_usize()] = Some(20);
|
||||
table[OpCode::COINBASE.to_usize()] = Some(2);
|
||||
table[OpCode::TIMESTAMP.to_usize()] = Some(2);
|
||||
table[OpCode::NUMBER.to_usize()] = Some(2);
|
||||
table[OpCode::DIFFICULTY.to_usize()] = Some(2);
|
||||
table[OpCode::GASLIMIT.to_usize()] = Some(2);
|
||||
table[OpCode::POP.to_usize()] = Some(2);
|
||||
table[OpCode::MLOAD.to_usize()] = Some(3);
|
||||
table[OpCode::MSTORE.to_usize()] = Some(3);
|
||||
table[OpCode::MSTORE8.to_usize()] = Some(3);
|
||||
table[OpCode::SLOAD.to_usize()] = Some(50);
|
||||
table[OpCode::SSTORE.to_usize()] = Some(0);
|
||||
table[OpCode::JUMP.to_usize()] = Some(8);
|
||||
table[OpCode::JUMPI.to_usize()] = Some(10);
|
||||
table[OpCode::PC.to_usize()] = Some(2);
|
||||
table[OpCode::MSIZE.to_usize()] = Some(2);
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
table[OpCode::GAS.to_usize()] = Some(2);
|
||||
table[OpCode::JUMPDEST.to_usize()] = Some(1);
|
||||
|
||||
for op in OpCode::PUSH1.to_usize()..=OpCode::PUSH32.to_usize() {
|
||||
table[op] = Some(3);
|
||||
}
|
||||
|
||||
for op in OpCode::DUP1.to_usize()..=OpCode::DUP16.to_usize() {
|
||||
table[op] = Some(3);
|
||||
}
|
||||
|
||||
for op in OpCode::SWAP1.to_usize()..=OpCode::SWAP16.to_usize() {
|
||||
table[op] = Some(3);
|
||||
}
|
||||
|
||||
for (i, op) in (OpCode::LOG0.to_usize()..=OpCode::LOG4.to_usize())
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
table[op] = Some((1 + i as u16) * 375);
|
||||
}
|
||||
|
||||
table[OpCode::CREATE.to_usize()] = Some(32000);
|
||||
table[OpCode::CALL.to_usize()] = Some(40);
|
||||
table[OpCode::CALLCODE.to_usize()] = Some(40);
|
||||
table[OpCode::RETURN.to_usize()] = Some(0);
|
||||
table[OpCode::INVALID.to_usize()] = Some(0);
|
||||
table[OpCode::SELFDESTRUCT.to_usize()] = Some(0);
|
||||
|
||||
table
|
||||
});
|
||||
|
||||
static HOMESTEAD_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = *FRONTIER_GAS_COSTS;
|
||||
table[OpCode::DELEGATECALL.to_usize()] = Some(40);
|
||||
table
|
||||
});
|
||||
|
||||
static TANGERINE_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = *HOMESTEAD_GAS_COSTS;
|
||||
table[OpCode::BALANCE.to_usize()] = Some(400);
|
||||
table[OpCode::EXTCODESIZE.to_usize()] = Some(700);
|
||||
table[OpCode::EXTCODECOPY.to_usize()] = Some(700);
|
||||
table[OpCode::SLOAD.to_usize()] = Some(200);
|
||||
table[OpCode::CALL.to_usize()] = Some(700);
|
||||
table[OpCode::CALLCODE.to_usize()] = Some(700);
|
||||
table[OpCode::DELEGATECALL.to_usize()] = Some(700);
|
||||
table[OpCode::SELFDESTRUCT.to_usize()] = Some(5000);
|
||||
table
|
||||
});
|
||||
|
||||
static SPURIOUS_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *TANGERINE_GAS_COSTS);
|
||||
|
||||
static BYZANTIUM_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = *SPURIOUS_GAS_COSTS;
|
||||
table[OpCode::RETURNDATASIZE.to_usize()] = Some(2);
|
||||
table[OpCode::RETURNDATACOPY.to_usize()] = Some(3);
|
||||
table[OpCode::STATICCALL.to_usize()] = Some(700);
|
||||
table[OpCode::REVERT.to_usize()] = Some(0);
|
||||
table
|
||||
});
|
||||
|
||||
static CONSTANTINOPLE_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = *BYZANTIUM_GAS_COSTS;
|
||||
table[OpCode::SHL.to_usize()] = Some(3);
|
||||
table[OpCode::SHR.to_usize()] = Some(3);
|
||||
table[OpCode::SAR.to_usize()] = Some(3);
|
||||
table[OpCode::EXTCODEHASH.to_usize()] = Some(400);
|
||||
table[OpCode::CREATE2.to_usize()] = Some(32000);
|
||||
table
|
||||
});
|
||||
|
||||
static PETERSBURG_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *CONSTANTINOPLE_GAS_COSTS);
|
||||
|
||||
static ISTANBUL_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = *PETERSBURG_GAS_COSTS;
|
||||
table[OpCode::BALANCE.to_usize()] = Some(700);
|
||||
table[OpCode::CHAINID.to_usize()] = Some(2);
|
||||
table[OpCode::EXTCODEHASH.to_usize()] = Some(700);
|
||||
table[OpCode::SELFBALANCE.to_usize()] = Some(5);
|
||||
table[OpCode::SLOAD.to_usize()] = Some(800);
|
||||
table
|
||||
});
|
||||
|
||||
static BERLIN_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = *ISTANBUL_GAS_COSTS;
|
||||
table[OpCode::EXTCODESIZE.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::EXTCODECOPY.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::EXTCODEHASH.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::BALANCE.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::CALL.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::CALLCODE.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::DELEGATECALL.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::STATICCALL.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table[OpCode::SLOAD.to_usize()] = Some(WARM_STORAGE_READ_COST);
|
||||
table
|
||||
});
|
||||
|
||||
static LONDON_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| {
|
||||
let mut table = *BERLIN_GAS_COSTS;
|
||||
table[OpCode::BASEFEE.to_usize()] = Some(2);
|
||||
table
|
||||
});
|
||||
|
||||
static SHANGHAI_GAS_COSTS: Lazy<[Option<u16>; 256]> = Lazy::new(|| *LONDON_GAS_COSTS);
|
||||
|
||||
pub fn gas_costs(revision: Revision) -> &'static [Option<u16>; 256] {
|
||||
match revision {
|
||||
Revision::Frontier => &FRONTIER_GAS_COSTS,
|
||||
Revision::Homestead => &HOMESTEAD_GAS_COSTS,
|
||||
Revision::Tangerine => &TANGERINE_GAS_COSTS,
|
||||
Revision::Spurious => &SPURIOUS_GAS_COSTS,
|
||||
Revision::Byzantium => &BYZANTIUM_GAS_COSTS,
|
||||
Revision::Constantinople => &CONSTANTINOPLE_GAS_COSTS,
|
||||
Revision::Petersburg => &PETERSBURG_GAS_COSTS,
|
||||
Revision::Istanbul => &ISTANBUL_GAS_COSTS,
|
||||
Revision::Berlin => &BERLIN_GAS_COSTS,
|
||||
Revision::London => &LONDON_GAS_COSTS,
|
||||
Revision::Shanghai => &SHANGHAI_GAS_COSTS,
|
||||
#[test]
|
||||
fn const_gas_cost() {
|
||||
assert!(has_const_gas_cost::<{ OpCode::STOP }>());
|
||||
assert!(has_const_gas_cost::<{ OpCode::ADD }>());
|
||||
assert!(has_const_gas_cost::<{ OpCode::PUSH1 }>());
|
||||
assert!(!has_const_gas_cost::<{ OpCode::SHL }>());
|
||||
assert!(!has_const_gas_cost::<{ OpCode::BALANCE }>());
|
||||
assert!(!has_const_gas_cost::<{ OpCode::SLOAD }>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,33 @@ use crate::execution::evm::{common::*, state::*};
|
||||
use arrayref::array_ref;
|
||||
use ethnum::U256;
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn push<const LEN: usize>(stack: &mut Stack, code: &[u8]) -> usize {
|
||||
stack.push(u256_from_slice(&code[..LEN]));
|
||||
LEN
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn push1(stack: &mut Stack, v: u8) {
|
||||
stack.push(v.into());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn push32(stack: &mut Stack, code: &[u8]) {
|
||||
stack.push(U256::from_be_bytes(*array_ref!(code, 0, 32)));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn dup<const HEIGHT: usize>(stack: &mut Stack) {
|
||||
stack.push(*stack.get(HEIGHT - 1));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn swap<const HEIGHT: usize>(stack: &mut Stack) {
|
||||
stack.swap_top(HEIGHT);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub(crate) fn pop(stack: &mut Stack) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,14 +11,12 @@ pub use state::{ExecutionState, Stack};
|
||||
pub const MAX_CODE_SIZE: usize = 0x6000;
|
||||
|
||||
mod common;
|
||||
pub mod continuation;
|
||||
pub mod host;
|
||||
#[macro_use]
|
||||
pub mod instructions;
|
||||
mod interpreter;
|
||||
pub mod opcode;
|
||||
mod state;
|
||||
pub mod tracing;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -5,12 +5,12 @@ use std::{borrow::Cow, fmt::Display};
|
||||
pub struct OpCode(pub u8);
|
||||
|
||||
impl OpCode {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub const fn to_u8(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub const fn to_usize(self) -> usize {
|
||||
self.to_u8() as usize
|
||||
}
|
||||
@@ -322,7 +322,7 @@ impl OpCode {
|
||||
}
|
||||
|
||||
#[cfg(feature = "util")]
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn push_size(self) -> Option<u8> {
|
||||
(self.to_u8() >= OpCode::PUSH1.to_u8() && self.to_u8() <= OpCode::PUSH32.to_u8())
|
||||
.then(|| self.to_u8() - OpCode::PUSH1.to_u8() + 1)
|
||||
|
||||
@@ -13,29 +13,29 @@ pub const STACK_SIZE: usize = 1024;
|
||||
pub struct Stack(pub ArrayVec<U256, STACK_SIZE>);
|
||||
|
||||
impl Stack {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self(ArrayVec::new_const())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_pos(&self, pos: usize) -> usize {
|
||||
#[inline]
|
||||
const fn get_pos(&self, pos: usize) -> usize {
|
||||
self.len() - 1 - pos
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn get(&self, pos: usize) -> &U256 {
|
||||
&self.0[self.get_pos(pos)]
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self, pos: usize) -> &mut U256 {
|
||||
let pos = self.get_pos(pos);
|
||||
&mut self.0[pos]
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
pub const fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
@@ -44,17 +44,17 @@ impl Stack {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn push(&mut self, v: U256) {
|
||||
unsafe { self.0.push_unchecked(v) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn pop(&mut self) -> U256 {
|
||||
self.0.pop().expect("underflow")
|
||||
unsafe { self.0.pop_unchecked() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn swap_top(&mut self, pos: usize) {
|
||||
let top = self.0.len() - 1;
|
||||
let pos = self.get_pos(pos);
|
||||
@@ -68,17 +68,17 @@ const PAGE_SIZE: usize = 4 * 1024;
|
||||
pub struct Memory(BytesMut);
|
||||
|
||||
impl Memory {
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self(BytesMut::with_capacity(PAGE_SIZE))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[inline]
|
||||
pub fn grow(&mut self, size: usize) {
|
||||
let cap = self.0.capacity();
|
||||
if size > cap {
|
||||
let additional_pages = ((size - cap) + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
self.0.reserve(PAGE_SIZE * additional_pages);
|
||||
let required_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
self.0.reserve((PAGE_SIZE * required_pages) - self.0.len());
|
||||
}
|
||||
self.0.resize(size, 0);
|
||||
}
|
||||
@@ -92,21 +92,21 @@ impl Default for Memory {
|
||||
|
||||
/// EVM execution state.
|
||||
#[derive(Clone, Debug, Getters, MutGetters)]
|
||||
pub struct ExecutionState {
|
||||
pub struct ExecutionState<'m> {
|
||||
#[getset(get = "pub", get_mut = "pub")]
|
||||
pub(crate) gas_left: i64,
|
||||
#[getset(get = "pub", get_mut = "pub")]
|
||||
pub(crate) stack: Stack,
|
||||
#[getset(get = "pub", get_mut = "pub")]
|
||||
pub(crate) memory: Memory,
|
||||
pub(crate) message: InterpreterMessage,
|
||||
pub(crate) message: &'m InterpreterMessage,
|
||||
#[getset(get = "pub", get_mut = "pub")]
|
||||
pub(crate) return_data: Bytes,
|
||||
pub(crate) output_data: Bytes,
|
||||
}
|
||||
|
||||
impl ExecutionState {
|
||||
pub fn new(message: InterpreterMessage) -> Self {
|
||||
impl<'m> ExecutionState<'m> {
|
||||
pub fn new(message: &'m InterpreterMessage) -> Self {
|
||||
Self {
|
||||
gas_left: message.gas,
|
||||
stack: Stack::default(),
|
||||
@@ -139,4 +139,12 @@ mod tests {
|
||||
|
||||
assert_eq!(*stack.get(2), 0xde);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grow() {
|
||||
let mut mem = Memory::new();
|
||||
mem.grow(PAGE_SIZE * 2 + 1);
|
||||
assert_eq!(mem.len(), PAGE_SIZE * 2 + 1);
|
||||
assert_eq!(mem.capacity(), PAGE_SIZE * 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,8 @@ fn delegatecall() {
|
||||
.inspect_host(move |host, _| {
|
||||
let gas_left = 1700 - 736;
|
||||
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
let call_msg = r.calls.last().unwrap();
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = host.recorded.calls.last().unwrap();
|
||||
assert_eq!(call_msg.gas, gas_left - gas_left / 64);
|
||||
assert_eq!(call_msg.input_data.len(), 3);
|
||||
assert_eq!(call_msg.value.to_be_bytes()[17], 0xfe);
|
||||
@@ -52,10 +50,8 @@ fn delegatecall_static() {
|
||||
.status(StatusCode::Success)
|
||||
.gas_used(719)
|
||||
.inspect_host(|host, _| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
let call_msg = r.calls.last().unwrap();
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = host.recorded.calls.last().unwrap();
|
||||
assert_eq!(call_msg.gas, 1);
|
||||
assert!(call_msg.is_static);
|
||||
})
|
||||
@@ -108,9 +104,8 @@ fn create() {
|
||||
0xcc
|
||||
);
|
||||
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
assert_eq!(r.calls.last().unwrap().input_data.len(), 0x20);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls.last().unwrap().input_data.len(), 0x20);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -129,10 +124,9 @@ fn create_gas() {
|
||||
49719
|
||||
})
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(
|
||||
r.calls.last().unwrap().gas,
|
||||
host.recorded.calls.last().unwrap().gas,
|
||||
if rev == Revision::Homestead {
|
||||
17991
|
||||
} else {
|
||||
@@ -164,11 +158,9 @@ fn create2() {
|
||||
.gas_used(115817)
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
|
||||
let call_msg = r.calls.last().unwrap();
|
||||
let call_msg = host.recorded.calls.last().unwrap();
|
||||
assert_eq!(
|
||||
call_msg.kind,
|
||||
CallKind::Create2 {
|
||||
@@ -203,14 +195,12 @@ fn create2_salt_cost() {
|
||||
.status(StatusCode::Success)
|
||||
.gas_left(0)
|
||||
.inspect_host(|host, _| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(
|
||||
r.calls.last().unwrap().kind,
|
||||
host.recorded.calls.last().unwrap().kind,
|
||||
CallKind::Create2 { salt: U256::ZERO }
|
||||
);
|
||||
assert_eq!(r.calls.last().unwrap().depth, 1);
|
||||
assert_eq!(host.recorded.calls.last().unwrap().depth, 1);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -219,7 +209,7 @@ fn create2_salt_cost() {
|
||||
.gas_left(0)
|
||||
.inspect_host(|host, _| {
|
||||
// No another CREATE2.
|
||||
assert_eq!(host.recorded.lock().calls.len(), 0)
|
||||
assert_eq!(host.recorded.calls.len(), 0)
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -244,7 +234,7 @@ fn create_balance_too_low() {
|
||||
.status(StatusCode::Success)
|
||||
.output_value(0_u128)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().calls, []);
|
||||
assert_eq!(host.recorded.calls, []);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -277,11 +267,9 @@ fn create_failure() {
|
||||
.status(StatusCode::Success)
|
||||
.output_data(H256::from(create_address).to_fixed_bytes())
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(
|
||||
r.calls.last().unwrap().kind,
|
||||
host.recorded.calls.last().unwrap().kind,
|
||||
if op == OpCode::CREATE {
|
||||
CallKind::Create
|
||||
} else {
|
||||
@@ -298,11 +286,9 @@ fn create_failure() {
|
||||
.status(StatusCode::Success)
|
||||
.output_value(0_u128)
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(
|
||||
r.calls.last().unwrap().kind,
|
||||
host.recorded.calls.last().unwrap().kind,
|
||||
if op == OpCode::CREATE {
|
||||
CallKind::Create
|
||||
} else {
|
||||
@@ -319,11 +305,9 @@ fn create_failure() {
|
||||
.status(StatusCode::Success)
|
||||
.output_value(0_u128)
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(
|
||||
r.calls.last().unwrap().kind,
|
||||
host.recorded.calls.last().unwrap().kind,
|
||||
if op == OpCode::CREATE {
|
||||
CallKind::Create
|
||||
} else {
|
||||
@@ -364,7 +348,7 @@ fn call_failing_with_value() {
|
||||
.gas_used(7447)
|
||||
.inspect_host(|host, _| {
|
||||
// There was no call().
|
||||
assert_eq!(host.recorded.lock().calls, []);
|
||||
assert_eq!(host.recorded.calls, []);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -374,7 +358,7 @@ fn call_failing_with_value() {
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
// There was no call().
|
||||
assert_eq!(host.recorded.lock().calls, []);
|
||||
assert_eq!(host.recorded.calls, []);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -384,7 +368,7 @@ fn call_failing_with_value() {
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
// There was no call().
|
||||
assert_eq!(host.recorded.lock().calls, []);
|
||||
assert_eq!(host.recorded.calls, []);
|
||||
})
|
||||
.check();
|
||||
}
|
||||
@@ -407,9 +391,8 @@ fn call_with_value() {
|
||||
.gas_used(7447 + 32082)
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
let call_msg = &r.calls[0];
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = &host.recorded.calls[0];
|
||||
assert_eq!(call_msg.kind, CallKind::Call);
|
||||
assert_eq!(call_msg.depth, 1);
|
||||
assert_eq!(call_msg.gas, 32083);
|
||||
@@ -433,7 +416,7 @@ fn call_with_value_depth_limit() {
|
||||
.gas_used(7447)
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().calls, []);
|
||||
assert_eq!(host.recorded.calls, []);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -466,7 +449,7 @@ fn call_depth_limit() {
|
||||
)
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().calls, []);
|
||||
assert_eq!(host.recorded.calls, []);
|
||||
})
|
||||
.output_value(U256::ZERO)
|
||||
.check()
|
||||
@@ -597,9 +580,8 @@ fn call_value_zero_to_nonexistent_account() {
|
||||
.gas_used(729 + (call_gas - gas_left))
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(|host, _| {
|
||||
let recorded = host.recorded.lock();
|
||||
assert_eq!(recorded.calls.len(), 1);
|
||||
let call_msg = &recorded.calls[0];
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = &host.recorded.calls[0];
|
||||
assert_eq!(call_msg.kind, CallKind::Call);
|
||||
assert_eq!(call_msg.depth, 1);
|
||||
assert_eq!(call_msg.gas, 6000);
|
||||
@@ -645,7 +627,7 @@ fn call_new_account_creation_cost() {
|
||||
.output_value(U256::ONE)
|
||||
.inspect_host(move |host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
call_dst, // Account exist?
|
||||
call_dst, // Call.
|
||||
@@ -664,16 +646,15 @@ fn call_new_account_creation_cost() {
|
||||
.gas_used(25000 + 9000 + 739)
|
||||
.output_value(U256::ONE)
|
||||
.inspect_host(move |host, msg| {
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
let call_msg = &r.calls[0];
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = &host.recorded.calls[0];
|
||||
assert_eq!(call_msg.recipient, call_dst);
|
||||
assert_eq!(call_msg.gas, 2300);
|
||||
assert_eq!(call_msg.sender, destination);
|
||||
assert_eq!(call_msg.value, 1);
|
||||
assert_eq!(call_msg.input_data, Bytes::new());
|
||||
assert_eq!(
|
||||
r.account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
call_dst, // Account exist?
|
||||
msg.recipient, // Balance.
|
||||
@@ -693,16 +674,15 @@ fn call_new_account_creation_cost() {
|
||||
.gas_used(739)
|
||||
.output_value(U256::ONE)
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
let call_msg = &r.calls[0];
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = &host.recorded.calls[0];
|
||||
assert_eq!(call_msg.recipient, call_dst);
|
||||
assert_eq!(call_msg.gas, 0);
|
||||
assert_eq!(call_msg.sender, destination);
|
||||
assert_eq!(call_msg.value, 0);
|
||||
assert_eq!(call_msg.input_data, Bytes::new());
|
||||
assert_eq!(
|
||||
r.account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
call_dst // Call.
|
||||
]
|
||||
@@ -719,16 +699,15 @@ fn call_new_account_creation_cost() {
|
||||
.gas_used(25000 + 9000 + 739)
|
||||
.output_value(U256::ONE)
|
||||
.inspect_host(move |host, msg| {
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
let call_msg = &r.calls[0];
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = &host.recorded.calls[0];
|
||||
assert_eq!(call_msg.recipient, call_dst);
|
||||
assert_eq!(call_msg.gas, 2300);
|
||||
assert_eq!(call_msg.sender, destination);
|
||||
assert_eq!(call_msg.value, 1);
|
||||
assert_eq!(call_msg.input_data, Bytes::new());
|
||||
assert_eq!(
|
||||
r.account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
call_dst, // Account exist?
|
||||
msg.recipient, // Balance.
|
||||
@@ -755,9 +734,8 @@ fn callcode_new_account_create() {
|
||||
.gas_used(59722)
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, _| {
|
||||
let recorded = host.recorded.lock();
|
||||
assert_eq!(recorded.calls.len(), 1);
|
||||
let call_msg = &recorded.calls[0];
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
let call_msg = &host.recorded.calls[0];
|
||||
assert_eq!(call_msg.kind, CallKind::CallCode);
|
||||
assert_eq!(call_msg.depth, 1);
|
||||
assert_eq!(call_msg.gas, 52_300);
|
||||
@@ -801,8 +779,8 @@ fn call_then_oog() {
|
||||
.gas_left(0)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().calls.len(), 1);
|
||||
assert_eq!(host.recorded.lock().calls[0].gas, 254);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls[0].gas, 254);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -839,8 +817,8 @@ fn callcode_then_oog() {
|
||||
.gas(825)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().calls.len(), 1);
|
||||
assert_eq!(host.recorded.lock().calls[0].gas, 100);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls[0].gas, 100);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -878,8 +856,8 @@ fn delegatecall_then_oog() {
|
||||
.gas_left(0)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().calls.len(), 1);
|
||||
assert_eq!(host.recorded.lock().calls[0].gas, 254);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls[0].gas, 254);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -917,8 +895,8 @@ fn staticcall_then_oog() {
|
||||
.gas_used(1000)
|
||||
.gas_left(0)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().calls.len(), 1);
|
||||
assert_eq!(host.recorded.lock().calls[0].gas, 254);
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls[0].gas, 254);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -932,11 +910,9 @@ fn staticcall_input() {
|
||||
.append_bc(CallInstruction::staticcall(0_u128).gas(0xee).input(32, 3)),
|
||||
)
|
||||
.inspect_host(|host, _| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.calls.len(), 1);
|
||||
assert_eq!(r.calls[0].gas, 0xee);
|
||||
assert_eq!(r.calls[0].input_data[..], hex!("010203"));
|
||||
assert_eq!(host.recorded.calls.len(), 1);
|
||||
assert_eq!(host.recorded.calls[0].gas, 0xee);
|
||||
assert_eq!(host.recorded.calls[0].input_data[..], hex!("010203"));
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -1113,7 +1089,7 @@ fn returndatasize() {
|
||||
|
||||
t.apply_host_fn(|host, _| {
|
||||
host.call_result.output_data = Bytes::new();
|
||||
host.call_result.status_code = StatusCode::InternalError(String::new());
|
||||
host.call_result.status_code = StatusCode::InternalError("");
|
||||
})
|
||||
.gas_used(735)
|
||||
.output_data([0])
|
||||
|
||||
@@ -18,7 +18,7 @@ fn eip2929_case1() {
|
||||
.output_data([])
|
||||
.inspect_host(|host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
msg.sender,
|
||||
msg.recipient,
|
||||
@@ -65,7 +65,7 @@ fn eip2929_case2() {
|
||||
.output_data([])
|
||||
.inspect_host(|host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
msg.sender,
|
||||
msg.recipient,
|
||||
@@ -355,7 +355,7 @@ fn eip2929_delegatecall_cold() {
|
||||
.gas_used(2618)
|
||||
.inspect_host(|host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
msg.sender,
|
||||
msg.recipient,
|
||||
@@ -371,7 +371,7 @@ fn eip2929_delegatecall_cold() {
|
||||
.gas_used(2617)
|
||||
.inspect_host(|host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
msg.sender,
|
||||
msg.recipient,
|
||||
|
||||
@@ -1374,7 +1374,7 @@ fn memory_access() {
|
||||
.map(MemoryAccessParams::from)
|
||||
.collect();
|
||||
|
||||
let metrics = &*crate::execution::evm::instructions::PROPERTIES;
|
||||
let metrics = &crate::execution::evm::instructions::PROPERTIES;
|
||||
|
||||
for p in memory_access_test_cases {
|
||||
let push_size = format!("64{:0>10x}", p.size);
|
||||
|
||||
@@ -461,10 +461,8 @@ fn log() {
|
||||
.status(StatusCode::Success)
|
||||
.gas_used((421 + n * 375) as i64)
|
||||
.inspect_host(move |host, _| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.logs.len(), 1);
|
||||
let last_log = r.logs.last().unwrap();
|
||||
assert_eq!(host.recorded.logs.len(), 1);
|
||||
let last_log = host.recorded.logs.last().unwrap();
|
||||
assert_eq!(&*last_log.data, &hex!("7700") as &[u8]);
|
||||
assert_eq!(last_log.topics.len(), n);
|
||||
for i in 0..n {
|
||||
@@ -485,9 +483,8 @@ fn log0_empty() {
|
||||
.opcode(OpCode::LOG0),
|
||||
)
|
||||
.inspect_host(|host, _| {
|
||||
let r = host.recorded.lock();
|
||||
assert_eq!(r.logs.len(), 1);
|
||||
let last_log = r.logs.last().unwrap();
|
||||
assert_eq!(host.recorded.logs.len(), 1);
|
||||
let last_log = host.recorded.logs.last().unwrap();
|
||||
assert_eq!(last_log.topics.len(), 0);
|
||||
assert_eq!(last_log.data.len(), 0);
|
||||
})
|
||||
@@ -517,7 +514,7 @@ fn log_data_cost() {
|
||||
.gas_used(cost as i64)
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().logs.len(), 1);
|
||||
assert_eq!(host.recorded.logs.len(), 1);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -532,7 +529,7 @@ fn selfdestruct() {
|
||||
.gas_used(5003)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().selfdestructs,
|
||||
host.recorded.selfdestructs,
|
||||
[SelfdestructRecord {
|
||||
selfdestructed: Address::zero(),
|
||||
beneficiary: Address::from(hex!("0000000000000000000000000000000000000009"))
|
||||
@@ -548,7 +545,7 @@ fn selfdestruct() {
|
||||
.gas_used(3)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().selfdestructs,
|
||||
host.recorded.selfdestructs,
|
||||
[SelfdestructRecord {
|
||||
selfdestructed: Address::zero(),
|
||||
beneficiary: Address::from(hex!("0000000000000000000000000000000000000007"))
|
||||
@@ -564,7 +561,7 @@ fn selfdestruct() {
|
||||
.gas_used(30003)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().selfdestructs,
|
||||
host.recorded.selfdestructs,
|
||||
[SelfdestructRecord {
|
||||
selfdestructed: Address::zero(),
|
||||
beneficiary: Address::from(hex!("0000000000000000000000000000000000000008"))
|
||||
@@ -584,7 +581,6 @@ fn selfdestruct_with_balance() {
|
||||
let mut t = EvmTester::new()
|
||||
.code(code)
|
||||
.destination(hex!("000000000000000000000000000000000000005e"))
|
||||
.collect_traces(true)
|
||||
.apply_host_fn(|host, msg| {
|
||||
host.accounts.entry(msg.recipient).or_default().balance = U256::ZERO;
|
||||
});
|
||||
@@ -594,9 +590,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.gas_used(3)
|
||||
.inspect_host(|host, msg| {
|
||||
let r = host.recorded.lock();
|
||||
|
||||
assert_eq!(r.account_accesses, [msg.recipient]); // Selfdestruct.
|
||||
assert_eq!(host.recorded.account_accesses, [msg.recipient]); // Selfdestruct.
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -606,7 +600,7 @@ fn selfdestruct_with_balance() {
|
||||
.gas_used(30003)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Exists?
|
||||
beneficiary,
|
||||
@@ -623,7 +617,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(move |host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Exists?
|
||||
beneficiary
|
||||
@@ -638,7 +632,7 @@ fn selfdestruct_with_balance() {
|
||||
.gas_used(5003)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Balance.
|
||||
msg.recipient,
|
||||
@@ -654,7 +648,7 @@ fn selfdestruct_with_balance() {
|
||||
.gas(5002)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().account_accesses, []);
|
||||
assert_eq!(host.recorded.account_accesses, []);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -668,7 +662,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(|host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Selfdestruct.
|
||||
msg.recipient
|
||||
@@ -683,7 +677,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Exists?
|
||||
beneficiary,
|
||||
@@ -700,7 +694,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(move |host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Exists?
|
||||
beneficiary,
|
||||
@@ -715,7 +709,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Balance
|
||||
msg.recipient,
|
||||
@@ -734,7 +728,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Balance
|
||||
msg.recipient,
|
||||
@@ -756,7 +750,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(|host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Selfdestruct.
|
||||
msg.recipient,
|
||||
@@ -771,7 +765,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Exists?
|
||||
beneficiary,
|
||||
@@ -787,7 +781,7 @@ fn selfdestruct_with_balance() {
|
||||
.gas(5002)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(move |host, _| {
|
||||
assert_eq!(host.recorded.lock().account_accesses, []);
|
||||
assert_eq!(host.recorded.account_accesses, []);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -797,7 +791,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Balance.
|
||||
msg.recipient,
|
||||
@@ -813,7 +807,7 @@ fn selfdestruct_with_balance() {
|
||||
.gas(5002)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().account_accesses, []);
|
||||
assert_eq!(host.recorded.account_accesses, []);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -827,7 +821,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(|host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Selfdestruct
|
||||
msg.recipient
|
||||
@@ -842,7 +836,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Exists?
|
||||
beneficiary,
|
||||
@@ -858,7 +852,7 @@ fn selfdestruct_with_balance() {
|
||||
.gas(5002)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().account_accesses, []);
|
||||
assert_eq!(host.recorded.account_accesses, []);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -868,7 +862,7 @@ fn selfdestruct_with_balance() {
|
||||
.status(StatusCode::Success)
|
||||
.inspect_host(move |host, msg| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[
|
||||
// Balance
|
||||
msg.recipient,
|
||||
@@ -885,7 +879,7 @@ fn selfdestruct_with_balance() {
|
||||
.gas(5002)
|
||||
.status(StatusCode::OutOfGas)
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().account_accesses, []);
|
||||
assert_eq!(host.recorded.account_accesses, []);
|
||||
})
|
||||
.check();
|
||||
}
|
||||
@@ -911,7 +905,7 @@ fn blockhash() {
|
||||
assert_eq!(output[13], 0);
|
||||
})
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().blockhashes, [] as [u64; 0]);
|
||||
assert_eq!(host.recorded.blockhashes, [] as [u64; 0]);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -924,7 +918,7 @@ fn blockhash() {
|
||||
assert_eq!(output[13], 0);
|
||||
})
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().blockhashes, [] as [u64; 0]);
|
||||
assert_eq!(host.recorded.blockhashes, [] as [u64; 0]);
|
||||
})
|
||||
.check();
|
||||
|
||||
@@ -936,7 +930,7 @@ fn blockhash() {
|
||||
assert_eq!(output[13], 0x13);
|
||||
})
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(host.recorded.lock().blockhashes, [0]);
|
||||
assert_eq!(host.recorded.blockhashes, [0]);
|
||||
})
|
||||
.check();
|
||||
}
|
||||
@@ -961,9 +955,9 @@ fn extcode() {
|
||||
assert_eq!(output.len(), 4);
|
||||
assert_eq!(output[..3], host.accounts[&addr].code[..3]);
|
||||
assert_eq!(output[3], 0);
|
||||
assert_eq!(host.recorded.lock().account_accesses.len(), 2);
|
||||
assert_eq!(host.recorded.lock().account_accesses[0].0[19], 0xfe);
|
||||
assert_eq!(host.recorded.lock().account_accesses[1].0[19], 0xfe);
|
||||
assert_eq!(host.recorded.account_accesses.len(), 2);
|
||||
assert_eq!(host.recorded.account_accesses[0].0[19], 0xfe);
|
||||
assert_eq!(host.recorded.account_accesses[1].0[19], 0xfe);
|
||||
})
|
||||
.check()
|
||||
}
|
||||
@@ -1124,7 +1118,7 @@ fn extcodecopy_nonzero_index() {
|
||||
.output_data(hex!("c000"))
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[hex!("000000000000000000000000000000000000000a").into()]
|
||||
);
|
||||
})
|
||||
@@ -1152,7 +1146,7 @@ fn extcodecopy_fill_tail() {
|
||||
.output_data(hex!("ff00"))
|
||||
.inspect_host(|host, _| {
|
||||
assert_eq!(
|
||||
host.recorded.lock().account_accesses,
|
||||
host.recorded.account_accesses,
|
||||
[hex!("000000000000000000000000000000000000000a").into()]
|
||||
);
|
||||
})
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
use super::*;
|
||||
use crate::models::*;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Passed into execution context to collect metrics.
|
||||
pub trait Tracer {
|
||||
#[doc(hidden)]
|
||||
const DUMMY: bool = false;
|
||||
|
||||
/// Called when execution starts.
|
||||
fn notify_execution_start(
|
||||
&mut self,
|
||||
revision: Revision,
|
||||
message: InterpreterMessage,
|
||||
code: Bytes,
|
||||
);
|
||||
/// Called on each instruction.
|
||||
fn notify_instruction_start(&mut self, pc: usize, opcode: OpCode, state: &ExecutionState);
|
||||
/// Called when execution ends.
|
||||
fn notify_execution_end(&mut self, output: &Output);
|
||||
}
|
||||
|
||||
/// Tracer which does nothing.
|
||||
pub struct NoopTracer;
|
||||
|
||||
impl Tracer for NoopTracer {
|
||||
const DUMMY: bool = true;
|
||||
|
||||
fn notify_execution_start(&mut self, _: Revision, _: InterpreterMessage, _: Bytes) {}
|
||||
|
||||
fn notify_instruction_start(&mut self, _: usize, _: OpCode, _: &ExecutionState) {}
|
||||
|
||||
fn notify_execution_end(&mut self, _: &Output) {}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExecutionStart {
|
||||
pub depth: i32,
|
||||
pub rev: Revision,
|
||||
#[serde(rename = "static")]
|
||||
pub is_static: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct InstructionStart {
|
||||
pub pc: usize,
|
||||
pub op: u8,
|
||||
pub op_name: &'static str,
|
||||
pub gas: i64,
|
||||
pub stack: Stack,
|
||||
pub memory_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct ExecutionEnd {
|
||||
pub error: Option<String>,
|
||||
pub gas: i64,
|
||||
pub gas_used: i64,
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
struct TracerContext {
|
||||
message: InterpreterMessage,
|
||||
code: Bytes,
|
||||
}
|
||||
|
||||
/// Tracer which prints to stdout.
|
||||
#[derive(Default)]
|
||||
pub struct StdoutTracer {
|
||||
execution_stack: Vec<TracerContext>,
|
||||
}
|
||||
|
||||
impl Tracer for StdoutTracer {
|
||||
fn notify_execution_start(
|
||||
&mut self,
|
||||
revision: Revision,
|
||||
message: InterpreterMessage,
|
||||
code: Bytes,
|
||||
) {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(&ExecutionStart {
|
||||
depth: message.depth,
|
||||
rev: revision,
|
||||
is_static: message.is_static,
|
||||
})
|
||||
.unwrap()
|
||||
);
|
||||
self.execution_stack.push(TracerContext { message, code });
|
||||
}
|
||||
|
||||
fn notify_instruction_start(&mut self, pc: usize, _: OpCode, state: &ExecutionState) {
|
||||
let context = self.execution_stack.last().unwrap();
|
||||
let opcode = OpCode(context.code[pc]);
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(&InstructionStart {
|
||||
pc,
|
||||
op: opcode.0,
|
||||
op_name: opcode.name(),
|
||||
gas: state.gas_left,
|
||||
stack: state.stack.clone(),
|
||||
memory_size: state.memory.len()
|
||||
})
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
fn notify_execution_end(&mut self, output: &Output) {
|
||||
let context = self.execution_stack.pop().unwrap();
|
||||
let error = match &output.status_code {
|
||||
StatusCode::Success => None,
|
||||
other => Some(other.to_string()),
|
||||
};
|
||||
let (gas_left, gas_used) = if error.is_none() {
|
||||
(output.gas_left, context.message.gas - output.gas_left)
|
||||
} else {
|
||||
(0, context.message.gas)
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(&ExecutionEnd {
|
||||
error,
|
||||
gas: gas_left,
|
||||
gas_used,
|
||||
output: hex::encode(&output.output_data),
|
||||
})
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ use bytes::Bytes;
|
||||
use ethereum_types::*;
|
||||
use ethnum::U256;
|
||||
use hex_literal::hex;
|
||||
use parking_lot::Mutex;
|
||||
use std::{cmp::min, collections::HashMap};
|
||||
|
||||
/// LOG record.
|
||||
@@ -64,25 +63,13 @@ pub struct Records {
|
||||
pub selfdestructs: Vec<SelfdestructRecord>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MockedHost {
|
||||
pub accounts: HashMap<Address, Account>,
|
||||
pub tx_context: TxContext,
|
||||
pub block_hash: U256,
|
||||
pub call_result: Output,
|
||||
pub recorded: Mutex<Records>,
|
||||
}
|
||||
|
||||
impl Clone for MockedHost {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
accounts: self.accounts.clone(),
|
||||
tx_context: self.tx_context.clone(),
|
||||
block_hash: self.block_hash,
|
||||
call_result: self.call_result.clone(),
|
||||
recorded: Mutex::new(self.recorded.lock().clone()),
|
||||
}
|
||||
}
|
||||
pub recorded: Records,
|
||||
}
|
||||
|
||||
impl Default for MockedHost {
|
||||
@@ -121,13 +108,13 @@ impl Records {
|
||||
}
|
||||
|
||||
impl Host for MockedHost {
|
||||
fn account_exists(&self, address: ethereum_types::Address) -> bool {
|
||||
self.recorded.lock().record_account_access(address);
|
||||
fn account_exists(&mut self, address: ethereum_types::Address) -> bool {
|
||||
self.recorded.record_account_access(address);
|
||||
self.accounts.contains_key(&address)
|
||||
}
|
||||
|
||||
fn get_storage(&self, address: ethereum_types::Address, key: U256) -> U256 {
|
||||
self.recorded.lock().record_account_access(address);
|
||||
fn get_storage(&mut self, address: ethereum_types::Address, key: U256) -> U256 {
|
||||
self.recorded.record_account_access(address);
|
||||
|
||||
self.accounts
|
||||
.get(&address)
|
||||
@@ -141,7 +128,7 @@ impl Host for MockedHost {
|
||||
key: U256,
|
||||
value: U256,
|
||||
) -> StorageStatus {
|
||||
self.recorded.lock().record_account_access(address);
|
||||
self.recorded.record_account_access(address);
|
||||
|
||||
// Get the reference to the old value.
|
||||
// This will create the account in case it was not present.
|
||||
@@ -180,8 +167,8 @@ impl Host for MockedHost {
|
||||
status
|
||||
}
|
||||
|
||||
fn get_balance(&self, address: ethereum_types::Address) -> ethnum::U256 {
|
||||
self.recorded.lock().record_account_access(address);
|
||||
fn get_balance(&mut self, address: ethereum_types::Address) -> ethnum::U256 {
|
||||
self.recorded.record_account_access(address);
|
||||
|
||||
self.accounts
|
||||
.get(&address)
|
||||
@@ -189,8 +176,8 @@ impl Host for MockedHost {
|
||||
.unwrap_or(U256::ZERO)
|
||||
}
|
||||
|
||||
fn get_code_size(&self, address: ethereum_types::Address) -> ethnum::U256 {
|
||||
self.recorded.lock().record_account_access(address);
|
||||
fn get_code_size(&mut self, address: ethereum_types::Address) -> ethnum::U256 {
|
||||
self.recorded.record_account_access(address);
|
||||
|
||||
self.accounts
|
||||
.get(&address)
|
||||
@@ -198,8 +185,8 @@ impl Host for MockedHost {
|
||||
.unwrap_or(U256::ZERO)
|
||||
}
|
||||
|
||||
fn get_code_hash(&self, address: ethereum_types::Address) -> U256 {
|
||||
self.recorded.lock().record_account_access(address);
|
||||
fn get_code_hash(&mut self, address: ethereum_types::Address) -> U256 {
|
||||
self.recorded.record_account_access(address);
|
||||
|
||||
self.accounts
|
||||
.get(&address)
|
||||
@@ -207,8 +194,8 @@ impl Host for MockedHost {
|
||||
.unwrap_or(U256::ZERO)
|
||||
}
|
||||
|
||||
fn copy_code(&self, address: Address, code_offset: usize, buffer: &mut [u8]) -> usize {
|
||||
self.recorded.lock().record_account_access(address);
|
||||
fn copy_code(&mut self, address: Address, code_offset: usize, buffer: &mut [u8]) -> usize {
|
||||
self.recorded.record_account_access(address);
|
||||
|
||||
self.accounts
|
||||
.get(&address)
|
||||
@@ -233,54 +220,52 @@ impl Host for MockedHost {
|
||||
address: ethereum_types::Address,
|
||||
beneficiary: ethereum_types::Address,
|
||||
) {
|
||||
let mut r = self.recorded.lock();
|
||||
|
||||
r.record_account_access(address);
|
||||
r.selfdestructs.push(SelfdestructRecord {
|
||||
self.recorded.record_account_access(address);
|
||||
self.recorded.selfdestructs.push(SelfdestructRecord {
|
||||
selfdestructed: address,
|
||||
beneficiary,
|
||||
});
|
||||
}
|
||||
|
||||
fn call(&mut self, msg: &InterpreterMessage) -> Output {
|
||||
let mut r = self.recorded.lock();
|
||||
fn call(&mut self, msg: Call) -> Output {
|
||||
let msg = match msg {
|
||||
Call::Call(message) => message.clone(),
|
||||
Call::Create(message) => message.clone().into(),
|
||||
};
|
||||
self.recorded.record_account_access(msg.recipient);
|
||||
|
||||
r.record_account_access(msg.recipient);
|
||||
|
||||
if r.calls.len() < MAX_RECORDED_CALLS {
|
||||
r.calls.push(msg.clone());
|
||||
if self.recorded.calls.len() < MAX_RECORDED_CALLS {
|
||||
self.recorded.calls.push(msg.clone());
|
||||
let call_msg = msg;
|
||||
if !call_msg.input_data.is_empty() {
|
||||
r.call_inputs.push(call_msg.input_data.clone());
|
||||
self.recorded.call_inputs.push(call_msg.input_data);
|
||||
}
|
||||
}
|
||||
self.call_result.clone()
|
||||
}
|
||||
|
||||
fn get_tx_context(&self) -> TxContext {
|
||||
fn get_tx_context(&mut self) -> TxContext {
|
||||
self.tx_context.clone()
|
||||
}
|
||||
|
||||
fn get_block_hash(&self, block_number: u64) -> U256 {
|
||||
self.recorded.lock().blockhashes.push(block_number);
|
||||
fn get_block_hash(&mut self, block_number: u64) -> U256 {
|
||||
self.recorded.blockhashes.push(block_number);
|
||||
self.block_hash
|
||||
}
|
||||
|
||||
fn emit_log(&mut self, address: ethereum_types::Address, data: &[u8], topics: &[U256]) {
|
||||
self.recorded.lock().logs.push(LogRecord {
|
||||
fn emit_log(&mut self, address: ethereum_types::Address, data: Bytes, topics: &[U256]) {
|
||||
self.recorded.logs.push(LogRecord {
|
||||
creator: address,
|
||||
data: data.to_vec().into(),
|
||||
data,
|
||||
topics: topics.to_vec(),
|
||||
});
|
||||
}
|
||||
|
||||
fn access_account(&mut self, address: ethereum_types::Address) -> AccessStatus {
|
||||
let mut r = self.recorded.lock();
|
||||
|
||||
// Check if the address have been already accessed.
|
||||
let already_accessed = r.account_accesses.iter().any(|&a| a == address);
|
||||
let already_accessed = self.recorded.account_accesses.iter().any(|&a| a == address);
|
||||
|
||||
r.record_account_access(address);
|
||||
self.recorded.record_account_access(address);
|
||||
|
||||
if address.0 >= hex!("0000000000000000000000000000000000000001")
|
||||
&& address.0 <= hex!("0000000000000000000000000000000000000009")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
execution::evm::{
|
||||
tracing::*,
|
||||
util::{mocked_host::*, *},
|
||||
*,
|
||||
},
|
||||
@@ -17,7 +16,6 @@ fn exec(
|
||||
revision: Revision,
|
||||
message: InterpreterMessage,
|
||||
code: Vec<u8>,
|
||||
collect_traces: bool,
|
||||
) -> Output {
|
||||
// Add EIP-2929 tweak.
|
||||
if revision >= Revision::Berlin {
|
||||
@@ -26,11 +24,7 @@ fn exec(
|
||||
}
|
||||
let code = AnalyzedCode::analyze(&code);
|
||||
|
||||
if collect_traces {
|
||||
code.execute(host, &mut StdoutTracer::default(), None, message, revision)
|
||||
} else {
|
||||
code.execute(host, &mut NoopTracer, None, message, revision)
|
||||
}
|
||||
code.execute(host, &message, revision)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -59,7 +53,6 @@ pub struct EvmTester {
|
||||
gas_check: Option<GasCheck>,
|
||||
expected_status_codes: Option<Vec<StatusCode>>,
|
||||
expected_output_data: Option<Vec<u8>>,
|
||||
collect_traces: bool,
|
||||
}
|
||||
|
||||
impl Default for EvmTester {
|
||||
@@ -93,7 +86,6 @@ impl EvmTester {
|
||||
gas_check: None,
|
||||
expected_status_codes: None,
|
||||
expected_output_data: None,
|
||||
collect_traces: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,27 +209,13 @@ impl EvmTester {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn collect_traces(mut self, doit: bool) -> Self {
|
||||
self.collect_traces = doit;
|
||||
self
|
||||
}
|
||||
|
||||
/// Execute provided code, run checks and return bytecode returned by EVM.
|
||||
pub fn check_and_get_result(self) -> Output {
|
||||
if self.collect_traces {
|
||||
println!("Executing code: {}", hex::encode(&self.code));
|
||||
}
|
||||
let mut host = self.host;
|
||||
for f in self.apply_host_fns {
|
||||
(f)(&mut host, &self.message);
|
||||
}
|
||||
let output = exec(
|
||||
&mut host,
|
||||
self.revision,
|
||||
self.message.clone(),
|
||||
self.code,
|
||||
self.collect_traces,
|
||||
);
|
||||
let output = exec(&mut host, self.revision, self.message.clone(), self.code);
|
||||
|
||||
if let Some(status_codes) = self.expected_status_codes {
|
||||
assert!(
|
||||
|
||||
@@ -6,10 +6,9 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
chain::protocol_param::{fee, param},
|
||||
crypto::keccak256,
|
||||
execution::evm::{
|
||||
continuation::{interrupt::*, interrupt_data::*, resume_data::*},
|
||||
host::*,
|
||||
AnalyzedCode, CallKind, CreateMessage, InterpreterMessage, Output, StatusCode,
|
||||
host::*, AnalyzedCode, CallKind, CreateMessage, InterpreterMessage, Output, StatusCode,
|
||||
},
|
||||
h256_to_u256,
|
||||
models::*,
|
||||
@@ -17,7 +16,6 @@ use crate::{
|
||||
};
|
||||
use anyhow::Context;
|
||||
use bytes::Bytes;
|
||||
use sha3::{Digest, Keccak256};
|
||||
use std::{cmp::min, convert::TryFrom};
|
||||
|
||||
pub struct CallResult {
|
||||
@@ -34,7 +32,7 @@ where
|
||||
B: State,
|
||||
{
|
||||
state: &'state mut IntraBlockState<'r, B>,
|
||||
tracer: Option<&'tracer mut dyn Tracer>,
|
||||
tracer: &'tracer mut dyn Tracer,
|
||||
analysis_cache: &'analysis mut AnalysisCache,
|
||||
header: &'h PartialHeader,
|
||||
block_spec: &'c BlockExecutionSpec,
|
||||
@@ -42,10 +40,10 @@ where
|
||||
beneficiary: Address,
|
||||
}
|
||||
|
||||
pub fn execute<B: State>(
|
||||
state: &mut IntraBlockState<'_, B>,
|
||||
tracer: Option<&mut dyn Tracer>,
|
||||
analysis_cache: &mut AnalysisCache,
|
||||
pub fn execute<'db, 'tracer, 'analysis, B: State>(
|
||||
state: &mut IntraBlockState<'db, B>,
|
||||
tracer: &'tracer mut dyn Tracer,
|
||||
analysis_cache: &'analysis mut AnalysisCache,
|
||||
header: &PartialHeader,
|
||||
block_spec: &BlockExecutionSpec,
|
||||
txn: &MessageWithSender,
|
||||
@@ -62,7 +60,7 @@ pub fn execute<B: State>(
|
||||
};
|
||||
|
||||
let res = if let TransactionAction::Call(to) = txn.action() {
|
||||
evm.call(InterpreterMessage {
|
||||
evm.call(&InterpreterMessage {
|
||||
kind: CallKind::Call,
|
||||
is_static: false,
|
||||
depth: 0,
|
||||
@@ -74,7 +72,7 @@ pub fn execute<B: State>(
|
||||
code_address: to,
|
||||
})?
|
||||
} else {
|
||||
evm.create(CreateMessage {
|
||||
evm.create(&CreateMessage {
|
||||
depth: 0,
|
||||
gas: gas as i64,
|
||||
sender: txn.sender,
|
||||
@@ -96,7 +94,7 @@ impl<'r, 'state, 'tracer, 'analysis, 'h, 'c, 't, B>
|
||||
where
|
||||
B: State,
|
||||
{
|
||||
fn create(&mut self, message: CreateMessage) -> anyhow::Result<Output> {
|
||||
fn create(&mut self, message: &CreateMessage) -> anyhow::Result<Output> {
|
||||
let mut res = Output {
|
||||
status_code: StatusCode::Success,
|
||||
gas_left: message.gas,
|
||||
@@ -115,11 +113,7 @@ where
|
||||
|
||||
let contract_addr = {
|
||||
if let Some(salt) = message.salt {
|
||||
create2_address(
|
||||
message.sender,
|
||||
salt,
|
||||
H256::from_slice(&Keccak256::digest(&message.initcode[..])[..]),
|
||||
)
|
||||
create2_address(message.sender, salt, keccak256(&message.initcode[..]))
|
||||
} else {
|
||||
create_address(message.sender, nonce)
|
||||
}
|
||||
@@ -127,17 +121,15 @@ where
|
||||
|
||||
self.state.access_account(contract_addr);
|
||||
|
||||
if let Some(tracer) = self.tracer.as_mut() {
|
||||
tracer.capture_start(
|
||||
message.depth.try_into().unwrap(),
|
||||
message.sender,
|
||||
contract_addr,
|
||||
MessageKind::Create,
|
||||
message.initcode.clone(),
|
||||
message.gas.try_into().unwrap(),
|
||||
message.endowment,
|
||||
);
|
||||
};
|
||||
self.tracer.capture_start(
|
||||
message.depth.try_into().unwrap(),
|
||||
message.sender,
|
||||
contract_addr,
|
||||
MessageKind::Create,
|
||||
message.initcode.clone(),
|
||||
message.gas.try_into().unwrap(),
|
||||
message.endowment,
|
||||
);
|
||||
|
||||
if self.state.get_nonce(contract_addr)? != 0
|
||||
|| self.state.get_code_hash(contract_addr)? != EMPTY_HASH
|
||||
@@ -171,7 +163,7 @@ where
|
||||
value: message.endowment,
|
||||
};
|
||||
|
||||
res = self.execute(deploy_message, message.initcode, None)?;
|
||||
res = self.execute(&deploy_message, &message.initcode, None)?;
|
||||
|
||||
if res.status_code == StatusCode::Success {
|
||||
let code_len = res.output_data.len();
|
||||
@@ -209,64 +201,55 @@ where
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn call(&mut self, message: InterpreterMessage) -> anyhow::Result<Output> {
|
||||
let mut res = Output {
|
||||
status_code: StatusCode::Success,
|
||||
gas_left: message.gas,
|
||||
output_data: Bytes::new(),
|
||||
create_address: None,
|
||||
};
|
||||
|
||||
let value = message.value;
|
||||
if message.kind != CallKind::DelegateCall && self.state.get_balance(message.sender)? < value
|
||||
fn call(&mut self, message: &InterpreterMessage) -> anyhow::Result<Output> {
|
||||
if message.kind != CallKind::DelegateCall
|
||||
&& self.state.get_balance(message.sender)? < message.value
|
||||
{
|
||||
res.status_code = StatusCode::InsufficientBalance;
|
||||
return Ok(res);
|
||||
return Ok(Output {
|
||||
status_code: StatusCode::InsufficientBalance,
|
||||
gas_left: message.gas,
|
||||
output_data: Bytes::new(),
|
||||
create_address: None,
|
||||
});
|
||||
}
|
||||
|
||||
let precompiled = self.is_precompiled(message.code_address);
|
||||
|
||||
let code = if !precompiled {
|
||||
self.state.get_code(message.code_address)?
|
||||
let code_kind = if self.is_precompiled(message.code_address) {
|
||||
CodeKind::Precompile
|
||||
} else {
|
||||
None
|
||||
CodeKind::Bytecode(self.state.get_code(message.code_address)?)
|
||||
};
|
||||
|
||||
if let Some(tracer) = &mut self.tracer {
|
||||
let call_kind = {
|
||||
match (message.kind, message.is_static) {
|
||||
self.tracer.capture_start(
|
||||
message.depth as u16,
|
||||
message.sender,
|
||||
message.recipient,
|
||||
MessageKind::Call {
|
||||
call_kind: match (message.kind, message.is_static) {
|
||||
(CallKind::Call, true) => super::tracer::CallKind::StaticCall,
|
||||
(CallKind::Call, false) => super::tracer::CallKind::Call,
|
||||
(CallKind::CallCode, _) => super::tracer::CallKind::CallCode,
|
||||
(CallKind::DelegateCall, _) => super::tracer::CallKind::DelegateCall,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
tracer.capture_start(
|
||||
message.depth.try_into().unwrap(),
|
||||
message.sender,
|
||||
message.recipient,
|
||||
MessageKind::Call {
|
||||
call_kind,
|
||||
code_kind: if precompiled {
|
||||
CodeKind::Precompile
|
||||
} else {
|
||||
CodeKind::Bytecode(None)
|
||||
},
|
||||
},
|
||||
message.input_data.clone(),
|
||||
message.gas.try_into().unwrap(),
|
||||
value,
|
||||
)
|
||||
}
|
||||
code_kind: code_kind.clone(),
|
||||
},
|
||||
message.input_data.clone(),
|
||||
message.gas as u64,
|
||||
message.value,
|
||||
);
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-161
|
||||
if value == 0
|
||||
if message.value == 0
|
||||
&& self.block_spec.revision >= Revision::Spurious
|
||||
&& !precompiled
|
||||
&& matches!(code_kind, CodeKind::Bytecode(_))
|
||||
&& !self.state.exists(message.code_address)?
|
||||
{
|
||||
return Ok(res);
|
||||
return Ok(Output {
|
||||
status_code: StatusCode::Success,
|
||||
gas_left: message.gas,
|
||||
output_data: Bytes::new(),
|
||||
create_address: None,
|
||||
});
|
||||
}
|
||||
|
||||
let snapshot = self.state.take_snapshot();
|
||||
@@ -277,40 +260,57 @@ where
|
||||
// https://github.com/ethereum/go-ethereum/blob/v1.9.25/core/vm/evm.go#L391
|
||||
self.state.touch(message.recipient);
|
||||
} else {
|
||||
self.state.subtract_from_balance(message.sender, value)?;
|
||||
self.state.add_to_balance(message.recipient, value)?;
|
||||
self.state
|
||||
.subtract_from_balance(message.sender, message.value)?;
|
||||
self.state
|
||||
.add_to_balance(message.recipient, message.value)?;
|
||||
}
|
||||
}
|
||||
|
||||
if precompiled {
|
||||
let num = message.code_address.0[ADDRESS_LENGTH - 1] as usize;
|
||||
let contract = &precompiled::CONTRACTS[num - 1];
|
||||
let input = message.input_data;
|
||||
if let Some(gas) = (contract.gas)(input.clone(), self.block_spec.revision)
|
||||
.and_then(|g| i64::try_from(g).ok())
|
||||
{
|
||||
if gas > message.gas {
|
||||
res.status_code = StatusCode::OutOfGas;
|
||||
} else if let Some(output) = (contract.run)(input) {
|
||||
res.status_code = StatusCode::Success;
|
||||
res.gas_left = message.gas - gas;
|
||||
res.output_data = output;
|
||||
} else {
|
||||
res.status_code = StatusCode::PrecompileFailure;
|
||||
let mut res = match &code_kind {
|
||||
CodeKind::Bytecode(code) => {
|
||||
let code = code.as_ref().map(|v| v as &[u8]).unwrap_or(&[]);
|
||||
if code.is_empty() {
|
||||
return Ok(Output {
|
||||
status_code: StatusCode::Success,
|
||||
gas_left: message.gas,
|
||||
output_data: Bytes::new(),
|
||||
create_address: None,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
res.status_code = StatusCode::OutOfGas;
|
||||
}
|
||||
} else {
|
||||
let code = code.unwrap_or_default();
|
||||
if code.is_empty() {
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
let code_hash = self.state.get_code_hash(message.code_address)?;
|
||||
let code_hash = self.state.get_code_hash(message.code_address)?;
|
||||
|
||||
res = self.execute(message, code, Some(code_hash))?;
|
||||
}
|
||||
self.execute(message, code, Some(&code_hash))?
|
||||
}
|
||||
CodeKind::Precompile => {
|
||||
let num = message.code_address.0[ADDRESS_LENGTH - 1] as usize;
|
||||
let contract = &precompiled::CONTRACTS[num - 1];
|
||||
let input = message.input_data.clone();
|
||||
let mut res = Output {
|
||||
status_code: StatusCode::Success,
|
||||
gas_left: message.gas,
|
||||
output_data: Bytes::new(),
|
||||
create_address: None,
|
||||
};
|
||||
if let Some(gas) = (contract.gas)(input.clone(), self.block_spec.revision)
|
||||
.and_then(|g| i64::try_from(g).ok())
|
||||
{
|
||||
if gas > message.gas {
|
||||
res.status_code = StatusCode::OutOfGas;
|
||||
} else if let Some(output) = (contract.run)(input) {
|
||||
res.status_code = StatusCode::Success;
|
||||
res.gas_left = message.gas - gas;
|
||||
res.output_data = output;
|
||||
} else {
|
||||
res.status_code = StatusCode::PrecompileFailure;
|
||||
}
|
||||
} else {
|
||||
res.status_code = StatusCode::OutOfGas;
|
||||
}
|
||||
res
|
||||
}
|
||||
};
|
||||
|
||||
if res.status_code != StatusCode::Success {
|
||||
self.state.revert_to_snapshot(snapshot);
|
||||
@@ -324,307 +324,29 @@ where
|
||||
|
||||
fn execute(
|
||||
&mut self,
|
||||
msg: InterpreterMessage,
|
||||
code: Bytes,
|
||||
code_hash: Option<H256>,
|
||||
msg: &InterpreterMessage,
|
||||
code: &[u8],
|
||||
code_hash: Option<&H256>,
|
||||
) -> anyhow::Result<Output> {
|
||||
let a;
|
||||
let analysis = if let Some(code_hash) = code_hash {
|
||||
if let Some(cache) = self.analysis_cache.get(code_hash) {
|
||||
if let Some(cache) = self.analysis_cache.get(code_hash).cloned() {
|
||||
cache
|
||||
} else {
|
||||
let analysis = AnalyzedCode::analyze(&code);
|
||||
self.analysis_cache.put(code_hash, analysis);
|
||||
self.analysis_cache.get(code_hash).unwrap()
|
||||
let analysis = AnalyzedCode::analyze(code);
|
||||
self.analysis_cache.put(*code_hash, analysis.clone());
|
||||
analysis
|
||||
}
|
||||
} else {
|
||||
a = AnalyzedCode::analyze(&code);
|
||||
&a
|
||||
AnalyzedCode::analyze(code)
|
||||
};
|
||||
|
||||
let mut interrupt = analysis
|
||||
.execute_resumable(false, msg, self.block_spec.revision)
|
||||
.resume(());
|
||||
let revision = self.block_spec.revision;
|
||||
|
||||
let output = loop {
|
||||
interrupt = match interrupt {
|
||||
Interrupt::InstructionStart { .. } => unreachable!("tracing is disabled"),
|
||||
Interrupt::AccountExists { interrupt, address } => {
|
||||
let exists = if self.block_spec.revision >= Revision::Spurious {
|
||||
!self.state.is_dead(address)?
|
||||
} else {
|
||||
self.state.exists(address)?
|
||||
};
|
||||
interrupt.resume(AccountExistsStatus { exists })
|
||||
}
|
||||
Interrupt::GetBalance { interrupt, address } => {
|
||||
let balance = self.state.get_balance(address)?;
|
||||
interrupt.resume(Balance { balance })
|
||||
}
|
||||
Interrupt::GetCodeSize { interrupt, address } => {
|
||||
let code_size =
|
||||
u64::try_from(self.state.get_code(address)?.map(|c| c.len()).unwrap_or(0))?
|
||||
.into();
|
||||
interrupt.resume(CodeSize { code_size })
|
||||
}
|
||||
Interrupt::GetStorage {
|
||||
interrupt,
|
||||
address,
|
||||
location,
|
||||
} => {
|
||||
let value = self.state.get_current_storage(address, location)?;
|
||||
interrupt.resume(StorageValue { value })
|
||||
}
|
||||
Interrupt::SetStorage {
|
||||
interrupt,
|
||||
address,
|
||||
location,
|
||||
value: new_val,
|
||||
} => {
|
||||
let current_val = self.state.get_current_storage(address, location)?;
|
||||
let mut host = EvmHost { inner: self };
|
||||
|
||||
let status = if current_val == new_val {
|
||||
StorageStatus::Unchanged
|
||||
} else {
|
||||
self.state.set_storage(address, location, new_val)?;
|
||||
let output = analysis.execute(&mut host, msg, revision);
|
||||
|
||||
let eip1283 = self.block_spec.revision >= Revision::Istanbul
|
||||
|| self.block_spec.revision == Revision::Constantinople;
|
||||
|
||||
if !eip1283 {
|
||||
if current_val == 0 {
|
||||
StorageStatus::Added
|
||||
} else if new_val == 0 {
|
||||
self.state.add_refund(fee::R_SCLEAR);
|
||||
StorageStatus::Deleted
|
||||
} else {
|
||||
StorageStatus::Modified
|
||||
}
|
||||
} else {
|
||||
let sload_cost = {
|
||||
if self.block_spec.revision >= Revision::Berlin {
|
||||
fee::WARM_STORAGE_READ_COST
|
||||
} else if self.block_spec.revision >= Revision::Istanbul {
|
||||
fee::G_SLOAD_ISTANBUL
|
||||
} else {
|
||||
fee::G_SLOAD_TANGERINE_WHISTLE
|
||||
}
|
||||
};
|
||||
|
||||
let mut sstore_reset_gas = fee::G_SRESET;
|
||||
if self.block_spec.revision >= Revision::Berlin {
|
||||
sstore_reset_gas -= fee::COLD_SLOAD_COST;
|
||||
}
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-1283
|
||||
let original_val =
|
||||
self.state.get_original_storage(address, location)?;
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-3529
|
||||
let sstore_clears_refund =
|
||||
if self.block_spec.revision >= Revision::London {
|
||||
sstore_reset_gas + fee::ACCESS_LIST_STORAGE_KEY_COST
|
||||
} else {
|
||||
fee::R_SCLEAR
|
||||
};
|
||||
|
||||
if original_val == current_val {
|
||||
if original_val == 0 {
|
||||
StorageStatus::Added
|
||||
} else {
|
||||
if new_val == 0 {
|
||||
self.state.add_refund(sstore_clears_refund);
|
||||
}
|
||||
StorageStatus::Modified
|
||||
}
|
||||
} else {
|
||||
if original_val != 0 {
|
||||
if current_val == 0 {
|
||||
self.state.subtract_refund(sstore_clears_refund);
|
||||
}
|
||||
if new_val == 0 {
|
||||
self.state.add_refund(sstore_clears_refund);
|
||||
}
|
||||
}
|
||||
if original_val == new_val {
|
||||
let refund = {
|
||||
if original_val == 0 {
|
||||
fee::G_SSET - sload_cost
|
||||
} else {
|
||||
sstore_reset_gas - sload_cost
|
||||
}
|
||||
};
|
||||
|
||||
self.state.add_refund(refund);
|
||||
}
|
||||
StorageStatus::ModifiedAgain
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
interrupt.resume(StorageStatusInfo { status })
|
||||
}
|
||||
Interrupt::GetCodeHash { interrupt, address } => {
|
||||
let hash = h256_to_u256({
|
||||
if self.state.is_dead(address)? {
|
||||
H256::zero()
|
||||
} else {
|
||||
self.state.get_code_hash(address)?
|
||||
}
|
||||
});
|
||||
interrupt.resume(CodeHash { hash })
|
||||
}
|
||||
Interrupt::CopyCode {
|
||||
interrupt,
|
||||
address,
|
||||
offset,
|
||||
max_size,
|
||||
} => {
|
||||
let mut buffer = vec![0; max_size];
|
||||
|
||||
let code = self.state.get_code(address)?.unwrap_or_default();
|
||||
|
||||
let mut copied = 0;
|
||||
if offset < code.len() {
|
||||
copied = min(max_size, code.len() - offset);
|
||||
buffer[..copied].copy_from_slice(&code[offset..offset + copied]);
|
||||
}
|
||||
|
||||
buffer.truncate(copied);
|
||||
let code = buffer.into();
|
||||
|
||||
interrupt.resume(Code { code })
|
||||
}
|
||||
Interrupt::Selfdestruct {
|
||||
interrupt,
|
||||
address,
|
||||
beneficiary,
|
||||
} => {
|
||||
self.state.record_selfdestruct(address);
|
||||
let balance = self.state.get_balance(address)?;
|
||||
self.state.add_to_balance(beneficiary, balance)?;
|
||||
self.state.set_balance(address, 0)?;
|
||||
|
||||
if let Some(tracer) = &mut self.tracer {
|
||||
tracer.capture_self_destruct(address, beneficiary);
|
||||
}
|
||||
|
||||
interrupt.resume(())
|
||||
}
|
||||
Interrupt::Call {
|
||||
interrupt,
|
||||
call_data,
|
||||
} => {
|
||||
let output = match call_data {
|
||||
Call::Create(message) => {
|
||||
let mut res = self.create(message)?;
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-211
|
||||
if res.status_code != StatusCode::Revert {
|
||||
// geth returns CREATE output only in case of REVERT
|
||||
res.output_data = Default::default();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
Call::Call(message) => self.call(message)?,
|
||||
};
|
||||
|
||||
interrupt.resume(CallOutput { output })
|
||||
}
|
||||
Interrupt::GetTxContext { interrupt } => {
|
||||
let base_fee_per_gas = self.header.base_fee_per_gas.unwrap_or(U256::ZERO);
|
||||
let tx_gas_price = self.txn.effective_gas_price(base_fee_per_gas);
|
||||
let tx_origin = self.txn.sender;
|
||||
let block_coinbase = self.beneficiary;
|
||||
let block_number = self.header.number.0;
|
||||
let block_timestamp = self.header.timestamp;
|
||||
let block_gas_limit = self.header.gas_limit;
|
||||
let block_difficulty = self.header.difficulty;
|
||||
let chain_id = self.block_spec.params.chain_id.0.into();
|
||||
let block_base_fee = base_fee_per_gas;
|
||||
|
||||
let context = TxContext {
|
||||
tx_gas_price,
|
||||
tx_origin,
|
||||
block_coinbase,
|
||||
block_number,
|
||||
block_timestamp,
|
||||
block_gas_limit,
|
||||
block_difficulty,
|
||||
chain_id,
|
||||
block_base_fee,
|
||||
};
|
||||
|
||||
interrupt.resume(TxContextData { context })
|
||||
}
|
||||
Interrupt::GetBlockHash {
|
||||
interrupt,
|
||||
block_number,
|
||||
} => {
|
||||
let base_number = self.header.number;
|
||||
let distance = base_number.0 - block_number;
|
||||
assert!(distance <= 256);
|
||||
|
||||
let mut hash = self.header.parent_hash;
|
||||
|
||||
for i in 1..distance {
|
||||
hash = self
|
||||
.state
|
||||
.db()
|
||||
.read_header(BlockNumber(base_number.0 - i), hash)?
|
||||
.context("no header")?
|
||||
.parent_hash;
|
||||
}
|
||||
|
||||
let hash = h256_to_u256(hash);
|
||||
interrupt.resume(BlockHash { hash })
|
||||
}
|
||||
Interrupt::EmitLog {
|
||||
interrupt,
|
||||
address,
|
||||
topics,
|
||||
data,
|
||||
} => {
|
||||
self.state.add_log(Log {
|
||||
address,
|
||||
topics: topics.into_iter().map(u256_to_h256).collect(),
|
||||
data,
|
||||
});
|
||||
|
||||
interrupt.resume(())
|
||||
}
|
||||
Interrupt::AccessAccount { interrupt, address } => {
|
||||
let status = if self.is_precompiled(address) {
|
||||
AccessStatus::Warm
|
||||
} else {
|
||||
self.state.access_account(address)
|
||||
};
|
||||
interrupt.resume(AccessAccountStatus { status })
|
||||
}
|
||||
Interrupt::AccessStorage {
|
||||
interrupt,
|
||||
address,
|
||||
location,
|
||||
} => {
|
||||
let status = self.state.access_storage(address, location);
|
||||
interrupt.resume(AccessStorageStatus { status })
|
||||
}
|
||||
Interrupt::Complete { result, .. } => {
|
||||
let output = match result {
|
||||
Ok(output) => output.into(),
|
||||
Err(status_code) => Output {
|
||||
status_code,
|
||||
gas_left: 0,
|
||||
output_data: bytes::Bytes::new(),
|
||||
create_address: None,
|
||||
},
|
||||
};
|
||||
|
||||
break output;
|
||||
}
|
||||
};
|
||||
};
|
||||
self.tracer.capture_end(&output);
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
@@ -654,10 +376,275 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
struct EvmHost<'r, 'state, 'tracer, 'analysis, 'h, 'c, 't, 'a, B>
|
||||
where
|
||||
B: State,
|
||||
{
|
||||
inner: &'a mut Evm<'r, 'state, 'tracer, 'analysis, 'h, 'c, 't, B>,
|
||||
}
|
||||
|
||||
impl<'r, 'state, 'tracer, 'analysis, 'h, 'c, 't, 'a, B: State> Host
|
||||
for EvmHost<'r, 'state, 'tracer, 'analysis, 'h, 'c, 't, 'a, B>
|
||||
{
|
||||
fn trace_instructions(&self) -> bool {
|
||||
self.inner.tracer.trace_instructions()
|
||||
}
|
||||
fn tracer(&mut self, mut f: impl FnMut(&mut dyn Tracer)) {
|
||||
(f)(self.inner.tracer)
|
||||
}
|
||||
|
||||
fn account_exists(&mut self, address: Address) -> bool {
|
||||
if self.inner.block_spec.revision >= Revision::Spurious {
|
||||
!self.inner.state.is_dead(address).unwrap()
|
||||
} else {
|
||||
self.inner.state.exists(address).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_storage(&mut self, address: Address, location: U256) -> U256 {
|
||||
self.inner
|
||||
.state
|
||||
.get_current_storage(address, location)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn set_storage(&mut self, address: Address, location: U256, new_val: U256) -> StorageStatus {
|
||||
let current_val = self
|
||||
.inner
|
||||
.state
|
||||
.get_current_storage(address, location)
|
||||
.unwrap();
|
||||
|
||||
if current_val == new_val {
|
||||
StorageStatus::Unchanged
|
||||
} else {
|
||||
self.inner
|
||||
.state
|
||||
.set_storage(address, location, new_val)
|
||||
.unwrap();
|
||||
|
||||
let eip1283 = self.inner.block_spec.revision >= Revision::Istanbul
|
||||
|| self.inner.block_spec.revision == Revision::Constantinople;
|
||||
|
||||
if !eip1283 {
|
||||
if current_val == 0 {
|
||||
StorageStatus::Added
|
||||
} else if new_val == 0 {
|
||||
self.inner.state.add_refund(fee::R_SCLEAR);
|
||||
StorageStatus::Deleted
|
||||
} else {
|
||||
StorageStatus::Modified
|
||||
}
|
||||
} else {
|
||||
let sload_cost = {
|
||||
if self.inner.block_spec.revision >= Revision::Berlin {
|
||||
fee::WARM_STORAGE_READ_COST
|
||||
} else if self.inner.block_spec.revision >= Revision::Istanbul {
|
||||
fee::G_SLOAD_ISTANBUL
|
||||
} else {
|
||||
fee::G_SLOAD_TANGERINE_WHISTLE
|
||||
}
|
||||
};
|
||||
|
||||
let mut sstore_reset_gas = fee::G_SRESET;
|
||||
if self.inner.block_spec.revision >= Revision::Berlin {
|
||||
sstore_reset_gas -= fee::COLD_SLOAD_COST;
|
||||
}
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-1283
|
||||
let original_val = self
|
||||
.inner
|
||||
.state
|
||||
.get_original_storage(address, location)
|
||||
.unwrap();
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-3529
|
||||
let sstore_clears_refund = if self.inner.block_spec.revision >= Revision::London {
|
||||
sstore_reset_gas + fee::ACCESS_LIST_STORAGE_KEY_COST
|
||||
} else {
|
||||
fee::R_SCLEAR
|
||||
};
|
||||
|
||||
if original_val == current_val {
|
||||
if original_val == 0 {
|
||||
StorageStatus::Added
|
||||
} else {
|
||||
if new_val == 0 {
|
||||
self.inner.state.add_refund(sstore_clears_refund);
|
||||
}
|
||||
StorageStatus::Modified
|
||||
}
|
||||
} else {
|
||||
if original_val != 0 {
|
||||
if current_val == 0 {
|
||||
self.inner.state.subtract_refund(sstore_clears_refund);
|
||||
}
|
||||
if new_val == 0 {
|
||||
self.inner.state.add_refund(sstore_clears_refund);
|
||||
}
|
||||
}
|
||||
if original_val == new_val {
|
||||
let refund = {
|
||||
if original_val == 0 {
|
||||
fee::G_SSET - sload_cost
|
||||
} else {
|
||||
sstore_reset_gas - sload_cost
|
||||
}
|
||||
};
|
||||
|
||||
self.inner.state.add_refund(refund);
|
||||
}
|
||||
StorageStatus::ModifiedAgain
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_balance(&mut self, address: Address) -> U256 {
|
||||
self.inner.state.get_balance(address).unwrap()
|
||||
}
|
||||
|
||||
fn get_code_size(&mut self, address: Address) -> U256 {
|
||||
u64::try_from(
|
||||
self.inner
|
||||
.state
|
||||
.get_code(address)
|
||||
.unwrap()
|
||||
.map(|c| c.len())
|
||||
.unwrap_or(0),
|
||||
)
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn get_code_hash(&mut self, address: Address) -> U256 {
|
||||
h256_to_u256({
|
||||
if self.inner.state.is_dead(address).unwrap() {
|
||||
H256::zero()
|
||||
} else {
|
||||
self.inner.state.get_code_hash(address).unwrap()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn copy_code(&mut self, address: Address, offset: usize, buffer: &mut [u8]) -> usize {
|
||||
let code = self
|
||||
.inner
|
||||
.state
|
||||
.get_code(address)
|
||||
.unwrap()
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut copied = 0;
|
||||
if offset < code.len() {
|
||||
copied = min(buffer.len(), code.len() - offset);
|
||||
buffer[..copied].copy_from_slice(&code[offset..offset + copied]);
|
||||
}
|
||||
|
||||
copied
|
||||
}
|
||||
|
||||
fn selfdestruct(&mut self, address: Address, beneficiary: Address) {
|
||||
self.inner.state.record_selfdestruct(address);
|
||||
let balance = self.inner.state.get_balance(address).unwrap();
|
||||
self.inner
|
||||
.state
|
||||
.add_to_balance(beneficiary, balance)
|
||||
.unwrap();
|
||||
self.inner.state.set_balance(address, 0).unwrap();
|
||||
|
||||
self.tracer(|t| t.capture_self_destruct(address, beneficiary));
|
||||
}
|
||||
|
||||
fn call(&mut self, msg: Call) -> Output {
|
||||
match msg {
|
||||
Call::Create(message) => {
|
||||
let mut res = self.inner.create(message).unwrap();
|
||||
|
||||
// https://eips.ethereum.org/EIPS/eip-211
|
||||
if res.status_code != StatusCode::Revert {
|
||||
// geth returns CREATE output only in case of REVERT
|
||||
res.output_data = Default::default();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
Call::Call(message) => self.inner.call(message).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tx_context(&mut self) -> TxContext {
|
||||
let base_fee_per_gas = self.inner.header.base_fee_per_gas.unwrap_or(U256::ZERO);
|
||||
let tx_gas_price = self.inner.txn.effective_gas_price(base_fee_per_gas);
|
||||
let tx_origin = self.inner.txn.sender;
|
||||
let block_coinbase = self.inner.beneficiary;
|
||||
let block_number = self.inner.header.number.0;
|
||||
let block_timestamp = self.inner.header.timestamp;
|
||||
let block_gas_limit = self.inner.header.gas_limit;
|
||||
let block_difficulty = self.inner.header.difficulty;
|
||||
let chain_id = self.inner.block_spec.params.chain_id.0.into();
|
||||
let block_base_fee = base_fee_per_gas;
|
||||
|
||||
TxContext {
|
||||
tx_gas_price,
|
||||
tx_origin,
|
||||
block_coinbase,
|
||||
block_number,
|
||||
block_timestamp,
|
||||
block_gas_limit,
|
||||
block_difficulty,
|
||||
chain_id,
|
||||
block_base_fee,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_block_hash(&mut self, block_number: u64) -> U256 {
|
||||
let base_number = self.inner.header.number;
|
||||
let distance = base_number.0 - block_number;
|
||||
assert!(distance <= 256);
|
||||
|
||||
let mut hash = self.inner.header.parent_hash;
|
||||
|
||||
for i in 1..distance {
|
||||
hash = self
|
||||
.inner
|
||||
.state
|
||||
.db()
|
||||
.read_header(BlockNumber(base_number.0 - i), hash)
|
||||
.unwrap()
|
||||
.context("no header")
|
||||
.unwrap()
|
||||
.parent_hash;
|
||||
}
|
||||
|
||||
h256_to_u256(hash)
|
||||
}
|
||||
|
||||
fn emit_log(&mut self, address: Address, data: Bytes, topics: &[U256]) {
|
||||
self.inner.state.add_log(Log {
|
||||
address,
|
||||
topics: topics.iter().copied().map(u256_to_h256).collect(),
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
fn access_account(&mut self, address: Address) -> AccessStatus {
|
||||
if self.inner.is_precompiled(address) {
|
||||
AccessStatus::Warm
|
||||
} else {
|
||||
self.inner.state.access_account(address)
|
||||
}
|
||||
}
|
||||
|
||||
fn access_storage(&mut self, address: Address, location: U256) -> AccessStatus {
|
||||
self.inner.state.access_storage(address, location)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{res::chainspec::MAINNET, InMemoryState};
|
||||
use crate::{execution::tracer::NoopTracer, res::chainspec::MAINNET, InMemoryState};
|
||||
use bytes_literal::bytes;
|
||||
use hex_literal::hex;
|
||||
|
||||
@@ -667,9 +654,10 @@ mod tests {
|
||||
txn: &MessageWithSender,
|
||||
gas: u64,
|
||||
) -> CallResult {
|
||||
let mut tracer = NoopTracer;
|
||||
super::execute(
|
||||
state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut AnalysisCache::default(),
|
||||
header,
|
||||
&MAINNET.collect_block_spec(header.number),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use self::{analysis_cache::AnalysisCache, processor::ExecutionProcessor};
|
||||
use self::{analysis_cache::AnalysisCache, processor::ExecutionProcessor, tracer::NoopTracer};
|
||||
use crate::{consensus, crypto::*, models::*, State};
|
||||
|
||||
pub mod address;
|
||||
@@ -17,10 +17,11 @@ pub fn execute_block<S: State>(
|
||||
) -> anyhow::Result<Vec<Receipt>> {
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut engine = consensus::engine_factory(config.clone())?;
|
||||
let mut tracer = NoopTracer;
|
||||
let config = config.collect_block_spec(header.number);
|
||||
ExecutionProcessor::new(
|
||||
state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *engine,
|
||||
header,
|
||||
|
||||
@@ -20,7 +20,7 @@ where
|
||||
S: State,
|
||||
{
|
||||
state: IntraBlockState<'r, S>,
|
||||
tracer: Option<&'tracer mut dyn Tracer>,
|
||||
tracer: &'tracer mut dyn Tracer,
|
||||
analysis_cache: &'analysis mut AnalysisCache,
|
||||
engine: &'e mut dyn Consensus,
|
||||
header: &'h PartialHeader,
|
||||
@@ -36,7 +36,7 @@ where
|
||||
{
|
||||
pub fn new(
|
||||
state: &'r mut S,
|
||||
tracer: Option<&'tracer mut dyn Tracer>,
|
||||
tracer: &'tracer mut dyn Tracer,
|
||||
analysis_cache: &'analysis mut AnalysisCache,
|
||||
engine: &'e mut dyn Consensus,
|
||||
header: &'h PartialHeader,
|
||||
@@ -160,7 +160,7 @@ where
|
||||
&mut self.state,
|
||||
// https://github.com/rust-lang/rust-clippy/issues/7846
|
||||
#[allow(clippy::needless_option_as_deref)]
|
||||
self.tracer.as_deref_mut(),
|
||||
self.tracer,
|
||||
self.analysis_cache,
|
||||
self.header,
|
||||
self.block_spec,
|
||||
@@ -304,7 +304,11 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{execution::address::create_address, res::chainspec::MAINNET, InMemoryState};
|
||||
use crate::{
|
||||
execution::{address::create_address, tracer::NoopTracer},
|
||||
res::chainspec::MAINNET,
|
||||
InMemoryState,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use bytes_literal::bytes;
|
||||
use hex_literal::hex;
|
||||
@@ -339,9 +343,10 @@ mod tests {
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut engine = engine_factory(MAINNET.clone()).unwrap();
|
||||
let block_spec = MAINNET.collect_block_spec(header.number);
|
||||
let mut tracer = NoopTracer;
|
||||
let mut processor = ExecutionProcessor::new(
|
||||
&mut state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *engine,
|
||||
&header,
|
||||
@@ -384,9 +389,10 @@ mod tests {
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut engine = engine_factory(MAINNET.clone()).unwrap();
|
||||
let block_spec = MAINNET.collect_block_spec(header.number);
|
||||
let mut tracer = NoopTracer;
|
||||
let mut processor = ExecutionProcessor::new(
|
||||
&mut state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *engine,
|
||||
&header,
|
||||
@@ -444,9 +450,10 @@ mod tests {
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut engine = engine_factory(MAINNET.clone()).unwrap();
|
||||
let block_spec = MAINNET.collect_block_spec(header.number);
|
||||
let mut tracer = NoopTracer;
|
||||
let mut processor = ExecutionProcessor::new(
|
||||
&mut state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *engine,
|
||||
&header,
|
||||
@@ -562,9 +569,10 @@ mod tests {
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut engine = engine_factory(MAINNET.clone()).unwrap();
|
||||
let block_spec = MAINNET.collect_block_spec(header.number);
|
||||
let mut tracer = NoopTracer;
|
||||
let mut processor = ExecutionProcessor::new(
|
||||
&mut state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *engine,
|
||||
&header,
|
||||
@@ -671,9 +679,10 @@ mod tests {
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut engine = engine_factory(MAINNET.clone()).unwrap();
|
||||
let block_spec = MAINNET.collect_block_spec(header.number);
|
||||
let mut tracer = NoopTracer;
|
||||
let mut processor = ExecutionProcessor::new(
|
||||
&mut state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *engine,
|
||||
&header,
|
||||
@@ -726,9 +735,10 @@ mod tests {
|
||||
let mut analysis_cache = AnalysisCache::default();
|
||||
let mut engine = engine_factory(MAINNET.clone()).unwrap();
|
||||
let block_spec = MAINNET.collect_block_spec(header.number);
|
||||
let mut tracer = NoopTracer;
|
||||
let mut processor = ExecutionProcessor::new(
|
||||
&mut state,
|
||||
None,
|
||||
&mut tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *engine,
|
||||
&header,
|
||||
|
||||
103
src/execution/tracer/eip3155_tracer.rs
Normal file
103
src/execution/tracer/eip3155_tracer.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use super::*;
|
||||
use crate::{
|
||||
execution::evm::{ExecutionState, OpCode, Output, Stack, StatusCode},
|
||||
models::*,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExecutionStart {
|
||||
pub depth: u16,
|
||||
pub rev: Revision,
|
||||
#[serde(rename = "static")]
|
||||
pub is_static: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct InstructionStart {
|
||||
pub pc: usize,
|
||||
pub op: u8,
|
||||
pub op_name: &'static str,
|
||||
pub gas: u64,
|
||||
pub stack: Stack,
|
||||
pub memory_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct ExecutionEnd {
|
||||
pub error: Option<String>,
|
||||
pub gas: u64,
|
||||
pub gas_used: u64,
|
||||
pub output: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TracerContext {
|
||||
message_gas: u64,
|
||||
}
|
||||
|
||||
/// Tracer which prints to stdout.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct StdoutTracer {
|
||||
execution_stack: Vec<TracerContext>,
|
||||
}
|
||||
|
||||
impl Tracer for StdoutTracer {
|
||||
fn capture_start(
|
||||
&mut self,
|
||||
_: u16,
|
||||
_: Address,
|
||||
_: Address,
|
||||
_: MessageKind,
|
||||
_: Bytes,
|
||||
gas: u64,
|
||||
_: U256,
|
||||
) {
|
||||
self.execution_stack
|
||||
.push(TracerContext { message_gas: gas });
|
||||
}
|
||||
|
||||
fn capture_state(&mut self, env: &ExecutionState, pc: usize, op: OpCode, _: u64, _: u16) {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(&InstructionStart {
|
||||
pc,
|
||||
op: op.0,
|
||||
op_name: op.name(),
|
||||
gas: env.gas_left as u64,
|
||||
stack: env.stack.clone(),
|
||||
memory_size: env.memory.len()
|
||||
})
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
fn capture_end(&mut self, output: &Output) {
|
||||
let context = self.execution_stack.pop().unwrap();
|
||||
let error = match output.status_code {
|
||||
StatusCode::Success => None,
|
||||
other => Some(other.to_string()),
|
||||
};
|
||||
let (gas_left, gas_used) = match output.status_code {
|
||||
StatusCode::Success | StatusCode::Revert => (
|
||||
output.gas_left as u64,
|
||||
context.message_gas - output.gas_left as u64,
|
||||
),
|
||||
_ => (0, context.message_gas),
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string(&ExecutionEnd {
|
||||
error,
|
||||
gas: gas_left,
|
||||
gas_used,
|
||||
output: hex::encode(&output.output_data),
|
||||
})
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,19 @@
|
||||
pub mod eip3155_tracer;
|
||||
|
||||
use auto_impl::auto_impl;
|
||||
pub use eip3155_tracer::StdoutTracer;
|
||||
|
||||
use crate::{
|
||||
execution::evm::{ExecutionState, OpCode, StatusCode},
|
||||
execution::evm::{ExecutionState, OpCode},
|
||||
models::*,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt::Debug,
|
||||
};
|
||||
|
||||
use super::evm::Output;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum CodeKind {
|
||||
@@ -29,7 +39,11 @@ pub enum MessageKind {
|
||||
}
|
||||
|
||||
#[allow(unused, clippy::too_many_arguments)]
|
||||
pub trait Tracer: Send + 'static {
|
||||
#[auto_impl(&mut)]
|
||||
pub trait Tracer: Debug + Send {
|
||||
fn trace_instructions(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn capture_start(
|
||||
&mut self,
|
||||
depth: u16,
|
||||
@@ -44,20 +58,24 @@ pub trait Tracer: Send + 'static {
|
||||
fn capture_state(
|
||||
&mut self,
|
||||
env: &ExecutionState,
|
||||
pc: u64,
|
||||
pc: usize,
|
||||
op: OpCode,
|
||||
cost: u64,
|
||||
return_data: Bytes,
|
||||
depth: u16,
|
||||
err: StatusCode,
|
||||
) {
|
||||
}
|
||||
fn capture_end(&mut self, depth: u16, output: Bytes, gas_left: u64, err: StatusCode) {}
|
||||
fn capture_end(&mut self, output: &Output) {}
|
||||
fn capture_self_destruct(&mut self, caller: Address, beneficiary: Address) {}
|
||||
fn capture_account_read(&mut self, account: Address) {}
|
||||
fn capture_account_write(&mut self, account: Address) {}
|
||||
}
|
||||
|
||||
/// Tracer which does nothing.
|
||||
#[derive(Debug)]
|
||||
pub struct NoopTracer;
|
||||
|
||||
impl Tracer for NoopTracer {}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct CallTracerFlags {
|
||||
pub from: bool,
|
||||
@@ -1,5 +1,7 @@
|
||||
#![feature(
|
||||
bool_to_option,
|
||||
const_for,
|
||||
const_mut_refs,
|
||||
generator_trait,
|
||||
generators,
|
||||
let_else,
|
||||
|
||||
@@ -40,7 +40,7 @@ pub enum Revision {
|
||||
}
|
||||
|
||||
impl Revision {
|
||||
pub fn iter() -> impl IntoIterator<Item = Self> {
|
||||
pub const fn iter() -> [Revision; Revision::len()] {
|
||||
[
|
||||
Self::Frontier,
|
||||
Self::Homestead,
|
||||
|
||||
@@ -79,7 +79,7 @@ fn execute_batch_of_blocks<E: EnvironmentKind>(
|
||||
let mut call_tracer = CallTracer::default();
|
||||
let receipts = ExecutionProcessor::new(
|
||||
&mut buffer,
|
||||
Some(&mut call_tracer),
|
||||
&mut call_tracer,
|
||||
&mut analysis_cache,
|
||||
&mut *consensus_engine,
|
||||
&header,
|
||||
|
||||
Reference in New Issue
Block a user