mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-22 17:17:58 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 91773dd281 | |||
| c34f5a3d95 | |||
| 19075f76e0 | |||
| 0038634695 | |||
| 09d56f5177 | |||
| a59e287fa1 |
Generated
+451
-6
@@ -659,7 +659,7 @@ checksum = "4f317d20f047b3de4d9728c556e2e9a92c9a507702d2016424cd8be13a74ca5e"
|
||||
dependencies = [
|
||||
"alloy-json-rpc",
|
||||
"alloy-primitives",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"derive_more 2.0.1",
|
||||
"futures",
|
||||
"futures-utils-wasm",
|
||||
@@ -1189,6 +1189,151 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 2.5.3",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener-strategy",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-executor"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
"futures-lite",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-global-executor"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
|
||||
dependencies = [
|
||||
"async-channel 2.5.0",
|
||||
"async-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"parking",
|
||||
"polling",
|
||||
"rustix",
|
||||
"slab",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
|
||||
dependencies = [
|
||||
"event-listener 5.4.1",
|
||||
"event-listener-strategy",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00"
|
||||
dependencies = [
|
||||
"async-channel 2.5.0",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-signal",
|
||||
"async-task",
|
||||
"blocking",
|
||||
"cfg-if",
|
||||
"event-listener 5.4.1",
|
||||
"futures-lite",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-signal"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"atomic-waker",
|
||||
"cfg-if",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"rustix",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b"
|
||||
dependencies = [
|
||||
"async-channel 1.9.0",
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-process",
|
||||
"crossbeam-utils",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"gloo-timers",
|
||||
"kv-log-macro",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.6"
|
||||
@@ -1211,6 +1356,12 @@ dependencies = [
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-task"
|
||||
version = "4.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.88"
|
||||
@@ -1266,6 +1417,12 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
@@ -1404,6 +1561,19 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
|
||||
dependencies = [
|
||||
"async-channel 2.5.0",
|
||||
"async-task",
|
||||
"futures-io",
|
||||
"futures-lite",
|
||||
"piper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blst"
|
||||
version = "0.3.14"
|
||||
@@ -1437,6 +1607,29 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bson"
|
||||
version = "2.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969a9ba84b0ff843813e7249eed1678d9b6607ce5a3b8f0a47af3fcf7978e6e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.22.1",
|
||||
"bitvec",
|
||||
"getrandom 0.2.16",
|
||||
"getrandom 0.3.3",
|
||||
"hex",
|
||||
"indexmap 2.10.0",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_json",
|
||||
"time",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
@@ -1479,6 +1672,32 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cacache"
|
||||
version = "13.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5063741c7b2e260bbede781cf4679632dd90e2718e99f7715e46824b65670b"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"digest 0.10.7",
|
||||
"either",
|
||||
"futures",
|
||||
"hex",
|
||||
"libc",
|
||||
"memmap2",
|
||||
"miette",
|
||||
"reflink-copy",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
"sha2 0.10.9",
|
||||
"ssri",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.25"
|
||||
@@ -1559,6 +1778,15 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-hex"
|
||||
version = "1.14.1"
|
||||
@@ -2120,6 +2348,33 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener-strategy"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
|
||||
dependencies = [
|
||||
"event-listener 5.4.1",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "expander"
|
||||
version = "2.2.1"
|
||||
@@ -2361,6 +2616,19 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
@@ -2440,8 +2708,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2451,9 +2721,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2478,6 +2750,18 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
@@ -2572,6 +2856,12 @@ version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
@@ -2724,7 +3014,7 @@ version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -3094,6 +3384,15 @@ dependencies = [
|
||||
"sha3-asm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kv-log-macro"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -3119,7 +3418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"digest 0.9.0",
|
||||
"hmac-drbg",
|
||||
"libsecp256k1-core",
|
||||
@@ -3187,6 +3486,9 @@ name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
dependencies = [
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "loom"
|
||||
@@ -3236,6 +3538,15 @@ version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memory-db"
|
||||
version = "0.32.0"
|
||||
@@ -3257,6 +3568,29 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
|
||||
dependencies = [
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"thiserror 1.0.69",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "5.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
@@ -3386,7 +3720,7 @@ version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -3543,6 +3877,12 @@ dependencies = [
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
@@ -3648,6 +3988,17 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "piper"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"fastrand",
|
||||
"futures-io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
@@ -3701,6 +4052,21 @@ dependencies = [
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"hermit-abi 0.5.2",
|
||||
"pin-project-lite",
|
||||
"rustix",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
@@ -3988,6 +4354,18 @@ dependencies = [
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reflink-copy"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78c81d000a2c524133cc00d2f92f019d399e57906c3b7119271a2495354fe895"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"rustix",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -4038,7 +4416,7 @@ version = "0.12.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
@@ -4093,6 +4471,7 @@ dependencies = [
|
||||
"moka",
|
||||
"once_cell",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -4133,9 +4512,12 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"alloy",
|
||||
"anyhow",
|
||||
"bson",
|
||||
"cacache",
|
||||
"clap",
|
||||
"futures",
|
||||
"indexmap 2.10.0",
|
||||
"once_cell",
|
||||
"revive-dt-common",
|
||||
"revive-dt-compiler",
|
||||
"revive-dt-config",
|
||||
@@ -4144,8 +4526,10 @@ dependencies = [
|
||||
"revive-dt-node-interaction",
|
||||
"revive-dt-report",
|
||||
"semver 1.0.26",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"temp-dir",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@@ -4159,6 +4543,7 @@ dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-sol-types",
|
||||
"anyhow",
|
||||
"regex",
|
||||
"revive-common",
|
||||
"revive-dt-common",
|
||||
"semver 1.0.26",
|
||||
@@ -4201,6 +4586,7 @@ name = "revive-dt-report"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"revive-dt-common",
|
||||
"revive-dt-compiler",
|
||||
"revive-dt-config",
|
||||
"revive-dt-format",
|
||||
@@ -4647,6 +5033,7 @@ version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
dependencies = [
|
||||
"indexmap 2.10.0",
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
@@ -4690,7 +5077,7 @@ version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
@@ -4724,6 +5111,28 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.9"
|
||||
@@ -5198,6 +5607,23 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ssri"
|
||||
version = "9.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da7a2b3c2bc9693bcb40870c4e9b5bf0d79f9cb46273321bf855ec513e919082"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"digest 0.10.7",
|
||||
"hex",
|
||||
"miette",
|
||||
"serde",
|
||||
"sha-1",
|
||||
"sha2 0.10.9",
|
||||
"thiserror 1.0.69",
|
||||
"xxhash-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@@ -5848,6 +6274,12 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
@@ -5891,6 +6323,7 @@ checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -5900,6 +6333,12 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
@@ -6465,6 +6904,12 @@ dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xxhash-rust"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
|
||||
@@ -25,10 +25,13 @@ revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
|
||||
alloy-primitives = "1.2.1"
|
||||
alloy-sol-types = "1.2.1"
|
||||
anyhow = "1.0"
|
||||
bson = { version = "2.15.0" }
|
||||
cacache = { version = "13.1.0" }
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
foundry-compilers-artifacts = { version = "0.18.0" }
|
||||
futures = { version = "0.3.31" }
|
||||
hex = "0.4.3"
|
||||
regex = "1"
|
||||
moka = "0.12.10"
|
||||
reqwest = { version = "0.12.15", features = ["json"] }
|
||||
once_cell = "1.21"
|
||||
@@ -44,6 +47,7 @@ sp-core = "36.1.0"
|
||||
sp-runtime = "41.1.0"
|
||||
temp-dir = { version = "0.1.16" }
|
||||
tempfile = "3.3"
|
||||
thiserror = "2"
|
||||
tokio = { version = "1.47.0", default-features = false, features = [
|
||||
"rt-multi-thread",
|
||||
"process",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"modes": [
|
||||
"Y >=0.8.9",
|
||||
"E",
|
||||
"I"
|
||||
"E"
|
||||
],
|
||||
"cases": [
|
||||
{
|
||||
|
||||
@@ -13,4 +13,5 @@ anyhow = { workspace = true }
|
||||
moka = { workspace = true, features = ["sync"] }
|
||||
once_cell = { workspace = true }
|
||||
semver = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true, default-features = false, features = ["time"] }
|
||||
|
||||
@@ -47,27 +47,3 @@ pub fn read_dir(path: impl AsRef<Path>) -> Result<Box<dyn Iterator<Item = Result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PathExt {
|
||||
fn cached_canonicalize(&self) -> Result<PathBuf>;
|
||||
}
|
||||
|
||||
impl<T> PathExt for T
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
fn cached_canonicalize(&self) -> Result<PathBuf> {
|
||||
static CANONICALIZATION_CACHE: Lazy<Cache<PathBuf, PathBuf>> =
|
||||
Lazy::new(|| Cache::new(10_000));
|
||||
|
||||
let path = self.as_ref().to_path_buf();
|
||||
match CANONICALIZATION_CACHE.get(&path) {
|
||||
Some(canonicalized) => Ok(canonicalized),
|
||||
None => {
|
||||
let canonicalized = path.canonicalize()?;
|
||||
CANONICALIZATION_CACHE.insert(path, canonicalized.clone());
|
||||
Ok(canonicalized)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
mod mode;
|
||||
mod version_or_requirement;
|
||||
|
||||
pub use mode::*;
|
||||
pub use version_or_requirement::*;
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
use crate::types::VersionOrRequirement;
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// This represents a mode that a given test should be run with, if possible.
|
||||
///
|
||||
/// We obtain this by taking a [`ParsedMode`], which may be looser or more strict
|
||||
/// in its requirements, and then expanding it out into a list of [`Mode`]s.
|
||||
///
|
||||
/// Use [`ParsedMode::to_test_modes()`] to do this.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Mode {
|
||||
pub pipeline: ModePipeline,
|
||||
pub optimize_setting: ModeOptimizerSetting,
|
||||
pub version: Option<semver::VersionReq>,
|
||||
}
|
||||
|
||||
impl Display for Mode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.pipeline.fmt(f)?;
|
||||
f.write_str(" ")?;
|
||||
self.optimize_setting.fmt(f)?;
|
||||
|
||||
if let Some(version) = &self.version {
|
||||
f.write_str(" ")?;
|
||||
version.fmt(f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
/// Return all of the available mode combinations.
|
||||
pub fn all() -> impl Iterator<Item = Mode> {
|
||||
ModePipeline::test_cases().flat_map(|pipeline| {
|
||||
ModeOptimizerSetting::test_cases().map(move |optimize_setting| Mode {
|
||||
pipeline,
|
||||
optimize_setting,
|
||||
version: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolves the [`Mode`]'s solidity version requirement into a [`VersionOrRequirement`] if
|
||||
/// the requirement is present on the object. Otherwise, the passed default version is used.
|
||||
pub fn compiler_version_to_use(&self, default: Version) -> VersionOrRequirement {
|
||||
match self.version {
|
||||
Some(ref requirement) => requirement.clone().into(),
|
||||
None => default.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// What do we want the compiler to do?
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
pub enum ModePipeline {
|
||||
/// Compile Solidity code via Yul IR
|
||||
ViaYulIR,
|
||||
/// Compile Solidity direct to assembly
|
||||
ViaEVMAssembly,
|
||||
}
|
||||
|
||||
impl FromStr for ModePipeline {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
// via Yul IR
|
||||
"Y" => Ok(ModePipeline::ViaYulIR),
|
||||
// Don't go via Yul IR
|
||||
"E" => Ok(ModePipeline::ViaEVMAssembly),
|
||||
// Anything else that we see isn't a mode at all
|
||||
_ => Err(anyhow::anyhow!(
|
||||
"Unsupported pipeline '{s}': expected 'Y' or 'E'"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModePipeline {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ModePipeline::ViaYulIR => f.write_str("Y"),
|
||||
ModePipeline::ViaEVMAssembly => f.write_str("E"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModePipeline {
|
||||
/// Should we go via Yul IR?
|
||||
pub fn via_yul_ir(&self) -> bool {
|
||||
matches!(self, ModePipeline::ViaYulIR)
|
||||
}
|
||||
|
||||
/// An iterator over the available pipelines that we'd like to test,
|
||||
/// when an explicit pipeline was not specified.
|
||||
pub fn test_cases() -> impl Iterator<Item = ModePipeline> + Clone {
|
||||
[ModePipeline::ViaYulIR, ModePipeline::ViaEVMAssembly].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
pub enum ModeOptimizerSetting {
|
||||
/// 0 / -: Don't apply any optimizations
|
||||
M0,
|
||||
/// 1: Apply less than default optimizations
|
||||
M1,
|
||||
/// 2: Apply the default optimizations
|
||||
M2,
|
||||
/// 3 / +: Apply aggressive optimizations
|
||||
M3,
|
||||
/// s: Optimize for size
|
||||
Ms,
|
||||
/// z: Aggressively optimize for size
|
||||
Mz,
|
||||
}
|
||||
|
||||
impl FromStr for ModeOptimizerSetting {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"M0" => Ok(ModeOptimizerSetting::M0),
|
||||
"M1" => Ok(ModeOptimizerSetting::M1),
|
||||
"M2" => Ok(ModeOptimizerSetting::M2),
|
||||
"M3" => Ok(ModeOptimizerSetting::M3),
|
||||
"Ms" => Ok(ModeOptimizerSetting::Ms),
|
||||
"Mz" => Ok(ModeOptimizerSetting::Mz),
|
||||
_ => Err(anyhow::anyhow!(
|
||||
"Unsupported optimizer setting '{s}': expected 'M0', 'M1', 'M2', 'M3', 'Ms' or 'Mz'"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModeOptimizerSetting {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ModeOptimizerSetting::M0 => f.write_str("M0"),
|
||||
ModeOptimizerSetting::M1 => f.write_str("M1"),
|
||||
ModeOptimizerSetting::M2 => f.write_str("M2"),
|
||||
ModeOptimizerSetting::M3 => f.write_str("M3"),
|
||||
ModeOptimizerSetting::Ms => f.write_str("Ms"),
|
||||
ModeOptimizerSetting::Mz => f.write_str("Mz"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModeOptimizerSetting {
|
||||
/// An iterator over the available optimizer settings that we'd like to test,
|
||||
/// when an explicit optimizer setting was not specified.
|
||||
pub fn test_cases() -> impl Iterator<Item = ModeOptimizerSetting> + Clone {
|
||||
[
|
||||
// No optimizations:
|
||||
ModeOptimizerSetting::M0,
|
||||
// Aggressive optimizations:
|
||||
ModeOptimizerSetting::M3,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
/// Are any optimizations enabled?
|
||||
pub fn optimizations_enabled(&self) -> bool {
|
||||
!matches!(self, ModeOptimizerSetting::M0)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use anyhow::{Error, bail};
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum VersionOrRequirement {
|
||||
Version(Version),
|
||||
Requirement(VersionReq),
|
||||
@@ -42,26 +39,3 @@ impl TryFrom<VersionOrRequirement> for VersionReq {
|
||||
Ok(requirement)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for VersionOrRequirement {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(version) = Version::parse(s) {
|
||||
Ok(Self::Version(version))
|
||||
} else if let Ok(version_req) = VersionReq::parse(s) {
|
||||
Ok(Self::Requirement(version_req))
|
||||
} else {
|
||||
bail!("Not a valid version or version requirement")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for VersionOrRequirement {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VersionOrRequirement::Version(version) => version.fmt(f),
|
||||
VersionOrRequirement::Requirement(version_req) => version_req.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
use semver::Version;
|
||||
|
||||
/// This is the first version of solc that supports the `--via-ir` flag / "viaIR" input JSON.
|
||||
pub const SOLC_VERSION_SUPPORTING_VIA_YUL_IR: Version = Version::new(0, 8, 13);
|
||||
@@ -3,6 +3,8 @@
|
||||
//! - Polkadot revive resolc compiler
|
||||
//! - Polkadot revive Wasm compiler
|
||||
|
||||
mod constants;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
hash::Hash,
|
||||
@@ -19,6 +21,9 @@ use revive_dt_common::cached_fs::read_to_string;
|
||||
use revive_dt_common::types::VersionOrRequirement;
|
||||
use revive_dt_config::Arguments;
|
||||
|
||||
// Re-export this as it's a part of the compiler interface.
|
||||
pub use revive_dt_common::types::{Mode, ModeOptimizerSetting, ModePipeline};
|
||||
|
||||
pub mod revive_js;
|
||||
pub mod revive_resolc;
|
||||
pub mod solc;
|
||||
@@ -43,13 +48,20 @@ pub trait SolidityCompiler {
|
||||
) -> impl Future<Output = anyhow::Result<PathBuf>>;
|
||||
|
||||
fn version(&self) -> anyhow::Result<Version>;
|
||||
|
||||
/// Does the compiler support the provided mode and version settings?
|
||||
fn supports_mode(
|
||||
compiler_version: &Version,
|
||||
optimize_setting: ModeOptimizerSetting,
|
||||
pipeline: ModePipeline,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
/// The generic compilation input configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CompilerInput {
|
||||
pub enable_optimization: Option<bool>,
|
||||
pub via_ir: Option<bool>,
|
||||
pub pipeline: Option<ModePipeline>,
|
||||
pub optimization: Option<ModeOptimizerSetting>,
|
||||
pub evm_version: Option<EVMVersion>,
|
||||
pub allow_paths: Vec<PathBuf>,
|
||||
pub base_path: Option<PathBuf>,
|
||||
@@ -85,8 +97,8 @@ where
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
input: CompilerInput {
|
||||
enable_optimization: Default::default(),
|
||||
via_ir: Default::default(),
|
||||
pipeline: Default::default(),
|
||||
optimization: Default::default(),
|
||||
evm_version: Default::default(),
|
||||
allow_paths: Default::default(),
|
||||
base_path: Default::default(),
|
||||
@@ -98,13 +110,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_optimization(mut self, value: impl Into<Option<bool>>) -> Self {
|
||||
self.input.enable_optimization = value.into();
|
||||
pub fn with_optimization(mut self, value: impl Into<Option<ModeOptimizerSetting>>) -> Self {
|
||||
self.input.optimization = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_via_ir(mut self, value: impl Into<Option<bool>>) -> Self {
|
||||
self.input.via_ir = value.into();
|
||||
pub fn with_pipeline(mut self, value: impl Into<Option<ModePipeline>>) -> Self {
|
||||
self.input.pipeline = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
@@ -157,6 +169,14 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn then(self, callback: impl FnOnce(Self) -> Self) -> Self {
|
||||
callback(self)
|
||||
}
|
||||
|
||||
pub fn try_then<E>(self, callback: impl FnOnce(Self) -> Result<Self, E>) -> Result<Self, E> {
|
||||
callback(self)
|
||||
}
|
||||
|
||||
pub async fn try_build(
|
||||
self,
|
||||
compiler_path: impl AsRef<Path>,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//! compiling contracts to PolkaVM (PVM) bytecode.
|
||||
|
||||
use std::{
|
||||
os::unix::process::CommandExt,
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
@@ -15,7 +14,8 @@ use revive_solc_json_interface::{
|
||||
SolcStandardJsonOutput,
|
||||
};
|
||||
|
||||
use crate::{CompilerInput, CompilerOutput, SolidityCompiler};
|
||||
use super::constants::SOLC_VERSION_SUPPORTING_VIA_YUL_IR;
|
||||
use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler};
|
||||
|
||||
use alloy::json_abi::JsonAbi;
|
||||
use anyhow::Context;
|
||||
@@ -40,9 +40,8 @@ impl SolidityCompiler for Resolc {
|
||||
async fn build(
|
||||
&self,
|
||||
CompilerInput {
|
||||
enable_optimization,
|
||||
// Ignored and not honored since this is required for the resolc compilation.
|
||||
via_ir: _via_ir,
|
||||
pipeline,
|
||||
optimization,
|
||||
evm_version,
|
||||
allow_paths,
|
||||
base_path,
|
||||
@@ -54,6 +53,12 @@ impl SolidityCompiler for Resolc {
|
||||
}: CompilerInput,
|
||||
additional_options: Self::Options,
|
||||
) -> anyhow::Result<CompilerOutput> {
|
||||
if !matches!(pipeline, None | Some(ModePipeline::ViaYulIR)) {
|
||||
anyhow::bail!(
|
||||
"Resolc only supports the Y (via Yul IR) pipeline, but the provided pipeline is {pipeline:?}"
|
||||
);
|
||||
}
|
||||
|
||||
let input = SolcStandardJsonInput {
|
||||
language: SolcStandardJsonInputLanguage::Solidity,
|
||||
sources: sources
|
||||
@@ -82,7 +87,9 @@ impl SolidityCompiler for Resolc {
|
||||
output_selection: Some(SolcStandardJsonInputSettingsSelection::new_required()),
|
||||
via_ir: Some(true),
|
||||
optimizer: SolcStandardJsonInputSettingsOptimizer::new(
|
||||
enable_optimization.unwrap_or(false),
|
||||
optimization
|
||||
.unwrap_or(ModeOptimizerSetting::M0)
|
||||
.optimizations_enabled(),
|
||||
None,
|
||||
&Version::new(0, 0, 0),
|
||||
false,
|
||||
@@ -93,14 +100,11 @@ impl SolidityCompiler for Resolc {
|
||||
};
|
||||
|
||||
let mut command = AsyncCommand::new(&self.resolc_path);
|
||||
unsafe {
|
||||
command
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.arg("--standard-json")
|
||||
.pre_exec(|| Ok(()))
|
||||
};
|
||||
command
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.arg("--standard-json");
|
||||
|
||||
if let Some(ref base_path) = base_path {
|
||||
command.arg("--base-path").arg(base_path);
|
||||
@@ -219,15 +223,12 @@ impl SolidityCompiler for Resolc {
|
||||
// Logic for parsing the resolc version from the following string:
|
||||
// Solidity frontend for the revive compiler version 0.3.0+commit.b238913.llvm-18.1.8
|
||||
|
||||
let output = unsafe {
|
||||
Command::new(self.resolc_path.as_path())
|
||||
.arg("--version")
|
||||
.stdout(Stdio::piped())
|
||||
.pre_exec(|| Ok(()))
|
||||
.spawn()?
|
||||
.wait_with_output()?
|
||||
.stdout
|
||||
};
|
||||
let output = Command::new(self.resolc_path.as_path())
|
||||
.arg("--version")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?
|
||||
.wait_with_output()?
|
||||
.stdout;
|
||||
let output = String::from_utf8_lossy(&output);
|
||||
let version_string = output
|
||||
.split("version ")
|
||||
@@ -239,6 +240,18 @@ impl SolidityCompiler for Resolc {
|
||||
|
||||
Version::parse(version_string).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn supports_mode(
|
||||
compiler_version: &Version,
|
||||
_optimize_setting: ModeOptimizerSetting,
|
||||
pipeline: ModePipeline,
|
||||
) -> bool {
|
||||
// We only support the Y (IE compile via Yul IR) mode here, which also means that we can
|
||||
// only use solc version 0.8.13 and above. We must always compile via Yul IR as resolc
|
||||
// needs this to translate to LLVM IR and then RISCV.
|
||||
pipeline == ModePipeline::ViaYulIR
|
||||
&& compiler_version >= &SOLC_VERSION_SUPPORTING_VIA_YUL_IR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+45
-39
@@ -2,7 +2,6 @@
|
||||
//! compiling contracts to EVM bytecode.
|
||||
|
||||
use std::{
|
||||
os::unix::process::CommandExt,
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
@@ -11,7 +10,8 @@ use revive_dt_common::types::VersionOrRequirement;
|
||||
use revive_dt_config::Arguments;
|
||||
use revive_dt_solc_binaries::download_solc;
|
||||
|
||||
use crate::{CompilerInput, CompilerOutput, SolidityCompiler};
|
||||
use super::constants::SOLC_VERSION_SUPPORTING_VIA_YUL_IR;
|
||||
use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler};
|
||||
|
||||
use anyhow::Context;
|
||||
use foundry_compilers_artifacts::{
|
||||
@@ -32,12 +32,12 @@ pub struct Solc {
|
||||
impl SolidityCompiler for Solc {
|
||||
type Options = ();
|
||||
|
||||
#[tracing::instrument(level = "info", ret)]
|
||||
#[tracing::instrument(level = "debug", ret)]
|
||||
async fn build(
|
||||
&self,
|
||||
CompilerInput {
|
||||
enable_optimization,
|
||||
via_ir,
|
||||
pipeline,
|
||||
optimization,
|
||||
evm_version,
|
||||
allow_paths,
|
||||
base_path,
|
||||
@@ -47,6 +47,17 @@ impl SolidityCompiler for Solc {
|
||||
}: CompilerInput,
|
||||
_: Self::Options,
|
||||
) -> anyhow::Result<CompilerOutput> {
|
||||
let compiler_supports_via_ir = self.version()? >= SOLC_VERSION_SUPPORTING_VIA_YUL_IR;
|
||||
|
||||
// Be careful to entirely omit the viaIR field if the compiler does not support it,
|
||||
// as it will error if you provide fields it does not know about. Because
|
||||
// `supports_mode` is called prior to instantiating a compiler, we should never
|
||||
// ask for something which is invalid.
|
||||
let via_ir = match (pipeline, compiler_supports_via_ir) {
|
||||
(pipeline, true) => pipeline.map(|p| p.via_yul_ir()),
|
||||
(_pipeline, false) => None,
|
||||
};
|
||||
|
||||
let input = SolcInput {
|
||||
language: SolcLanguage::Solidity,
|
||||
sources: Sources(
|
||||
@@ -57,7 +68,7 @@ impl SolidityCompiler for Solc {
|
||||
),
|
||||
settings: Settings {
|
||||
optimizer: Optimizer {
|
||||
enabled: enable_optimization,
|
||||
enabled: optimization.map(|o| o.optimizations_enabled()),
|
||||
details: Some(Default::default()),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -103,14 +114,11 @@ impl SolidityCompiler for Solc {
|
||||
};
|
||||
|
||||
let mut command = AsyncCommand::new(&self.solc_path);
|
||||
unsafe {
|
||||
command
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.arg("--standard-json")
|
||||
.pre_exec(|| Ok(()))
|
||||
};
|
||||
command
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.arg("--standard-json");
|
||||
|
||||
if let Some(ref base_path) = base_path {
|
||||
command.arg("--base-path").arg(base_path);
|
||||
@@ -124,20 +132,12 @@ impl SolidityCompiler for Solc {
|
||||
.join(","),
|
||||
);
|
||||
}
|
||||
let mut child = command
|
||||
.spawn()
|
||||
.inspect_err(|err| tracing::error!(%err, "Failed to spawn the solc command"))?;
|
||||
let mut child = command.spawn()?;
|
||||
|
||||
let stdin = child.stdin.as_mut().expect("should be piped");
|
||||
let serialized_input = serde_json::to_vec(&input)?;
|
||||
stdin
|
||||
.write_all(&serialized_input)
|
||||
.await
|
||||
.inspect_err(|err| tracing::error!(%err, "Failed to write standard JSON to stdin"))?;
|
||||
let output = child
|
||||
.wait_with_output()
|
||||
.await
|
||||
.inspect_err(|err| tracing::error!(%err, "Failed to get the output of solc"))?;
|
||||
stdin.write_all(&serialized_input).await?;
|
||||
let output = child.wait_with_output().await?;
|
||||
|
||||
if !output.status.success() {
|
||||
let json_in = serde_json::to_string_pretty(&input)?;
|
||||
@@ -174,13 +174,10 @@ impl SolidityCompiler for Solc {
|
||||
|
||||
let mut compiler_output = CompilerOutput::default();
|
||||
for (contract_path, contracts) in parsed.contracts {
|
||||
let map =
|
||||
compiler_output
|
||||
.contracts
|
||||
.entry(contract_path.canonicalize().inspect_err(
|
||||
|err| tracing::error!(%err, "Canonicalization of path failed"),
|
||||
)?)
|
||||
.or_default();
|
||||
let map = compiler_output
|
||||
.contracts
|
||||
.entry(contract_path.canonicalize()?)
|
||||
.or_default();
|
||||
for (contract_name, contract_info) in contracts.into_iter() {
|
||||
let source_code = contract_info
|
||||
.evm
|
||||
@@ -220,13 +217,10 @@ impl SolidityCompiler for Solc {
|
||||
// Version: 0.8.30+commit.73712a01.Darwin.appleclang
|
||||
// ```
|
||||
|
||||
let child = unsafe {
|
||||
Command::new(self.solc_path.as_path())
|
||||
.arg("--version")
|
||||
.stdout(Stdio::piped())
|
||||
.pre_exec(|| Ok(()))
|
||||
.spawn()?
|
||||
};
|
||||
let child = Command::new(self.solc_path.as_path())
|
||||
.arg("--version")
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
let output = child.wait_with_output()?;
|
||||
let output = String::from_utf8_lossy(&output.stdout);
|
||||
let version_line = output
|
||||
@@ -240,6 +234,18 @@ impl SolidityCompiler for Solc {
|
||||
|
||||
Version::parse(version_string).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn supports_mode(
|
||||
compiler_version: &Version,
|
||||
_optimize_setting: ModeOptimizerSetting,
|
||||
pipeline: ModePipeline,
|
||||
) -> bool {
|
||||
// solc 0.8.13 and above supports --via-ir, and less than that does not. Thus, we support mode E
|
||||
// (ie no Yul IR) in either case, but only support Y (via Yul IR) if the compiler is new enough.
|
||||
pipeline == ModePipeline::ViaEVMAssembly
|
||||
|| (pipeline == ModePipeline::ViaYulIR
|
||||
&& compiler_version >= &SOLC_VERSION_SUPPORTING_VIA_YUL_IR)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -58,10 +58,6 @@ pub struct Arguments {
|
||||
#[arg(long = "geth-start-timeout", default_value = "5000")]
|
||||
pub geth_start_timeout: u64,
|
||||
|
||||
/// The test network chain ID.
|
||||
#[arg(short, long = "network-id", default_value = "420420420")]
|
||||
pub network_id: u64,
|
||||
|
||||
/// Configure nodes according to this genesis.json file.
|
||||
#[arg(long = "genesis", default_value = "genesis.json")]
|
||||
pub genesis_file: PathBuf,
|
||||
@@ -124,6 +120,10 @@ pub struct Arguments {
|
||||
/// By default it uses `eth-rpc` binary found in `$PATH`.
|
||||
#[arg(short = 'p', long = "eth_proxy", default_value = "eth-rpc")]
|
||||
pub eth_proxy: PathBuf,
|
||||
|
||||
/// Controls if the compilation cache should be invalidated or not.
|
||||
#[arg(short, long)]
|
||||
pub invalidate_compilation_cache: bool,
|
||||
}
|
||||
|
||||
impl Arguments {
|
||||
|
||||
@@ -23,12 +23,17 @@ revive-dt-report = { workspace = true }
|
||||
|
||||
alloy = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
bson = { workspace = true }
|
||||
cacache = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
semver = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
temp-dir = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
//! A wrapper around the compiler which allows for caching of compilation artifacts so that they can
|
||||
//! be reused between runs.
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use futures::FutureExt;
|
||||
use revive_dt_common::iterators::FilesWithExtensionIterator;
|
||||
use revive_dt_compiler::{Compiler, CompilerOutput, Mode, SolidityCompiler};
|
||||
use revive_dt_config::Arguments;
|
||||
use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata};
|
||||
|
||||
use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address};
|
||||
use anyhow::{Error, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tracing::{Instrument, debug, debug_span, instrument};
|
||||
|
||||
use crate::Platform;
|
||||
|
||||
pub struct CachedCompiler(ArtifactsCache);
|
||||
|
||||
impl CachedCompiler {
|
||||
pub async fn new(path: impl AsRef<Path>, invalidate_cache: bool) -> Result<Self> {
|
||||
let mut cache = ArtifactsCache::new(path);
|
||||
if invalidate_cache {
|
||||
cache = cache.with_invalidated_cache().await?;
|
||||
}
|
||||
Ok(Self(cache))
|
||||
}
|
||||
|
||||
/// Compiles or gets the compilation artifacts from the cache.
|
||||
#[instrument(
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(
|
||||
metadata_file_path = %metadata_file_path.as_ref().display(),
|
||||
%mode,
|
||||
platform = P::config_id().to_string()
|
||||
),
|
||||
err
|
||||
)]
|
||||
pub async fn compile_contracts<P: Platform>(
|
||||
&self,
|
||||
metadata: &Metadata,
|
||||
metadata_file_path: impl AsRef<Path>,
|
||||
mode: &Mode,
|
||||
config: &Arguments,
|
||||
deployed_libraries: Option<&HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
) -> Result<(CompilerOutput, Version)> {
|
||||
static CACHE_KEY_LOCK: Lazy<RwLock<HashMap<CacheKey, Arc<Mutex<()>>>>> =
|
||||
Lazy::new(Default::default);
|
||||
|
||||
let compiler_version_or_requirement = mode.compiler_version_to_use(config.solc.clone());
|
||||
let compiler_path = <P::Compiler as SolidityCompiler>::get_compiler_executable(
|
||||
config,
|
||||
compiler_version_or_requirement,
|
||||
)
|
||||
.await?;
|
||||
let compiler_version =
|
||||
<P::Compiler as SolidityCompiler>::new(compiler_path.clone()).version()?;
|
||||
|
||||
let cache_key = CacheKey {
|
||||
platform_key: P::config_id().to_string(),
|
||||
compiler_version: compiler_version.clone(),
|
||||
metadata_file_path: metadata_file_path.as_ref().to_path_buf(),
|
||||
solc_mode: mode.clone(),
|
||||
};
|
||||
|
||||
let compilation_callback = || {
|
||||
async move {
|
||||
compile_contracts::<P>(
|
||||
metadata.directory()?,
|
||||
compiler_path,
|
||||
metadata.files_to_compile()?,
|
||||
mode,
|
||||
deployed_libraries,
|
||||
)
|
||||
.map(|compilation_result| compilation_result.map(CacheValue::new))
|
||||
.await
|
||||
}
|
||||
.instrument(debug_span!(
|
||||
"Running compilation for the cache key",
|
||||
cache_key.platform_key = %cache_key.platform_key,
|
||||
cache_key.compiler_version = %cache_key.compiler_version,
|
||||
cache_key.metadata_file_path = %cache_key.metadata_file_path.display(),
|
||||
cache_key.solc_mode = %cache_key.solc_mode,
|
||||
))
|
||||
};
|
||||
|
||||
let compiled_contracts = match deployed_libraries {
|
||||
// If deployed libraries have been specified then we will re-compile the contract as it
|
||||
// means that linking is required in this case.
|
||||
Some(_) => {
|
||||
debug!("Deployed libraries defined, recompilation must take place");
|
||||
debug!("Cache miss");
|
||||
compilation_callback().await?.compiler_output
|
||||
}
|
||||
// If no deployed libraries are specified then we can follow the cached flow and attempt
|
||||
// to lookup the compilation artifacts in the cache.
|
||||
None => {
|
||||
debug!("Deployed libraries undefined, attempting to make use of cache");
|
||||
|
||||
// Lock this specific cache key such that we do not get inconsistent state. We want
|
||||
// that when multiple cases come in asking for the compilation artifacts then they
|
||||
// don't all trigger a compilation if there's a cache miss. Hence, the lock here.
|
||||
let read_guard = CACHE_KEY_LOCK.read().await;
|
||||
let mutex = match read_guard.get(&cache_key).cloned() {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
drop(read_guard);
|
||||
CACHE_KEY_LOCK
|
||||
.write()
|
||||
.await
|
||||
.entry(cache_key.clone())
|
||||
.or_default()
|
||||
.clone()
|
||||
}
|
||||
};
|
||||
let _guard = mutex.lock().await;
|
||||
|
||||
self.0
|
||||
.get_or_insert_with(&cache_key, compilation_callback)
|
||||
.await
|
||||
.map(|value| value.compiler_output)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok((compiled_contracts, compiler_version))
|
||||
}
|
||||
}
|
||||
|
||||
async fn compile_contracts<P: Platform>(
|
||||
metadata_directory: impl AsRef<Path>,
|
||||
compiler_path: impl AsRef<Path>,
|
||||
mut files_to_compile: impl Iterator<Item = PathBuf>,
|
||||
mode: &Mode,
|
||||
deployed_libraries: Option<&HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
) -> Result<CompilerOutput> {
|
||||
let all_sources_in_dir = FilesWithExtensionIterator::new(metadata_directory.as_ref())
|
||||
.with_allowed_extension("sol")
|
||||
.with_use_cached_fs(true)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Compiler::<P::Compiler>::new()
|
||||
.with_allow_path(metadata_directory)
|
||||
// Handling the modes
|
||||
.with_optimization(mode.optimize_setting)
|
||||
.with_pipeline(mode.pipeline)
|
||||
// Adding the contract sources to the compiler.
|
||||
.try_then(|compiler| {
|
||||
files_to_compile.try_fold(compiler, |compiler, path| compiler.with_source(path))
|
||||
})?
|
||||
// Adding the deployed libraries to the compiler.
|
||||
.then(|compiler| {
|
||||
deployed_libraries
|
||||
.iter()
|
||||
.flat_map(|value| value.iter())
|
||||
.map(|(instance, (ident, address, abi))| (instance, ident, address, abi))
|
||||
.flat_map(|(_, ident, address, _)| {
|
||||
all_sources_in_dir
|
||||
.iter()
|
||||
.map(move |path| (ident, address, path))
|
||||
})
|
||||
.fold(compiler, |compiler, (ident, address, path)| {
|
||||
compiler.with_library(path, ident.as_str(), *address)
|
||||
})
|
||||
})
|
||||
.try_build(compiler_path)
|
||||
.await
|
||||
}
|
||||
|
||||
struct ArtifactsCache {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl ArtifactsCache {
|
||||
pub fn new(path: impl AsRef<Path>) -> Self {
|
||||
Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub async fn with_invalidated_cache(self) -> Result<Self> {
|
||||
cacache::clear(self.path.as_path())
|
||||
.await
|
||||
.map_err(Into::<Error>::into)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub async fn insert(&self, key: &CacheKey, value: &CacheValue) -> Result<()> {
|
||||
let key = bson::to_vec(key)?;
|
||||
let value = bson::to_vec(value)?;
|
||||
cacache::write(self.path.as_path(), key.encode_hex(), value).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get(&self, key: &CacheKey) -> Option<CacheValue> {
|
||||
let key = bson::to_vec(key).ok()?;
|
||||
let value = cacache::read(self.path.as_path(), key.encode_hex())
|
||||
.await
|
||||
.ok()?;
|
||||
let value = bson::from_slice::<CacheValue>(&value).ok()?;
|
||||
Some(value)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub async fn get_or_insert_with(
|
||||
&self,
|
||||
key: &CacheKey,
|
||||
callback: impl AsyncFnOnce() -> Result<CacheValue>,
|
||||
) -> Result<CacheValue> {
|
||||
match self.get(key).await {
|
||||
Some(value) => {
|
||||
debug!("Cache hit");
|
||||
Ok(value)
|
||||
}
|
||||
None => {
|
||||
debug!("Cache miss");
|
||||
let value = callback().await?;
|
||||
self.insert(key, &value).await?;
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
struct CacheKey {
|
||||
/// The platform name that this artifact was compiled for. For example, this could be EVM or
|
||||
/// PVM.
|
||||
platform_key: String,
|
||||
|
||||
/// The version of the compiler that was used to compile the artifacts.
|
||||
compiler_version: Version,
|
||||
|
||||
/// The path of the metadata file that the compilation artifacts are for.
|
||||
metadata_file_path: PathBuf,
|
||||
|
||||
/// The mode that the compilation artifacts where compiled with.
|
||||
solc_mode: Mode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
struct CacheValue {
|
||||
/// The compiler output from the compilation run.
|
||||
compiler_output: CompilerOutput,
|
||||
}
|
||||
|
||||
impl CacheValue {
|
||||
pub fn new(compiler_output: CompilerOutput) -> Self {
|
||||
Self { compiler_output }
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ use revive_dt_format::input::{
|
||||
BalanceAssertion, Calldata, EtherValue, Expected, ExpectedOutput, Input, Method,
|
||||
StorageEmptyAssertion,
|
||||
};
|
||||
use revive_dt_format::metadata::{ContractInstance, ContractPathAndIdent};
|
||||
use revive_dt_format::metadata::{ContractIdent, ContractInstance, ContractPathAndIdent};
|
||||
use revive_dt_format::{input::Step, metadata::Metadata};
|
||||
use revive_dt_node::Node;
|
||||
use revive_dt_node_interaction::EthereumNode;
|
||||
@@ -44,7 +44,7 @@ pub struct CaseState<T: Platform> {
|
||||
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
||||
|
||||
/// This map stores the contracts deployments for this case.
|
||||
deployed_contracts: HashMap<ContractInstance, (Address, JsonAbi)>,
|
||||
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
||||
|
||||
/// This map stores the variables used for each one of the cases contained in the metadata
|
||||
/// file.
|
||||
@@ -63,7 +63,7 @@ where
|
||||
pub fn new(
|
||||
compiler_version: Version,
|
||||
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
||||
deployed_contracts: HashMap<ContractInstance, (Address, JsonAbi)>,
|
||||
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
compiled_contracts,
|
||||
@@ -155,17 +155,10 @@ where
|
||||
async fn handle_input_contract_deployment(
|
||||
&mut self,
|
||||
metadata: &Metadata,
|
||||
case_idx: CaseIdx,
|
||||
_: CaseIdx,
|
||||
input: &Input,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<HashMap<ContractInstance, TransactionReceipt>> {
|
||||
let span = tracing::debug_span!(
|
||||
"Handling contract deployment",
|
||||
?case_idx,
|
||||
instance = ?input.instance
|
||||
);
|
||||
let _guard = span.enter();
|
||||
|
||||
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
|
||||
for instance in input.find_all_contract_instances().into_iter() {
|
||||
if !self.deployed_contracts.contains_key(&instance) {
|
||||
@@ -316,9 +309,6 @@ where
|
||||
resolver: &impl ResolverApi,
|
||||
tracing_result: &CallFrame,
|
||||
) -> anyhow::Result<()> {
|
||||
let span = tracing::info_span!("Handling input expectations");
|
||||
let _guard = span.enter();
|
||||
|
||||
// Resolving the `input.expected` into a series of expectations that we can then assert on.
|
||||
let mut expectations = match input {
|
||||
Input {
|
||||
@@ -508,9 +498,6 @@ where
|
||||
execution_receipt: TransactionReceipt,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
||||
let span = tracing::info_span!("Handling input diff");
|
||||
let _guard = span.enter();
|
||||
|
||||
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
|
||||
diff_mode: Some(true),
|
||||
disable_code: None,
|
||||
@@ -662,7 +649,7 @@ where
|
||||
value: Option<EtherValue>,
|
||||
node: &T::Blockchain,
|
||||
) -> anyhow::Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
|
||||
if let Some((address, abi)) = self.deployed_contracts.get(contract_instance) {
|
||||
if let Some((_, address, abi)) = self.deployed_contracts.get(contract_instance) {
|
||||
return Ok((*address, abi.clone(), None));
|
||||
}
|
||||
|
||||
@@ -746,8 +733,10 @@ where
|
||||
"Deployed contract"
|
||||
);
|
||||
|
||||
self.deployed_contracts
|
||||
.insert(contract_instance.clone(), (address, abi.clone()));
|
||||
self.deployed_contracts.insert(
|
||||
contract_instance.clone(),
|
||||
(contract_ident, address, abi.clone()),
|
||||
);
|
||||
|
||||
Ok((address, abi, Some(receipt)))
|
||||
}
|
||||
|
||||
+160
-313
@@ -1,3 +1,5 @@
|
||||
mod cached_compiler;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
@@ -6,24 +8,21 @@ use std::{
|
||||
};
|
||||
|
||||
use alloy::{
|
||||
json_abi::JsonAbi,
|
||||
network::{Ethereum, TransactionBuilder},
|
||||
primitives::Address,
|
||||
rpc::types::TransactionRequest,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use clap::Parser;
|
||||
use futures::StreamExt;
|
||||
use revive_dt_common::iterators::FilesWithExtensionIterator;
|
||||
use futures::stream::futures_unordered::FuturesUnordered;
|
||||
use futures::{Stream, StreamExt};
|
||||
use revive_dt_node_interaction::EthereumNode;
|
||||
use semver::Version;
|
||||
use temp_dir::TempDir;
|
||||
use tokio::sync::{Mutex, RwLock, mpsc};
|
||||
use tracing::{Instrument, Level, instrument};
|
||||
use tokio::sync::mpsc;
|
||||
use tracing::{Instrument, Level};
|
||||
use tracing_subscriber::{EnvFilter, FmtSubscriber};
|
||||
|
||||
use revive_dt_common::types::Mode;
|
||||
use revive_dt_compiler::SolidityCompiler;
|
||||
use revive_dt_compiler::{Compiler, CompilerOutput};
|
||||
use revive_dt_config::*;
|
||||
use revive_dt_core::{
|
||||
Geth, Kitchensink, Platform,
|
||||
@@ -33,30 +32,22 @@ use revive_dt_format::{
|
||||
case::{Case, CaseIdx},
|
||||
corpus::Corpus,
|
||||
input::{Input, Step},
|
||||
metadata::{ContractInstance, ContractPathAndIdent, Metadata, MetadataFile},
|
||||
mode::{Mode, SolcMode},
|
||||
metadata::{ContractPathAndIdent, Metadata, MetadataFile},
|
||||
};
|
||||
use revive_dt_node::pool::NodePool;
|
||||
use revive_dt_report::reporter::{Report, Span};
|
||||
|
||||
static TEMP_DIR: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
|
||||
use crate::cached_compiler::CachedCompiler;
|
||||
|
||||
type CompilationCache = Arc<
|
||||
RwLock<
|
||||
HashMap<
|
||||
(PathBuf, SolcMode, TestingPlatform),
|
||||
Arc<Mutex<Option<Arc<(Version, CompilerOutput)>>>>,
|
||||
>,
|
||||
>,
|
||||
>;
|
||||
static TEMP_DIR: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
|
||||
|
||||
/// this represents a single "test"; a mode, path and collection of cases.
|
||||
#[derive(Clone)]
|
||||
struct Test {
|
||||
metadata: Metadata,
|
||||
path: PathBuf,
|
||||
mode: SolcMode,
|
||||
case_idx: usize,
|
||||
mode: Mode,
|
||||
case_idx: CaseIdx,
|
||||
case: Case,
|
||||
}
|
||||
|
||||
@@ -144,8 +135,8 @@ where
|
||||
{
|
||||
let (report_tx, report_rx) = mpsc::unbounded_channel::<(Test, CaseResult)>();
|
||||
|
||||
let tests = prepare_tests::<L, F>(metadata_files);
|
||||
let driver_task = start_driver_task::<L, F>(args, tests, span, report_tx)?;
|
||||
let tests = prepare_tests::<L, F>(args, metadata_files);
|
||||
let driver_task = start_driver_task::<L, F>(args, tests, span, report_tx).await?;
|
||||
let status_reporter_task = start_reporter_task(report_rx);
|
||||
|
||||
tokio::join!(status_reporter_task, driver_task);
|
||||
@@ -153,7 +144,10 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_tests<L, F>(metadata_files: &[MetadataFile]) -> impl Iterator<Item = Test>
|
||||
fn prepare_tests<L, F>(
|
||||
args: &Arguments,
|
||||
metadata_files: &[MetadataFile],
|
||||
) -> impl Stream<Item = Test>
|
||||
where
|
||||
L: Platform,
|
||||
F: Platform,
|
||||
@@ -172,7 +166,8 @@ where
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(move |(case_idx, case)| {
|
||||
SolcMode::ALL
|
||||
metadata
|
||||
.solc_modes()
|
||||
.into_iter()
|
||||
.map(move |solc_mode| (path, metadata, case_idx, case, solc_mode))
|
||||
})
|
||||
@@ -225,41 +220,57 @@ where
|
||||
}
|
||||
None => true,
|
||||
})
|
||||
.flat_map(|(path, metadata, case_idx, case, solc_mode)| {
|
||||
if let Some(ref case_modes) = case.modes {
|
||||
case_modes
|
||||
.iter()
|
||||
.filter_map(Mode::as_solc_mode)
|
||||
.filter(|case_mode| case_mode.matches(&solc_mode))
|
||||
.map(|case_mode| (path, metadata, case_idx, case, case_mode.clone()))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
} else if let Some(ref metadata_modes) = metadata.modes {
|
||||
metadata_modes
|
||||
.iter()
|
||||
.filter_map(Mode::as_solc_mode)
|
||||
.filter(|metadata_mode| metadata_mode.matches(&solc_mode))
|
||||
.map(|metadata_mode| (path, metadata, case_idx, case, metadata_mode.clone()))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
} else {
|
||||
vec![(path, metadata, case_idx, case, solc_mode)].into_iter()
|
||||
}
|
||||
})
|
||||
.map(|(metadata_file_path, metadata, case_idx, case, solc_mode)| {
|
||||
Test {
|
||||
metadata: metadata.clone(),
|
||||
path: metadata_file_path.to_path_buf(),
|
||||
mode: solc_mode,
|
||||
case_idx,
|
||||
case_idx: case_idx.into(),
|
||||
case: case.clone(),
|
||||
}
|
||||
})
|
||||
.map(async |test| test)
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.filter_map(async move |test| {
|
||||
// Check that both compilers support this test, else we skip it
|
||||
let is_supported = does_compiler_support_mode::<L>(args, &test.mode).await.ok().unwrap_or(false) &&
|
||||
does_compiler_support_mode::<F>(args, &test.mode).await.ok().unwrap_or(false);
|
||||
|
||||
// We filter_map to avoid needing to clone `test`, but return it as-is.
|
||||
if is_supported {
|
||||
Some(test)
|
||||
} else {
|
||||
tracing::warn!(
|
||||
metadata_file_path = %test.path.display(),
|
||||
case_idx = %test.case_idx,
|
||||
case_name = ?test.case.name,
|
||||
mode = %test.mode,
|
||||
"Skipping test as one or both of the compilers don't support it"
|
||||
);
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn start_driver_task<L, F>(
|
||||
async fn does_compiler_support_mode<P: Platform>(
|
||||
args: &Arguments,
|
||||
tests: impl Iterator<Item = Test>,
|
||||
mode: &Mode,
|
||||
) -> anyhow::Result<bool> {
|
||||
let compiler_version_or_requirement = mode.compiler_version_to_use(args.solc.clone());
|
||||
let compiler_path =
|
||||
P::Compiler::get_compiler_executable(args, compiler_version_or_requirement).await?;
|
||||
let compiler_version = P::Compiler::new(compiler_path.clone()).version()?;
|
||||
|
||||
Ok(P::Compiler::supports_mode(
|
||||
&compiler_version,
|
||||
mode.optimize_setting,
|
||||
mode.pipeline,
|
||||
))
|
||||
}
|
||||
|
||||
async fn start_driver_task<L, F>(
|
||||
args: &Arguments,
|
||||
tests: impl Stream<Item = Test>,
|
||||
span: Span,
|
||||
report_tx: mpsc::UnboundedSender<(Test, CaseResult)>,
|
||||
) -> anyhow::Result<impl Future<Output = ()>>
|
||||
@@ -271,10 +282,16 @@ where
|
||||
{
|
||||
let leader_nodes = Arc::new(NodePool::<L::Blockchain>::new(args)?);
|
||||
let follower_nodes = Arc::new(NodePool::<F::Blockchain>::new(args)?);
|
||||
let compilation_cache = Arc::new(RwLock::new(HashMap::new()));
|
||||
let number_concurrent_tasks = args.number_of_concurrent_tasks();
|
||||
let cached_compiler = Arc::new(
|
||||
CachedCompiler::new(
|
||||
args.directory().join("compilation_cache"),
|
||||
args.invalidate_compilation_cache,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
|
||||
Ok(futures::stream::iter(tests).for_each_concurrent(
|
||||
Ok(tests.for_each_concurrent(
|
||||
// We want to limit the concurrent tasks here because:
|
||||
//
|
||||
// 1. We don't want to overwhelm the nodes with too many requests, leading to responses timing out.
|
||||
@@ -286,8 +303,8 @@ where
|
||||
move |test| {
|
||||
let leader_nodes = leader_nodes.clone();
|
||||
let follower_nodes = follower_nodes.clone();
|
||||
let compilation_cache = compilation_cache.clone();
|
||||
let report_tx = report_tx.clone();
|
||||
let cached_compiler = cached_compiler.clone();
|
||||
|
||||
async move {
|
||||
let leader_node = leader_nodes.round_robbin();
|
||||
@@ -298,17 +315,17 @@ where
|
||||
"Running driver",
|
||||
metadata_file_path = %test.path.display(),
|
||||
case_idx = ?test.case_idx,
|
||||
solc_mode = %test.mode,
|
||||
solc_mode = ?test.mode,
|
||||
);
|
||||
|
||||
let result = handle_case_driver::<L, F>(
|
||||
&test.path,
|
||||
&test.metadata,
|
||||
test.case_idx.into(),
|
||||
test.case_idx,
|
||||
&test.case,
|
||||
test.mode.clone(),
|
||||
args,
|
||||
compilation_cache.clone(),
|
||||
cached_compiler,
|
||||
leader_node,
|
||||
follower_node,
|
||||
span,
|
||||
@@ -329,7 +346,7 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseR
|
||||
|
||||
const GREEN: &str = "\x1B[32m";
|
||||
const RED: &str = "\x1B[31m";
|
||||
const COLOR_RESET: &str = "\x1B[0m";
|
||||
const COLOUR_RESET: &str = "\x1B[0m";
|
||||
const BOLD: &str = "\x1B[1m";
|
||||
const BOLD_RESET: &str = "\x1B[22m";
|
||||
|
||||
@@ -348,13 +365,13 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseR
|
||||
Ok(_inputs) => {
|
||||
number_of_successes += 1;
|
||||
eprintln!(
|
||||
"{GREEN}Case Succeeded:{COLOR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})"
|
||||
"{GREEN}Case Succeeded:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})"
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
number_of_failures += 1;
|
||||
eprintln!(
|
||||
"{RED}Case Failed:{COLOR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})"
|
||||
"{RED}Case Failed:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode})"
|
||||
);
|
||||
failures.push((test, err));
|
||||
}
|
||||
@@ -377,14 +394,14 @@ async fn start_reporter_task(mut report_rx: mpsc::UnboundedReceiver<(Test, CaseR
|
||||
let test_mode = test.mode.clone();
|
||||
|
||||
eprintln!(
|
||||
"---- {RED}Case Failed:{COLOR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode}) ----\n\n{err}\n"
|
||||
"---- {RED}Case Failed:{COLOUR_RESET} {test_path} -> {case_name}:{case_idx} (mode: {test_mode}) ----\n\n{err}\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Summary at the end.
|
||||
eprintln!(
|
||||
"{} cases: {GREEN}{number_of_successes}{COLOR_RESET} cases succeeded, {RED}{number_of_failures}{COLOR_RESET} cases failed in {} seconds",
|
||||
"{} cases: {GREEN}{number_of_successes}{COLOUR_RESET} cases succeeded, {RED}{number_of_failures}{COLOUR_RESET} cases failed in {} seconds",
|
||||
number_of_successes + number_of_failures,
|
||||
elapsed.as_secs()
|
||||
);
|
||||
@@ -396,9 +413,9 @@ async fn handle_case_driver<L, F>(
|
||||
metadata: &Metadata,
|
||||
case_idx: CaseIdx,
|
||||
case: &Case,
|
||||
mode: SolcMode,
|
||||
mode: Mode,
|
||||
config: &Arguments,
|
||||
compilation_cache: CompilationCache,
|
||||
cached_compiler: Arc<CachedCompiler>,
|
||||
leader_node: &L::Blockchain,
|
||||
follower_node: &F::Blockchain,
|
||||
_: Span,
|
||||
@@ -409,27 +426,19 @@ where
|
||||
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
|
||||
{
|
||||
let leader_pre_link_contracts = get_or_build_contracts::<L>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
config,
|
||||
compilation_cache.clone(),
|
||||
&HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
let follower_pre_link_contracts = get_or_build_contracts::<F>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
config,
|
||||
compilation_cache.clone(),
|
||||
&HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
let leader_pre_link_contracts = cached_compiler
|
||||
.compile_contracts::<L>(metadata, metadata_file_path, &mode, config, None)
|
||||
.await?
|
||||
.0
|
||||
.contracts;
|
||||
let follower_pre_link_contracts = cached_compiler
|
||||
.compile_contracts::<F>(metadata, metadata_file_path, &mode, config, None)
|
||||
.await?
|
||||
.0
|
||||
.contracts;
|
||||
|
||||
let mut leader_deployed_libraries = HashMap::new();
|
||||
let mut follower_deployed_libraries = HashMap::new();
|
||||
let mut leader_deployed_libraries = None::<HashMap<_, _>>;
|
||||
let mut follower_deployed_libraries = None::<HashMap<_, _>>;
|
||||
let mut contract_sources = metadata.contract_sources()?;
|
||||
for library_instance in metadata
|
||||
.libraries
|
||||
@@ -445,14 +454,10 @@ where
|
||||
.context("Failed to find the contract source")?;
|
||||
|
||||
let (leader_code, leader_abi) = leader_pre_link_contracts
|
||||
.1
|
||||
.contracts
|
||||
.get(&library_source_path)
|
||||
.and_then(|contracts| contracts.get(library_ident.as_str()))
|
||||
.context("Declared library was not compiled")?;
|
||||
let (follower_code, follower_abi) = follower_pre_link_contracts
|
||||
.1
|
||||
.contracts
|
||||
.get(&library_source_path)
|
||||
.and_then(|contracts| contracts.get(library_ident.as_str()))
|
||||
.context("Declared library was not compiled")?;
|
||||
@@ -545,81 +550,52 @@ where
|
||||
anyhow::bail!("Contract deployment didn't return an address");
|
||||
};
|
||||
|
||||
leader_deployed_libraries.insert(
|
||||
leader_deployed_libraries.get_or_insert_default().insert(
|
||||
library_instance.clone(),
|
||||
(leader_library_address, leader_abi.clone()),
|
||||
(
|
||||
library_ident.clone(),
|
||||
leader_library_address,
|
||||
leader_abi.clone(),
|
||||
),
|
||||
);
|
||||
follower_deployed_libraries.insert(
|
||||
follower_deployed_libraries.get_or_insert_default().insert(
|
||||
library_instance.clone(),
|
||||
(follower_library_address, follower_abi.clone()),
|
||||
(
|
||||
library_ident,
|
||||
follower_library_address,
|
||||
follower_abi.clone(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let metadata_file_contains_libraries = metadata
|
||||
.libraries
|
||||
.iter()
|
||||
.flat_map(|map| map.iter())
|
||||
.flat_map(|(_, value)| value.iter())
|
||||
.next()
|
||||
.is_some();
|
||||
let compiled_contracts_require_linking = leader_pre_link_contracts
|
||||
.1
|
||||
.contracts
|
||||
.values()
|
||||
.chain(follower_pre_link_contracts.1.contracts.values())
|
||||
.flat_map(|value| value.values())
|
||||
.any(|(code, _)| !code.chars().all(|char| char.is_ascii_hexdigit()));
|
||||
let (leader_compiled_contracts, follower_compiled_contracts) =
|
||||
if metadata_file_contains_libraries && compiled_contracts_require_linking {
|
||||
let leader_key = (
|
||||
metadata_file_path.to_path_buf(),
|
||||
mode.clone(),
|
||||
L::config_id(),
|
||||
);
|
||||
let follower_key = (
|
||||
metadata_file_path.to_path_buf(),
|
||||
mode.clone(),
|
||||
F::config_id(),
|
||||
);
|
||||
{
|
||||
let mut cache = compilation_cache.write().await;
|
||||
cache.remove(&leader_key);
|
||||
cache.remove(&follower_key);
|
||||
}
|
||||
|
||||
let leader_post_link_contracts = get_or_build_contracts::<L>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
config,
|
||||
compilation_cache.clone(),
|
||||
&leader_deployed_libraries,
|
||||
)
|
||||
.await?;
|
||||
let follower_post_link_contracts = get_or_build_contracts::<F>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
mode.clone(),
|
||||
config,
|
||||
compilation_cache,
|
||||
&follower_deployed_libraries,
|
||||
)
|
||||
.await?;
|
||||
|
||||
(leader_post_link_contracts, follower_post_link_contracts)
|
||||
} else {
|
||||
(leader_pre_link_contracts, follower_pre_link_contracts)
|
||||
};
|
||||
let (leader_post_link_contracts, leader_compiler_version) = cached_compiler
|
||||
.compile_contracts::<L>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
leader_deployed_libraries.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
let (follower_post_link_contracts, follower_compiler_version) = cached_compiler
|
||||
.compile_contracts::<F>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
follower_deployed_libraries.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let leader_state = CaseState::<L>::new(
|
||||
leader_compiled_contracts.0.clone(),
|
||||
leader_compiled_contracts.1.contracts.clone(),
|
||||
leader_deployed_libraries,
|
||||
leader_compiler_version,
|
||||
leader_post_link_contracts.contracts,
|
||||
leader_deployed_libraries.unwrap_or_default(),
|
||||
);
|
||||
let follower_state = CaseState::<F>::new(
|
||||
follower_compiled_contracts.0.clone(),
|
||||
follower_compiled_contracts.1.contracts.clone(),
|
||||
follower_deployed_libraries,
|
||||
follower_compiler_version,
|
||||
follower_post_link_contracts.contracts,
|
||||
follower_deployed_libraries.unwrap_or_default(),
|
||||
);
|
||||
|
||||
let mut driver = CaseDriver::<L, F>::new(
|
||||
@@ -634,147 +610,6 @@ where
|
||||
driver.execute().await
|
||||
}
|
||||
|
||||
async fn get_or_build_contracts<P: Platform>(
|
||||
metadata: &Metadata,
|
||||
metadata_file_path: &Path,
|
||||
mode: SolcMode,
|
||||
config: &Arguments,
|
||||
compilation_cache: CompilationCache,
|
||||
deployed_libraries: &HashMap<ContractInstance, (Address, JsonAbi)>,
|
||||
) -> anyhow::Result<Arc<(Version, CompilerOutput)>> {
|
||||
let key = (
|
||||
metadata_file_path.to_path_buf(),
|
||||
mode.clone(),
|
||||
P::config_id(),
|
||||
);
|
||||
if let Some(compilation_artifact) = compilation_cache.read().await.get(&key).cloned() {
|
||||
let mut compilation_artifact = compilation_artifact.lock().await;
|
||||
match *compilation_artifact {
|
||||
Some(ref compiled_contracts) => {
|
||||
tracing::debug!(?key, "Compiled contracts cache hit");
|
||||
return Ok(compiled_contracts.clone());
|
||||
}
|
||||
None => {
|
||||
tracing::debug!(?key, "Compiled contracts cache miss");
|
||||
let compiled_contracts = Arc::new(
|
||||
compile_contracts::<P>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
deployed_libraries,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
*compilation_artifact = Some(compiled_contracts.clone());
|
||||
return Ok(compiled_contracts.clone());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tracing::debug!(?key, "Compiled contracts cache miss");
|
||||
let mutex = {
|
||||
let mut compilation_cache = compilation_cache.write().await;
|
||||
let mutex = Arc::new(Mutex::new(None));
|
||||
compilation_cache.insert(key, mutex.clone());
|
||||
mutex
|
||||
};
|
||||
let mut compilation_artifact = mutex.lock().await;
|
||||
let compiled_contracts = Arc::new(
|
||||
compile_contracts::<P>(
|
||||
metadata,
|
||||
metadata_file_path,
|
||||
&mode,
|
||||
config,
|
||||
deployed_libraries,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
*compilation_artifact = Some(compiled_contracts.clone());
|
||||
Ok(compiled_contracts.clone())
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
level = "info",
|
||||
skip_all,
|
||||
fields(
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
mode = %mode,
|
||||
deployed_libraries = deployed_libraries.len(),
|
||||
),
|
||||
err
|
||||
)]
|
||||
async fn compile_contracts<P: Platform>(
|
||||
metadata: &Metadata,
|
||||
metadata_file_path: &Path,
|
||||
mode: &SolcMode,
|
||||
config: &Arguments,
|
||||
deployed_libraries: &HashMap<ContractInstance, (Address, JsonAbi)>,
|
||||
) -> anyhow::Result<(Version, CompilerOutput)> {
|
||||
let compiler_version_or_requirement = mode.compiler_version_to_use(config.solc.clone());
|
||||
let compiler_path =
|
||||
P::Compiler::get_compiler_executable(config, compiler_version_or_requirement).await?;
|
||||
let compiler_version = P::Compiler::new(compiler_path.clone())
|
||||
.version()
|
||||
.inspect_err(|err| tracing::error!(%err, "Failed to get compiler version"))?;
|
||||
|
||||
tracing::info!(
|
||||
%compiler_version,
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
mode = %mode,
|
||||
"Compiling contracts"
|
||||
);
|
||||
|
||||
let compiler = Compiler::<P::Compiler>::new()
|
||||
.with_allow_path(
|
||||
metadata
|
||||
.directory()
|
||||
.inspect_err(|err| tracing::error!(%err, "Failed to get the metadata directory"))?,
|
||||
)
|
||||
.with_optimization(mode.optimize)
|
||||
.with_via_ir(mode.via_ir);
|
||||
let mut compiler = metadata
|
||||
.files_to_compile()?
|
||||
.try_fold(compiler, |compiler, path| compiler.with_source(&path))?;
|
||||
for (library_instance, (library_address, _)) in deployed_libraries.iter() {
|
||||
let library_ident = &metadata
|
||||
.contracts
|
||||
.as_ref()
|
||||
.and_then(|contracts| contracts.get(library_instance))
|
||||
.expect("Impossible for library to not be found in contracts")
|
||||
.contract_ident;
|
||||
|
||||
// Note the following: we need to tell solc which files require the libraries to be linked
|
||||
// into them. We do not have access to this information and therefore we choose an easier,
|
||||
// yet more compute intensive route, of telling solc that all of the files need to link the
|
||||
// library and it will only perform the linking for the files that do actually need the
|
||||
// library.
|
||||
compiler =
|
||||
FilesWithExtensionIterator::new(metadata.directory().inspect_err(
|
||||
|err| tracing::error!(%err, "Failed to get the metadata directory"),
|
||||
)?)
|
||||
.with_allowed_extension("sol")
|
||||
.with_use_cached_fs(true)
|
||||
.fold(compiler, |compiler, path| {
|
||||
compiler.with_library(&path, library_ident.as_str(), *library_address)
|
||||
});
|
||||
}
|
||||
|
||||
let compiler_output = compiler
|
||||
.try_build(compiler_path)
|
||||
.await
|
||||
.inspect_err(|err| tracing::error!(%err, "Contract compilation failed"))?;
|
||||
|
||||
tracing::info!(
|
||||
%compiler_version,
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
mode = %mode,
|
||||
"Compiled contracts"
|
||||
);
|
||||
|
||||
Ok((compiler_version, compiler_output))
|
||||
}
|
||||
|
||||
async fn execute_corpus(
|
||||
args: &Arguments,
|
||||
tests: &[MetadataFile],
|
||||
@@ -806,28 +641,40 @@ async fn compile_corpus(
|
||||
.map(move |solc_mode| (metadata, solc_mode))
|
||||
});
|
||||
|
||||
let file = tempfile::NamedTempFile::new().expect("Failed to create temp file");
|
||||
let cached_compiler = CachedCompiler::new(file.path(), false)
|
||||
.await
|
||||
.map(Arc::new)
|
||||
.expect("Failed to create the cached compiler");
|
||||
|
||||
futures::stream::iter(tests)
|
||||
.for_each_concurrent(None, |(metadata, mode)| async move {
|
||||
match platform {
|
||||
TestingPlatform::Geth => {
|
||||
let _ = compile_contracts::<Geth>(
|
||||
&metadata.content,
|
||||
&metadata.path,
|
||||
&mode,
|
||||
config,
|
||||
&Default::default(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
TestingPlatform::Kitchensink => {
|
||||
let _ = compile_contracts::<Geth>(
|
||||
&metadata.content,
|
||||
&metadata.path,
|
||||
&mode,
|
||||
config,
|
||||
&Default::default(),
|
||||
)
|
||||
.await;
|
||||
.for_each_concurrent(None, |(metadata, mode)| {
|
||||
let cached_compiler = cached_compiler.clone();
|
||||
|
||||
async move {
|
||||
match platform {
|
||||
TestingPlatform::Geth => {
|
||||
let _ = cached_compiler
|
||||
.compile_contracts::<Geth>(
|
||||
metadata,
|
||||
metadata.path.as_path(),
|
||||
&mode,
|
||||
config,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
TestingPlatform::Kitchensink => {
|
||||
let _ = cached_compiler
|
||||
.compile_contracts::<Kitchensink>(
|
||||
metadata,
|
||||
metadata.path.as_path(),
|
||||
&mode,
|
||||
config,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,6 +17,7 @@ alloy = { workspace = true }
|
||||
alloy-primitives = { workspace = true }
|
||||
alloy-sol-types = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
semver = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
+10
-11
@@ -1,13 +1,10 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use revive_dt_common::macros::define_wrapper_type;
|
||||
|
||||
use crate::{
|
||||
input::{Expected, Step},
|
||||
metadata::{deserialize_compilation_modes, serialize_compilation_modes},
|
||||
mode::Mode,
|
||||
mode::ParsedMode,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq)]
|
||||
@@ -18,13 +15,8 @@ pub struct Case {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub comment: Option<String>,
|
||||
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "deserialize_compilation_modes",
|
||||
serialize_with = "serialize_compilation_modes"
|
||||
)]
|
||||
pub modes: Option<HashSet<Mode>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub modes: Option<Vec<ParsedMode>>,
|
||||
|
||||
#[serde(rename = "inputs")]
|
||||
pub steps: Vec<Step>,
|
||||
@@ -40,6 +32,7 @@ pub struct Case {
|
||||
}
|
||||
|
||||
impl Case {
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
pub fn steps_iterator(&self) -> impl Iterator<Item = Step> {
|
||||
let steps_len = self.steps.len();
|
||||
self.steps
|
||||
@@ -74,3 +67,9 @@ define_wrapper_type!(
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct CaseIdx(usize);
|
||||
);
|
||||
|
||||
impl std::fmt::Display for CaseIdx {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -739,12 +739,14 @@ impl<'de> Deserialize<'de> for EtherValue {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use alloy::{eips::BlockNumberOrTag, json_abi::JsonAbi};
|
||||
use alloy_primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, address};
|
||||
use alloy_sol_types::SolValue;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
use crate::metadata::ContractIdent;
|
||||
|
||||
struct MockResolver;
|
||||
|
||||
impl ResolverApi for MockResolver {
|
||||
@@ -818,11 +820,11 @@ mod tests {
|
||||
let mut contracts = HashMap::new();
|
||||
contracts.insert(
|
||||
ContractInstance::new("Contract"),
|
||||
(Address::ZERO, parsed_abi),
|
||||
(ContractIdent::new("Contract"), Address::ZERO, parsed_abi),
|
||||
);
|
||||
|
||||
let resolver = MockResolver;
|
||||
let context = ResolutionContext::new_from_parts(&contracts, None, None, None);
|
||||
let context = ResolutionContext::default().with_deployed_contracts(&contracts);
|
||||
let encoded = input.encoded_input(&resolver, context).await.unwrap();
|
||||
assert!(encoded.0.starts_with(&selector));
|
||||
|
||||
@@ -862,11 +864,11 @@ mod tests {
|
||||
let mut contracts = HashMap::new();
|
||||
contracts.insert(
|
||||
ContractInstance::new("Contract"),
|
||||
(Address::ZERO, parsed_abi),
|
||||
(ContractIdent::new("Contract"), Address::ZERO, parsed_abi),
|
||||
);
|
||||
|
||||
let resolver = MockResolver;
|
||||
let context = ResolutionContext::new_from_parts(&contracts, None, None, None);
|
||||
let context = ResolutionContext::default().with_deployed_contracts(&contracts);
|
||||
let encoded = input.encoded_input(&resolver, context).await.unwrap();
|
||||
assert!(encoded.0.starts_with(&selector));
|
||||
|
||||
@@ -909,11 +911,11 @@ mod tests {
|
||||
let mut contracts = HashMap::new();
|
||||
contracts.insert(
|
||||
ContractInstance::new("Contract"),
|
||||
(Address::ZERO, parsed_abi),
|
||||
(ContractIdent::new("Contract"), Address::ZERO, parsed_abi),
|
||||
);
|
||||
|
||||
let resolver = MockResolver;
|
||||
let context = ResolutionContext::new_from_parts(&contracts, None, None, None);
|
||||
let context = ResolutionContext::default().with_deployed_contracts(&contracts);
|
||||
let encoded = input.encoded_input(&resolver, context).await.unwrap();
|
||||
assert!(encoded.0.starts_with(&selector));
|
||||
|
||||
@@ -927,10 +929,10 @@ mod tests {
|
||||
|
||||
async fn resolve_calldata_item(
|
||||
input: &str,
|
||||
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
|
||||
deployed_contracts: &HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
||||
resolver: &impl ResolverApi,
|
||||
) -> anyhow::Result<U256> {
|
||||
let context = ResolutionContext::new_from_parts(deployed_contracts, None, None, None);
|
||||
let context = ResolutionContext::default().with_deployed_contracts(deployed_contracts);
|
||||
CalldataItem::new(input).resolve(resolver, context).await
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, HashSet},
|
||||
collections::BTreeMap,
|
||||
fmt::Display,
|
||||
fs::File,
|
||||
ops::Deref,
|
||||
@@ -8,17 +8,15 @@ use std::{
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use revive_common::EVMVersion;
|
||||
use revive_dt_common::{
|
||||
cached_fs::read_to_string, iterators::FilesWithExtensionIterator, macros::define_wrapper_type,
|
||||
types::Mode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
case::Case,
|
||||
mode::{Mode, SolcMode},
|
||||
};
|
||||
use crate::{case::Case, mode::ParsedMode};
|
||||
|
||||
pub const METADATA_FILE_EXTENSION: &str = "json";
|
||||
pub const SOLIDITY_CASE_FILE_EXTENSION: &str = "sol";
|
||||
@@ -67,13 +65,8 @@ pub struct Metadata {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub libraries: Option<BTreeMap<PathBuf, BTreeMap<ContractIdent, ContractInstance>>>,
|
||||
|
||||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "deserialize_compilation_modes",
|
||||
serialize_with = "serialize_compilation_modes"
|
||||
)]
|
||||
pub modes: Option<HashSet<Mode>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub modes: Option<Vec<ParsedMode>>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub file_path: Option<PathBuf>,
|
||||
@@ -91,21 +84,12 @@ pub struct Metadata {
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
/// Returns the solc modes of this metadata, inserting a default mode if not present.
|
||||
pub fn solc_modes(&self) -> Vec<SolcMode> {
|
||||
self.modes
|
||||
.to_owned()
|
||||
.unwrap_or_else(|| SolcMode::ALL.map(Mode::Solidity).iter().cloned().collect())
|
||||
.iter()
|
||||
.filter_map(|mode| match mode {
|
||||
Mode::Solidity(solc_mode) => Some(solc_mode),
|
||||
Mode::Unknown(mode) => {
|
||||
tracing::debug!("compiler: ignoring unknown mode '{mode}'");
|
||||
None
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
/// Returns the modes that we should test from this metadata.
|
||||
pub fn solc_modes(&self) -> Vec<Mode> {
|
||||
match &self.modes {
|
||||
Some(modes) => ParsedMode::many_to_modes(modes.iter()).collect(),
|
||||
None => Mode::all().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base directory of this metadata.
|
||||
@@ -274,37 +258,6 @@ impl Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_compilation_modes<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<HashSet<Mode>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let maybe_strings = Option::<Vec<String>>::deserialize(deserializer)?;
|
||||
Ok(maybe_strings.map(|strings| {
|
||||
strings
|
||||
.into_iter()
|
||||
.flat_map(Mode::parse_from_string)
|
||||
.collect()
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize_compilation_modes<S>(
|
||||
value: &Option<HashSet<Mode>>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match value {
|
||||
None => serializer.serialize_none(),
|
||||
Some(modes) => {
|
||||
let strings: Vec<String> = modes.iter().cloned().map(Into::<String>::into).collect();
|
||||
serializer.serialize_some(&strings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_wrapper_type!(
|
||||
/// Represents a contract instance found a metadata file.
|
||||
///
|
||||
|
||||
+221
-352
@@ -1,392 +1,261 @@
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
use regex::Regex;
|
||||
use revive_dt_common::types::{Mode, ModeOptimizerSetting, ModePipeline};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use revive_dt_common::types::VersionOrRequirement;
|
||||
use semver::Version;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Specifies a compilation mode for the test artifact that it requires. This is used as a filter
|
||||
/// when used in the [`Metadata`] and is used as a directive when used in the core crate.
|
||||
/// This represents a mode that has been parsed from test metadata.
|
||||
///
|
||||
/// [`Metadata`]: crate::metadata::Metadata
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Mode {
|
||||
/// A compilation mode that's been parsed from a String and into its contents.
|
||||
Solidity(SolcMode),
|
||||
/// An unknown compilation mode.
|
||||
Unknown(String),
|
||||
/// Mode strings can take the following form (in pseudo-regex):
|
||||
///
|
||||
/// ```text
|
||||
/// [YEILV][+-]? (M[0123sz])? <semver>?
|
||||
/// ```
|
||||
///
|
||||
/// We can parse valid mode strings into [`ParsedMode`] using [`ParsedMode::from_str`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
#[serde(try_from = "String", into = "String")]
|
||||
pub struct ParsedMode {
|
||||
pub pipeline: Option<ModePipeline>,
|
||||
pub optimize_flag: Option<bool>,
|
||||
pub optimize_setting: Option<ModeOptimizerSetting>,
|
||||
pub version: Option<semver::VersionReq>,
|
||||
}
|
||||
|
||||
impl From<Mode> for String {
|
||||
fn from(value: Mode) -> Self {
|
||||
value.to_string()
|
||||
impl FromStr for ParsedMode {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"(?x)
|
||||
^
|
||||
(?:(?P<pipeline>[YEILV])(?P<optimize_flag>[+-])?)? # Pipeline to use eg Y, E+, E-
|
||||
\s*
|
||||
(?P<optimize_setting>M[a-zA-Z0-9])? # Optimize setting eg M0, Ms, Mz
|
||||
\s*
|
||||
(?P<version>[>=<]*\d+(?:\.\d+)*)? # Optional semver version eg >=0.8.0, 0.7, <0.8
|
||||
$
|
||||
").unwrap()
|
||||
});
|
||||
|
||||
let Some(caps) = REGEX.captures(s) else {
|
||||
anyhow::bail!("Cannot parse mode '{s}' from string");
|
||||
};
|
||||
|
||||
let pipeline = match caps.name("pipeline") {
|
||||
Some(m) => Some(ModePipeline::from_str(m.as_str())?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let optimize_flag = caps.name("optimize_flag").map(|m| m.as_str() == "+");
|
||||
|
||||
let optimize_setting = match caps.name("optimize_setting") {
|
||||
Some(m) => Some(ModeOptimizerSetting::from_str(m.as_str())?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let version = match caps.name("version") {
|
||||
Some(m) => Some(semver::VersionReq::parse(m.as_str()).map_err(|e| {
|
||||
anyhow::anyhow!("Cannot parse the version requirement '{}': {e}", m.as_str())
|
||||
})?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(ParsedMode {
|
||||
pipeline,
|
||||
optimize_flag,
|
||||
optimize_setting,
|
||||
version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Mode {
|
||||
impl Display for ParsedMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Mode::Solidity(mode) => mode.fmt(f),
|
||||
Mode::Unknown(string) => string.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut has_written = false;
|
||||
|
||||
impl Mode {
|
||||
pub fn parse_from_string(str: impl AsRef<str>) -> Vec<Self> {
|
||||
let mut chars = str.as_ref().chars().peekable();
|
||||
|
||||
let compile_via_ir = match chars.next() {
|
||||
Some('Y') => true,
|
||||
Some('E') => false,
|
||||
_ => {
|
||||
tracing::warn!("Encountered an unknown mode {}", str.as_ref());
|
||||
return vec![Self::Unknown(str.as_ref().to_string())];
|
||||
if let Some(pipeline) = self.pipeline {
|
||||
pipeline.fmt(f)?;
|
||||
if let Some(optimize_flag) = self.optimize_flag {
|
||||
f.write_str(if optimize_flag { "+" } else { "-" })?;
|
||||
}
|
||||
};
|
||||
|
||||
let optimize_flag = match chars.peek() {
|
||||
Some('+') => {
|
||||
let _ = chars.next();
|
||||
Some(true)
|
||||
}
|
||||
Some('-') => {
|
||||
let _ = chars.next();
|
||||
Some(false)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut chars = chars.skip_while(|char| *char == ' ').peekable();
|
||||
|
||||
let version_requirement = match chars.peek() {
|
||||
Some('=' | '>' | '<' | '~' | '^' | '*' | '0'..='9') => {
|
||||
let version_requirement = chars.take_while(|char| *char != ' ').collect::<String>();
|
||||
let Ok(version_requirement) = VersionOrRequirement::from_str(&version_requirement)
|
||||
else {
|
||||
return vec![Self::Unknown(str.as_ref().to_string())];
|
||||
};
|
||||
Some(version_requirement)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match optimize_flag {
|
||||
Some(flag) => {
|
||||
vec![Self::Solidity(SolcMode {
|
||||
via_ir: compile_via_ir,
|
||||
optimize: flag,
|
||||
compiler_version_requirement: version_requirement,
|
||||
})]
|
||||
}
|
||||
None => {
|
||||
vec![
|
||||
Self::Solidity(SolcMode {
|
||||
via_ir: compile_via_ir,
|
||||
optimize: true,
|
||||
compiler_version_requirement: version_requirement.clone(),
|
||||
}),
|
||||
Self::Solidity(SolcMode {
|
||||
via_ir: compile_via_ir,
|
||||
optimize: false,
|
||||
compiler_version_requirement: version_requirement,
|
||||
}),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matches(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: self_via_ir,
|
||||
optimize: self_optimize,
|
||||
compiler_version_requirement: self_compiler_version_requirement,
|
||||
}),
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: other_via_ir,
|
||||
optimize: other_optimize,
|
||||
compiler_version_requirement: other_compiler_version_requirement,
|
||||
}),
|
||||
) => {
|
||||
let mut matches = true;
|
||||
matches &= self_via_ir == other_via_ir;
|
||||
matches &= self_optimize == other_optimize;
|
||||
match (
|
||||
self_compiler_version_requirement,
|
||||
other_compiler_version_requirement,
|
||||
) {
|
||||
(
|
||||
Some(VersionOrRequirement::Version(self_version)),
|
||||
Some(VersionOrRequirement::Version(other_version)),
|
||||
) => {
|
||||
matches &= self_version == other_version;
|
||||
}
|
||||
(
|
||||
Some(VersionOrRequirement::Version(version)),
|
||||
Some(VersionOrRequirement::Requirement(requirement)),
|
||||
)
|
||||
| (
|
||||
Some(VersionOrRequirement::Requirement(requirement)),
|
||||
Some(VersionOrRequirement::Version(version)),
|
||||
) => matches &= requirement.matches(version),
|
||||
(
|
||||
Some(VersionOrRequirement::Requirement(..)),
|
||||
Some(VersionOrRequirement::Requirement(..)),
|
||||
) => matches = false,
|
||||
(Some(_), None) | (None, Some(_)) | (None, None) => {}
|
||||
}
|
||||
matches
|
||||
}
|
||||
(Mode::Solidity { .. }, Mode::Unknown(_))
|
||||
| (Mode::Unknown(_), Mode::Solidity { .. })
|
||||
| (Mode::Unknown(_), Mode::Unknown(_)) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_solc_mode(&self) -> Option<&SolcMode> {
|
||||
if let Self::Solidity(mode) = self {
|
||||
Some(mode)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
|
||||
#[serde(into = "String")]
|
||||
pub struct SolcMode {
|
||||
pub via_ir: bool,
|
||||
pub optimize: bool,
|
||||
pub compiler_version_requirement: Option<VersionOrRequirement>,
|
||||
}
|
||||
|
||||
impl SolcMode {
|
||||
pub const ALL: [Self; 4] = [
|
||||
SolcMode {
|
||||
via_ir: false,
|
||||
optimize: false,
|
||||
compiler_version_requirement: None,
|
||||
},
|
||||
SolcMode {
|
||||
via_ir: false,
|
||||
optimize: true,
|
||||
compiler_version_requirement: None,
|
||||
},
|
||||
SolcMode {
|
||||
via_ir: true,
|
||||
optimize: false,
|
||||
compiler_version_requirement: None,
|
||||
},
|
||||
SolcMode {
|
||||
via_ir: true,
|
||||
optimize: true,
|
||||
compiler_version_requirement: None,
|
||||
},
|
||||
];
|
||||
|
||||
pub fn matches(&self, other: &Self) -> bool {
|
||||
Mode::Solidity(self.clone()).matches(&Mode::Solidity(other.clone()))
|
||||
}
|
||||
|
||||
/// Resolves the [`SolcMode`]'s solidity version requirement into a [`VersionOrRequirement`] if
|
||||
/// the requirement is present on the object. Otherwise, the passed default version is used.
|
||||
pub fn compiler_version_to_use(&self, default: Version) -> VersionOrRequirement {
|
||||
match self.compiler_version_requirement {
|
||||
Some(ref requirement) => requirement.clone(),
|
||||
None => default.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SolcMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self {
|
||||
via_ir,
|
||||
optimize,
|
||||
compiler_version_requirement,
|
||||
} = self;
|
||||
|
||||
if *via_ir {
|
||||
write!(f, "Y")?;
|
||||
} else {
|
||||
write!(f, "E")?;
|
||||
has_written = true;
|
||||
}
|
||||
|
||||
if *optimize {
|
||||
write!(f, "+")?;
|
||||
} else {
|
||||
write!(f, "-")?;
|
||||
if let Some(optimize_setting) = self.optimize_setting {
|
||||
if has_written {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
optimize_setting.fmt(f)?;
|
||||
has_written = true;
|
||||
}
|
||||
|
||||
if let Some(req) = compiler_version_requirement {
|
||||
write!(f, " {req}")?;
|
||||
if let Some(version) = &self.version {
|
||||
if has_written {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
version.fmt(f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SolcMode> for String {
|
||||
fn from(value: SolcMode) -> Self {
|
||||
value.to_string()
|
||||
impl From<ParsedMode> for String {
|
||||
fn from(parsed_mode: ParsedMode) -> Self {
|
||||
parsed_mode.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for ParsedMode {
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
ParsedMode::from_str(&value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParsedMode {
|
||||
/// This takes a [`ParsedMode`] and expands it into a list of [`Mode`]s that we should try.
|
||||
pub fn to_modes(&self) -> impl Iterator<Item = Mode> {
|
||||
let pipeline_iter = self.pipeline.as_ref().map_or_else(
|
||||
|| EitherIter::A(ModePipeline::test_cases()),
|
||||
|p| EitherIter::B(std::iter::once(*p)),
|
||||
);
|
||||
|
||||
let optimize_flag_setting = self.optimize_flag.map(|flag| {
|
||||
if flag {
|
||||
ModeOptimizerSetting::M3
|
||||
} else {
|
||||
ModeOptimizerSetting::M0
|
||||
}
|
||||
});
|
||||
|
||||
let optimize_flag_iter = match optimize_flag_setting {
|
||||
Some(setting) => EitherIter::A(std::iter::once(setting)),
|
||||
None => EitherIter::B(ModeOptimizerSetting::test_cases()),
|
||||
};
|
||||
|
||||
let optimize_settings_iter = self.optimize_setting.as_ref().map_or_else(
|
||||
|| EitherIter::A(optimize_flag_iter),
|
||||
|s| EitherIter::B(std::iter::once(*s)),
|
||||
);
|
||||
|
||||
pipeline_iter.flat_map(move |pipeline| {
|
||||
optimize_settings_iter
|
||||
.clone()
|
||||
.map(move |optimize_setting| Mode {
|
||||
pipeline,
|
||||
optimize_setting,
|
||||
version: self.version.clone(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return a set of [`Mode`]s that correspond to the given [`ParsedMode`]s.
|
||||
/// This avoids any duplicate entries.
|
||||
pub fn many_to_modes<'a>(
|
||||
parsed: impl Iterator<Item = &'a ParsedMode>,
|
||||
) -> impl Iterator<Item = Mode> {
|
||||
let modes: HashSet<_> = parsed.flat_map(|p| p.to_modes()).collect();
|
||||
modes.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that could be either of two iterators.
|
||||
#[derive(Clone, Debug)]
|
||||
enum EitherIter<A, B> {
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl<A, B> Iterator for EitherIter<A, B>
|
||||
where
|
||||
A: Iterator,
|
||||
B: Iterator<Item = A::Item>,
|
||||
{
|
||||
type Item = A::Item;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
EitherIter::A(iter) => iter.next(),
|
||||
EitherIter::B(iter) => iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use semver::Version;
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn mode_can_be_parsed_as_expected() {
|
||||
// Arrange
|
||||
let fixtures = [
|
||||
(
|
||||
"Y",
|
||||
vec![
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: true,
|
||||
compiler_version_requirement: None,
|
||||
}),
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: false,
|
||||
compiler_version_requirement: None,
|
||||
}),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Y+",
|
||||
vec![Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: true,
|
||||
compiler_version_requirement: None,
|
||||
})],
|
||||
),
|
||||
(
|
||||
"Y-",
|
||||
vec![Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: false,
|
||||
compiler_version_requirement: None,
|
||||
})],
|
||||
),
|
||||
(
|
||||
"E",
|
||||
vec![
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: false,
|
||||
optimize: true,
|
||||
compiler_version_requirement: None,
|
||||
}),
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: false,
|
||||
optimize: false,
|
||||
compiler_version_requirement: None,
|
||||
}),
|
||||
],
|
||||
),
|
||||
(
|
||||
"E+",
|
||||
vec![Mode::Solidity(SolcMode {
|
||||
via_ir: false,
|
||||
optimize: true,
|
||||
compiler_version_requirement: None,
|
||||
})],
|
||||
),
|
||||
(
|
||||
"E-",
|
||||
vec![Mode::Solidity(SolcMode {
|
||||
via_ir: false,
|
||||
optimize: false,
|
||||
compiler_version_requirement: None,
|
||||
})],
|
||||
),
|
||||
(
|
||||
"Y >=0.8.3",
|
||||
vec![
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: true,
|
||||
compiler_version_requirement: Some(VersionOrRequirement::Requirement(
|
||||
">=0.8.3".parse().unwrap(),
|
||||
)),
|
||||
}),
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: false,
|
||||
compiler_version_requirement: Some(VersionOrRequirement::Requirement(
|
||||
">=0.8.3".parse().unwrap(),
|
||||
)),
|
||||
}),
|
||||
],
|
||||
),
|
||||
(
|
||||
"Y 0.8.3",
|
||||
vec![
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: true,
|
||||
compiler_version_requirement: Some(VersionOrRequirement::Version(
|
||||
Version {
|
||||
major: 0,
|
||||
minor: 8,
|
||||
patch: 3,
|
||||
pre: Default::default(),
|
||||
build: Default::default(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
Mode::Solidity(SolcMode {
|
||||
via_ir: true,
|
||||
optimize: false,
|
||||
compiler_version_requirement: Some(VersionOrRequirement::Version(
|
||||
Version {
|
||||
major: 0,
|
||||
minor: 8,
|
||||
patch: 3,
|
||||
pre: Default::default(),
|
||||
build: Default::default(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
],
|
||||
),
|
||||
fn test_parsed_mode_from_str() {
|
||||
let strings = vec![
|
||||
("Mz", "Mz"),
|
||||
("Y", "Y"),
|
||||
("Y+", "Y+"),
|
||||
("Y-", "Y-"),
|
||||
("E", "E"),
|
||||
("E+", "E+"),
|
||||
("E-", "E-"),
|
||||
("Y M0", "Y M0"),
|
||||
("Y M1", "Y M1"),
|
||||
("Y M2", "Y M2"),
|
||||
("Y M3", "Y M3"),
|
||||
("Y Ms", "Y Ms"),
|
||||
("Y Mz", "Y Mz"),
|
||||
("E M0", "E M0"),
|
||||
("E M1", "E M1"),
|
||||
("E M2", "E M2"),
|
||||
("E M3", "E M3"),
|
||||
("E Ms", "E Ms"),
|
||||
("E Mz", "E Mz"),
|
||||
// When stringifying semver again, 0.8.0 becomes ^0.8.0 (same meaning)
|
||||
("Y 0.8.0", "Y ^0.8.0"),
|
||||
("E+ 0.8.0", "E+ ^0.8.0"),
|
||||
("Y M3 >=0.8.0", "Y M3 >=0.8.0"),
|
||||
("E Mz <0.7.0", "E Mz <0.7.0"),
|
||||
// We can parse +- _and_ M1/M2 but the latter takes priority.
|
||||
("Y+ M1 0.8.0", "Y+ M1 ^0.8.0"),
|
||||
("E- M2 0.7.0", "E- M2 ^0.7.0"),
|
||||
// We don't see this in the wild but it is parsed.
|
||||
("<=0.8", "<=0.8"),
|
||||
];
|
||||
|
||||
for (string, expectation) in fixtures {
|
||||
// Act
|
||||
let actual = Mode::parse_from_string(string);
|
||||
|
||||
// Assert
|
||||
for (actual, expected) in strings {
|
||||
let parsed = ParsedMode::from_str(actual)
|
||||
.expect(format!("Failed to parse mode string '{actual}'").as_str());
|
||||
assert_eq!(
|
||||
actual, expectation,
|
||||
"Parsed {string} into {actual:?} but expected {expectation:?}"
|
||||
)
|
||||
expected,
|
||||
parsed.to_string(),
|
||||
"Mode string '{actual}' did not parse to '{expected}': got '{parsed}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::uninlined_format_args)]
|
||||
fn mode_matches_as_expected() {
|
||||
// Arrange
|
||||
let fixtures = [("Y+", "Y+", true), ("Y+ >=0.8.3", "Y+", true)];
|
||||
fn test_parsed_mode_to_test_modes() {
|
||||
let strings = vec![
|
||||
("Mz", vec!["Y Mz", "E Mz"]),
|
||||
("Y", vec!["Y M0", "Y M3"]),
|
||||
("E", vec!["E M0", "E M3"]),
|
||||
("Y+", vec!["Y M3"]),
|
||||
("Y-", vec!["Y M0"]),
|
||||
("Y <=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8"]),
|
||||
(
|
||||
"<=0.8",
|
||||
vec!["Y M0 <=0.8", "Y M3 <=0.8", "E M0 <=0.8", "E M3 <=0.8"],
|
||||
),
|
||||
];
|
||||
|
||||
for (self_mode, other_mode, expected_result) in fixtures {
|
||||
let self_mode = Mode::parse_from_string(self_mode).pop().unwrap();
|
||||
let other_mode = Mode::parse_from_string(other_mode).pop().unwrap();
|
||||
for (actual, expected) in strings {
|
||||
let parsed = ParsedMode::from_str(actual)
|
||||
.expect(format!("Failed to parse mode string '{actual}'").as_str());
|
||||
let expected_set: HashSet<_> = expected.into_iter().map(|s| s.to_owned()).collect();
|
||||
let actual_set: HashSet<_> = parsed.to_modes().map(|m| m.to_string()).collect();
|
||||
|
||||
// Act
|
||||
let actual = self_mode.matches(&other_mode);
|
||||
|
||||
// Assert
|
||||
assert_eq!(
|
||||
actual, expected_result,
|
||||
"Match of {} and {} failed. Expected {} but got {}",
|
||||
self_mode, other_mode, expected_result, actual
|
||||
expected_set, actual_set,
|
||||
"Mode string '{actual}' did not expand to '{expected_set:?}': got '{actual_set:?}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId
|
||||
use alloy_primitives::TxHash;
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::metadata::ContractInstance;
|
||||
use crate::metadata::{ContractIdent, ContractInstance};
|
||||
|
||||
/// A trait of the interface are required to implement to be used by the resolution logic that this
|
||||
/// crate implements to go from string calldata and into the bytes calldata.
|
||||
@@ -48,7 +48,7 @@ pub trait ResolverApi {
|
||||
/// Contextual information required by the code that's performing the resolution.
|
||||
pub struct ResolutionContext<'a> {
|
||||
/// When provided the contracts provided here will be used for resolutions.
|
||||
deployed_contracts: Option<&'a HashMap<ContractInstance, (Address, JsonAbi)>>,
|
||||
deployed_contracts: Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
|
||||
/// When provided the variables in here will be used for performing resolutions.
|
||||
variables: Option<&'a HashMap<String, U256>>,
|
||||
@@ -66,7 +66,9 @@ impl<'a> ResolutionContext<'a> {
|
||||
}
|
||||
|
||||
pub fn new_from_parts(
|
||||
deployed_contracts: impl Into<Option<&'a HashMap<ContractInstance, (Address, JsonAbi)>>>,
|
||||
deployed_contracts: impl Into<
|
||||
Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
>,
|
||||
variables: impl Into<Option<&'a HashMap<String, U256>>>,
|
||||
block_number: impl Into<Option<&'a BlockNumber>>,
|
||||
transaction_hash: impl Into<Option<&'a TxHash>>,
|
||||
@@ -81,7 +83,9 @@ impl<'a> ResolutionContext<'a> {
|
||||
|
||||
pub fn with_deployed_contracts(
|
||||
mut self,
|
||||
deployed_contracts: impl Into<Option<&'a HashMap<ContractInstance, (Address, JsonAbi)>>>,
|
||||
deployed_contracts: impl Into<
|
||||
Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
>,
|
||||
) -> Self {
|
||||
self.deployed_contracts = deployed_contracts.into();
|
||||
self
|
||||
@@ -122,17 +126,20 @@ impl<'a> ResolutionContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deployed_contract(&self, instance: &ContractInstance) -> Option<&(Address, JsonAbi)> {
|
||||
pub fn deployed_contract(
|
||||
&self,
|
||||
instance: &ContractInstance,
|
||||
) -> Option<&(ContractIdent, Address, JsonAbi)> {
|
||||
self.deployed_contracts
|
||||
.and_then(|deployed_contracts| deployed_contracts.get(instance))
|
||||
}
|
||||
|
||||
pub fn deployed_contract_address(&self, instance: &ContractInstance) -> Option<&Address> {
|
||||
self.deployed_contract(instance).map(|(a, _)| a)
|
||||
self.deployed_contract(instance).map(|(_, a, _)| a)
|
||||
}
|
||||
|
||||
pub fn deployed_contract_abi(&self, instance: &ContractInstance) -> Option<&JsonAbi> {
|
||||
self.deployed_contract(instance).map(|(_, a)| a)
|
||||
self.deployed_contract(instance).map(|(_, _, a)| a)
|
||||
}
|
||||
|
||||
pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> {
|
||||
|
||||
+26
-28
@@ -33,7 +33,7 @@ use alloy::{
|
||||
};
|
||||
use anyhow::Context;
|
||||
use revive_common::EVMVersion;
|
||||
use tracing::Instrument;
|
||||
use tracing::{Instrument, Level};
|
||||
|
||||
use revive_dt_common::{fs::clear_directory, futures::poll};
|
||||
use revive_dt_config::Arguments;
|
||||
@@ -48,7 +48,7 @@ static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
///
|
||||
/// Implements helpers to initialize, spawn and wait the node.
|
||||
///
|
||||
/// Assumes dev mode and IPC only (`P2P`, `http` etc. are kept disabled).
|
||||
/// Assumes dev mode and IPC only (`P2P`, `http`` etc. are kept disabled).
|
||||
///
|
||||
/// Prunes the child process and the base directory on drop.
|
||||
#[derive(Debug)]
|
||||
@@ -60,7 +60,6 @@ pub struct GethNode {
|
||||
geth: PathBuf,
|
||||
id: u32,
|
||||
handle: Option<Child>,
|
||||
network_id: u64,
|
||||
start_timeout: u64,
|
||||
wallet: EthereumWallet,
|
||||
nonce_manager: CachedNonceManager,
|
||||
@@ -165,8 +164,6 @@ impl GethNode {
|
||||
.arg(&self.data_directory)
|
||||
.arg("--ipcpath")
|
||||
.arg(&self.connection_string)
|
||||
.arg("--networkid")
|
||||
.arg(self.network_id.to_string())
|
||||
.arg("--nodiscover")
|
||||
.arg("--maxpeers")
|
||||
.arg("0")
|
||||
@@ -213,6 +210,7 @@ impl GethNode {
|
||||
|
||||
let maximum_wait_time = Duration::from_millis(self.start_timeout);
|
||||
let mut stderr = BufReader::new(logs_file).lines();
|
||||
let mut lines = vec![];
|
||||
loop {
|
||||
if let Some(Ok(line)) = stderr.next() {
|
||||
if line.contains(Self::ERROR_MARKER) {
|
||||
@@ -221,19 +219,24 @@ impl GethNode {
|
||||
if line.contains(Self::READY_MARKER) {
|
||||
return Ok(self);
|
||||
}
|
||||
lines.push(line);
|
||||
}
|
||||
if Instant::now().duration_since(start_time) > maximum_wait_time {
|
||||
anyhow::bail!("Timeout in starting geth");
|
||||
anyhow::bail!(
|
||||
"Timeout in starting geth: took longer than {}ms. stdout:\n\n{}\n",
|
||||
self.start_timeout,
|
||||
lines.join("\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(geth_node_id = self.id), level = "trace")]
|
||||
#[tracing::instrument(skip_all, fields(geth_node_id = self.id), level = Level::TRACE)]
|
||||
fn geth_stdout_log_file_path(&self) -> PathBuf {
|
||||
self.logs_directory.join(Self::GETH_STDOUT_LOG_FILE_NAME)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(geth_node_id = self.id), level = "trace")]
|
||||
#[tracing::instrument(skip_all, fields(geth_node_id = self.id), level = Level::TRACE)]
|
||||
fn geth_stderr_log_file_path(&self) -> PathBuf {
|
||||
self.logs_directory.join(Self::GETH_STDERR_LOG_FILE_NAME)
|
||||
}
|
||||
@@ -259,7 +262,7 @@ impl GethNode {
|
||||
.disable_recommended_fillers()
|
||||
.filler(FallbackGasFiller::new(
|
||||
25_000_000,
|
||||
100_000_000_000,
|
||||
1_000_000_000,
|
||||
1_000_000_000,
|
||||
))
|
||||
.filler(ChainIdFiller::default())
|
||||
@@ -268,7 +271,6 @@ impl GethNode {
|
||||
.connect(&connection_string)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.inspect_err(|err| tracing::error!(%err, "Failed to create the alloy provider"))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -279,29 +281,27 @@ impl EthereumNode for GethNode {
|
||||
&self,
|
||||
transaction: TransactionRequest,
|
||||
) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> {
|
||||
let span = tracing::debug_span!("Submitting transaction", ?transaction);
|
||||
let _guard = span.enter();
|
||||
|
||||
let provider = self.provider().await.map(Arc::new)?;
|
||||
let provider = Arc::new(self.provider().await?);
|
||||
let transaction_hash = *provider.send_transaction(transaction).await?.tx_hash();
|
||||
|
||||
// The following is a fix for the "transaction indexing is in progress" error that we used
|
||||
// to get. You can find more information on this in the following GH issue in geth
|
||||
// The following is a fix for the "transaction indexing is in progress" error that we
|
||||
// used to get. You can find more information on this in the following GH issue in geth
|
||||
// https://github.com/ethereum/go-ethereum/issues/28877. To summarize what's going on,
|
||||
// before we can get the receipt of the transaction it needs to have been indexed by the
|
||||
// node's indexer. Just because the transaction has been confirmed it doesn't mean that it
|
||||
// has been indexed. When we call alloy's `get_receipt` it checks if the transaction was
|
||||
// confirmed. If it has been, then it will call `eth_getTransactionReceipt` method which
|
||||
// _might_ return the above error if the tx has not yet been indexed yet. So, we need to
|
||||
// implement a retry mechanism for the receipt to keep retrying to get it until it
|
||||
// eventually works, but we only do that if the error we get back is the "transaction
|
||||
// node's indexer. Just because the transaction has been confirmed it doesn't mean that
|
||||
// it has been indexed. When we call alloy's `get_receipt` it checks if the transaction
|
||||
// was confirmed. If it has been, then it will call `eth_getTransactionReceipt` method
|
||||
// which _might_ return the above error if the tx has not yet been indexed yet. So, we
|
||||
// need to implement a retry mechanism for the receipt to keep retrying to get it until
|
||||
// it eventually works, but we only do that if the error we get back is the "transaction
|
||||
// indexing is in progress" error or if the receipt is None.
|
||||
//
|
||||
// Getting the transaction indexed and taking a receipt can take a long time especially
|
||||
// when a lot of transactions are being submitted to the node. Thus, while initially we only
|
||||
// allowed for 60 seconds of waiting with a 1 second delay in polling, we need to allow for
|
||||
// a larger wait time. Therefore, in here we allow for 5 minutes of waiting with exponential
|
||||
// backoff each time we attempt to get the receipt and find that it's not available.
|
||||
// when a lot of transactions are being submitted to the node. Thus, while initially we
|
||||
// only allowed for 60 seconds of waiting with a 1 second delay in polling, we need to
|
||||
// allow for a larger wait time. Therefore, in here we allow for 5 minutes of waiting
|
||||
// with exponential backoff each time we attempt to get the receipt and find that it's
|
||||
// not available.
|
||||
poll(
|
||||
Self::RECEIPT_POLLING_DURATION,
|
||||
Default::default(),
|
||||
@@ -327,7 +327,6 @@ impl EthereumNode for GethNode {
|
||||
?transaction_hash
|
||||
))
|
||||
.await
|
||||
.inspect(|receipt| tracing::info!(gas_used = receipt.gas_used, "Gas used on transaction"))
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "info", skip_all, fields(geth_node_id = self.id), err)]
|
||||
@@ -522,7 +521,6 @@ impl Node for GethNode {
|
||||
geth: config.geth.clone(),
|
||||
id,
|
||||
handle: None,
|
||||
network_id: config.network_id,
|
||||
start_timeout: config.geth_start_timeout,
|
||||
wallet,
|
||||
// We know that we only need to be storing 2 files so we can specify that when creating
|
||||
|
||||
@@ -367,9 +367,9 @@ impl KitchensinkNode {
|
||||
.disable_recommended_fillers()
|
||||
.network::<KitchenSinkNetwork>()
|
||||
.filler(FallbackGasFiller::new(
|
||||
30_000_000,
|
||||
200_000_000_000,
|
||||
3_000_000_000,
|
||||
25_000_000,
|
||||
1_000_000_000,
|
||||
1_000_000_000,
|
||||
))
|
||||
.filler(ChainIdFiller::default())
|
||||
.filler(NonceFiller::new(nonce_manager))
|
||||
|
||||
@@ -8,6 +8,7 @@ repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
revive-dt-common = { workspace = true }
|
||||
revive-dt-config = { workspace = true }
|
||||
revive-dt-format = { workspace = true }
|
||||
revive-dt-compiler = { workspace = true }
|
||||
|
||||
@@ -12,11 +12,12 @@ use std::{
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use revive_dt_compiler::{CompilerInput, CompilerOutput};
|
||||
use serde::Serialize;
|
||||
|
||||
use revive_dt_common::types::Mode;
|
||||
use revive_dt_compiler::{CompilerInput, CompilerOutput};
|
||||
use revive_dt_config::{Arguments, TestingPlatform};
|
||||
use revive_dt_format::{corpus::Corpus, mode::SolcMode};
|
||||
use revive_dt_format::corpus::Corpus;
|
||||
|
||||
use crate::analyzer::CompilerStatistics;
|
||||
|
||||
@@ -48,7 +49,7 @@ pub struct CompilationTask {
|
||||
/// The observed compiler output.
|
||||
pub json_output: Option<CompilerOutput>,
|
||||
/// The observed compiler mode.
|
||||
pub mode: SolcMode,
|
||||
pub mode: Mode,
|
||||
/// The observed compiler version.
|
||||
pub compiler_version: String,
|
||||
/// The observed error, if any.
|
||||
|
||||
Reference in New Issue
Block a user