diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..c01a6e03 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,30 @@ +[build] +rustflags = [ + "-W", "missing_docs", +] +incremental = true + +[target.x86_64-apple-darwin] +rustflags = [ + "-W", "missing_docs", + "-C", "link-arg=-mmacosx-version-min=11.0", +] + +[target.aarch64-apple-darwin] +rustflags = [ + "-W", "missing_docs", + "-C", "link-arg=-mmacosx-version-min=11.0", +] + +[profile.release] +strip = false + +[env] +LLVM_SYS_150_PREFIX = { value = "./target-llvm/target-final/", relative = true, force = false } +RUST_BACKTRACE = { value = "full" } +RUST_LOG = { value = "vm=trace" } + +[tools.clippy] +warn = [ + "missing_docs_in_private_items", +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d3af5205 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# The LLVM framework source and build +/llvm/ +/target-llvm*/ + +# External compilers +/solc-bin/* +/vyper-bin/* + +# The debug and trace artifacts +/debug/ +/trace/ + +# The dependency locks +# /Cargo.lock +# /LLVM.lock + +# IDE +/.idea/ +/.vscode/ + +# CI +/.github/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f6e48e73 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "tests"] + path = tests + url = https://github.com/matter-labs/era-compiler-tests + branch = main +[submodule "solidity"] + path = solidity + url = https://github.com/ethereum/solidity + branch = develop +[submodule "system-contracts"] + path = system-contracts + url = ssh://git@github.com/code-423n4/2023-03-zksync.git + branch = main diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..f129e606 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contribution Guidelines + +Thank you for considering helping out with the source code! We are extremely grateful for any consideration of +contributions to this repository. However, at this time, we generally do not accept external contributions. This policy +will change in the future, so please check back regularly for updates. + +For security issues, please contact us at [security@matterlabs.dev](mailto:security@matterlabs.dev). + +Thank you for your support in accelerating the mass adoption of crypto for personal sovereignty! diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..382d4cc3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2626 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "benchmark-analyzer" +version = "1.3.1" +dependencies = [ + "anyhow", + "colored", + "libmath", + "serde", + "serde_json", + "structopt", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "colored" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +dependencies = [ + "atty", + "lazy_static", + "winapi", +] + +[[package]] +name = "compiler-common" +version = "1.3.1" +source = "git+https://github.com/matter-labs/era-compiler-common?rev=a6c5b02e4f149f82f1c3821a6f258363308abd2a#a6c5b02e4f149f82f1c3821a6f258363308abd2a" + +[[package]] +name = "compiler-llvm-context" +version = "1.3.1" +source = "git+https://github.com/matter-labs/era-compiler-llvm-context?rev=a1e29ba41fc081ff67d967c11d5e110d38e5b1ac#a1e29ba41fc081ff67d967c11d5e110d38e5b1ac" +dependencies = [ + "anyhow", + "compiler-common", + "hex", + "inkwell", + "itertools", + "md5", + "num", + "once_cell", + "regex", + "semver", + "sha2", + "sha3 0.10.6", + "zkevm-assembly", + "zkevm_opcode_defs", +] + +[[package]] +name = "compiler-solidity" +version = "1.3.6" +source = "git+https://github.com/matter-labs/era-compiler-solidity?tag=v1.3.6#33e5759dd715925a3b8bbaab8937e24641d369c6" +dependencies = [ + "anyhow", + "colored", + "compiler-common", + "compiler-llvm-context", + "hex", + "inkwell", + "md5", + "mimalloc", + "num", + "rand 0.8.5", + "rayon", + "regex", + "semver", + "serde", + "serde_json", + "sha3 0.10.6", + "shell-words", + "structopt", + "thiserror", + "zkevm-assembly", +] + +[[package]] +name = "compiler-tester" +version = "1.3.1" +dependencies = [ + "anyhow", + "benchmark-analyzer", + "bincode", + "colored", + "compiler-common", + "compiler-llvm-context", + "compiler-solidity", + "compiler-vyper", + "glob", + "hex", + "inkwell", + "itertools", + "lazy_static", + "md5", + "rayon", + "regex", + "reqwest", + "ron", + "semver", + "serde", + "serde_json", + "serde_yaml", + "sha3 0.10.6", + "shell-words", + "solidity-adapter", + "structopt", + "tokio", + "web3", + "zkevm-assembly", + "zkevm_opcode_defs", + "zkevm_tester", +] + +[[package]] +name = "compiler-vyper" +version = "1.3.3" +source = "git+https://github.com/matter-labs/era-compiler-vyper?tag=v1.3.3#932a0ae673c7512112dc7ddd5fe8baf1c3da1227" +dependencies = [ + "anyhow", + "colored", + "compiler-common", + "compiler-llvm-context", + "hex", + "inkwell", + "lazy_static", + "mimalloc", + "rayon", + "semver", + "serde", + "serde_json", + "serde_stacker", + "sha3 0.10.6", + "shell-words", + "structopt", + "zkevm-assembly", + "zkevm_opcode_defs", +] + +[[package]] +name = "const-oid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "coverage-watcher" +version = "1.3.1" +dependencies = [ + "anyhow", + "compiler-common", + "serde", + "serde_yaml", + "structopt", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest 0.10.6", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "ethabi" +version = "16.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c98847055d934070b90e806e12d3936b787d0a115068981c1d8dfd5dfef5a5" +dependencies = [ + "ethereum-types", + "hex", + "serde", + "serde_json", + "sha3 0.9.1", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + +[[package]] +name = "futures" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-executor" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "headers" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +dependencies = [ + "base64 0.13.1", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inkwell" +version = "0.1.0" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-15#9e38068300c41dc6d20fb0d06832be256d6137d6" +dependencies = [ + "either", + "inkwell_internals", + "libc", + "llvm-sys", + "once_cell", + "parking_lot", +] + +[[package]] +name = "inkwell_internals" +version = "0.6.0" +source = "git+https://github.com/matter-labs-forks/inkwell?branch=llvm-15#9e38068300c41dc6d20fb0d06832be256d6137d6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "libmath" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfd3416934a853ae80d5c3b006f632dfcbaf320300c5167e88a469e9ac214502" +dependencies = [ + "rand 0.3.23", +] + +[[package]] +name = "libmimalloc-sys" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8c7cbf8b89019683667e347572e6d55a7df7ea36b0c4ce69961b0cde67b174" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "llvm-sys" +version = "150.0.5" +source = "git+https://github.com/matter-labs-forks/llvm-sys.rs?branch=llvm-15.0#71d3d59ea8f5974b9ffc3a6631676e0fa2825a27" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mimalloc" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcb174b18635f7561a0c6c9fc2ce57218ac7523cf72c50af80e2d79ab8f3ba1" +dependencies = [ + "libmimalloc-sys", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parity-scale-codec" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ron" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" +dependencies = [ + "base64 0.13.1", + "bitflags", + "serde", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c42e6f1735c5f00f51e43e28d6634141f2bcad10931b2609ddd74a86d751260" +dependencies = [ + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_stacker" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5557f4c1103cecd0e639a17ab22d670b89912d8a506589ee627bf738a15a5d" +dependencies = [ + "serde", + "stacker", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.6", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "solidity-adapter" +version = "1.3.1" +dependencies = [ + "anyhow", + "compiler-common", + "md5", + "regex", + "semver", + "serde", + "serde_yaml", + "structopt", + "web3", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stacker" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "winapi", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" + +[[package]] +name = "toml_edit" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna 0.3.0", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vlog" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2266fcb904c50fb17fda4c9a751a1715629ecf8b21f4c9d78b4890fb71525d71" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web3" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f258e254752d210b84fe117b31f1e3cc9cbf04c0d747eb7f8cf7cf5e370f6d" +dependencies = [ + "arrayvec", + "base64 0.13.1", + "bytes", + "derive_more", + "ethabi", + "ethereum-types", + "futures", + "futures-timer", + "headers", + "hex", + "idna 0.2.3", + "jsonrpc-core", + "log", + "once_cell", + "parking_lot", + "pin-project", + "reqwest", + "rlp", + "secp256k1", + "serde", + "serde_json", + "tiny-keccak", + "url", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winnow" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" + +[[package]] +name = "zk_evm" +version = "1.3.1" +source = "git+https://github.com/matter-labs/era-zk_evm?branch=main#1ca431a89a0d525761fc6d5952d40530d50fdfdd" +dependencies = [ + "blake2", + "k256", + "lazy_static", + "num", + "serde", + "serde_json", + "sha2", + "sha3 0.10.6", + "static_assertions", + "zkevm_opcode_defs", +] + +[[package]] +name = "zkevm-assembly" +version = "1.3.1" +source = "git+https://github.com/matter-labs/era-zkEVM-assembly?branch=main#f5f9d4ad68833cf37525f96996f09282019e3f40" +dependencies = [ + "env_logger", + "hex", + "lazy_static", + "log", + "nom", + "num-bigint", + "num-traits", + "regex", + "sha3 0.10.6", + "smallvec", + "structopt", + "thiserror", + "zkevm_opcode_defs", +] + +[[package]] +name = "zkevm_opcode_defs" +version = "1.3.1" +source = "git+https://github.com/matter-labs/era-zkevm_opcode_defs?branch=main#f936ff20a18976f07ef89a909b5688f09dac7f07" +dependencies = [ + "bitflags", + "ethereum-types", + "lazy_static", + "sha2", +] + +[[package]] +name = "zkevm_tester" +version = "1.3.0" +source = "git+https://github.com/matter-labs/era-zkevm_tester?branch=v1.3.1#e8eb2bd994663954ef5738f5511651204165c25e" +dependencies = [ + "anyhow", + "futures", + "hex", + "num-bigint", + "num-traits", + "serde", + "serde_json", + "sha2", + "tracing", + "vlog", + "zk_evm", + "zkevm-assembly", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..ad1c572a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "compiler_tester", + "solidity_adapter", + "coverage_watcher", + "benchmark_analyzer", +] diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..9fc9f4c8 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright (c) 2019 Matter Labs + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..2739ea6e --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Matter Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LLVM.lock b/LLVM.lock new file mode 100644 index 00000000..8dd66382 --- /dev/null +++ b/LLVM.lock @@ -0,0 +1,2 @@ +url = "https://github.com/matter-labs/era-compiler-llvm" +branch = "v1.3.2" diff --git a/README.md b/README.md new file mode 100644 index 00000000..bd8a3dbb --- /dev/null +++ b/README.md @@ -0,0 +1,211 @@ +# zkSync Era: The zkEVM Compiler Integration Test Framework + +[![Logo](eraLogo.svg)](https://zksync.io/) + +zkSync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security +or decentralization. As it's EVM-compatible (with Solidity/Vyper), 99% of Ethereum projects can redeploy without +needing to refactor or re-audit any code. zkSync Era also uses an LLVM-based compiler that will eventually enable +developers to write smart contracts in popular languages such as C++ and Rust. + +The `compiler-tester` integration test framework runs tests for Matter Labs compilers which target the zkEVM, +for supported languages listed below. It compiles source code via internal API calls, +e.g. to [Inkwell](https://thedan64.github.io/inkwell/inkwell/index.html). In software quality assurance jargon, +this makes it a whitebox testing framework. + +The `compiler-tester` repository includes the Compiler Tests Collection repository as a submodule. + +By default, the Tester SHOULD run the entire Collection in all possible combinations of compiler versions and settings, +but it MAY omit some subset of the combinations for the sake of saving time, e.g. when only front-end changes have been +made, and there is no point of running tests in all LLVM optimization modes. + +## Building + +1. Install some tools system-wide: + 1.a. `apt install cmake ninja-build clang-13 lld-13 parallel pkg-config` on a Debian-based Linux, with optional `musl-tools` if you need a `musl` build + 1.b. `pacman -S cmake ninja clang lld parallel` on an Arch-based Linux + 1.c. On MacOS, install the [HomeBrew](https://brew.sh) package manager (being careful to install it as the appropriate user), then `brew install cmake ninja coreutils parallel`. Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apple’s Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager. + 1.d. Their equivalents with other package managers + +1. [Install Rust](https://www.rust-lang.org/tools/install). + +1. Check out or clone the appropriate branch of this repository using the `--recursive` option. + +1. Install the LLVM building tool: `cargo install compiler-llvm-builder`. + +1. Clone and build the LLVM framework: `zkevm-llvm clone && zkevm-llvm build`. + +1. Build the Tester with `cargo build --release`. + +Then run the tests using the examples below under “Usage.” + +## What is supported + +### Languages + +- Solidity +- Yul +- Vyper +- LLVM IR +- zkEVM assembly + +### Optimization modes + +- LLVM middle-end (levels 0 to 3, s, z, e.g. `M0`, `M1` etc.) +- LLVM back-end (levels 0 to 3, e.g. `B0`, `B1` etc.) +- `solc` compiler (disabled or enabled), marked as one of [`Y-`, `Y+`, `E-`, `E+`] +- `vyper` compiler (disabled or enabled), marked as one of [`V-`, `V+`] + +### Compiler versions + +Only relevant for Solidity and Vyper tests: + +- `^0.8` for compiling Solidity via Yul +- [0.4.10; latest] for compiling Solidity via EVM assembly +- [0.3.3; latest] for compiling Vyper via LLL IR + +### Compiler pipelines + +Currently only relevant for the Solidity compiler, where you can choose the IR: + +- Yul (preferred for Solidity ≥0.8) +- EVM (supports Solidity ≥0.4) + +### Wildcards + +Most of the specifiers support wildcards `*` (any), `^` ('3' and 'z'). +With no mode argument, iterates over all option combinations (approximately 800). + +## Usage + +Each command assumes you are at the root of the `compiler-tester` repository. + +### Generic command + +```bash +cargo run --release --bin compiler-tester -- [-v] [-D] [-T[T]] \ + [--path="${PATH}"]* \ + [--mode="${MODE}"]* +``` + +There are more rarely used options, which you may check out with `./target/release/compiler-tester --help`. + +### Example 1 + +Run a simple Solidity test, dumping Yul, unoptimized and optimized LLVM IR, and zkEVM assembly to the specified directory. + +Use: + +- Yul as the Solidity IR (`Y`) +- Yul optimizations enabled (`+`) +- level 3 optimizations in LLVM middle-end (`M3`) +- level 3 optimizations in LLVM back-end (`B3`) +- Solidity compiler version (`0.8.19`) + +Output: + +- failed and invalid tests only (absence of `-v`) +- the compiler debug data to the `./debug/` directory (`-D`) +- the VM trace data to the `./trace/` directory (`-T`) + +```bash +cargo run --release --bin compiler-tester -- -DT \ + --path='tests/solidity/simple/default.sol' \ + --mode='Y+M3B3 0.8.19' +``` + +### Example 2 + +Run all simple Solidity tests. This currently runs about three hundred tests and takes about eight minutes. + +Use: + +- level 1 optimizations in LLVM middle-end (`M1`) +- level 2 optimizations in LLVM back-end (`B2`) + +Output: + +- all tests, passed and failed (`-v`) +- the VM trace data to the `./trace/` directory (`-T`) + +```bash +cargo run --release --bin compiler-tester -- -vT \ + --path='tests/yul/' \ + --mode='M1B2' +``` + +### Example 3 + +Run all tests (currently about three million) in all modes. +This takes a few hours on the CI server, and probably much longer on your personal machine. + +```bash +cargo run --release --bin compiler-tester +``` + +## Benchmarking + +1. Change the LLVM branch to the base in the `LLVM.lock` file at the repository root, checkout and build it: +``` +zkevm-llvm checkout && zkevm-llvm build +``` + +2. Run the Tester with the desired filters and the output JSON path: +``` +./target/release/compiler-tester \ + --path='tests/solidity/simple/default.sol' \ + --mode='Y+M^B3 0.8.19' \ + --benchmark='reference.json' +``` + +3. Change the LLVM branch to your patch in the `LLVM.lock` file at the repository root, checkout and build it: +``` +zkevm-llvm checkout && zkevm-llvm build +``` + +4. Run the Tester with the desired filters and the output JSON path: +``` +./target/release/compiler-tester \ + --path='tests/solidity/simple/default.sol' \ + --mode='Y+M^B3 0.8.19' \ + --benchmark='candidate.json' +``` + +5. Run the benchmark analyzer on the two JSONs: +``` +cargo run --release --bin benchmark-analyzer -- --reference reference.json --candidate candidate.json +``` + +After you make any changes in LLVM, you only need to repeat steps 2-3 to update the working branch benchmark data. + +## Troubleshooting + +- If you get a “failed to authenticate when downloading repository… if the git CLI succeeds then net.git-fetch-with-cli may help here” error, +then prepending the `cargo` command with `CARGO_NET_GIT_FETCH_WITH_CLI=true` +may help. +- On MacOS, `git config --global credential.helper osxkeychain` followed by cloning a repository manually with a personal access token may help. +- Unset any LLVM-related environment variables you may have set, especially `LLVM_SYS__PREFIX` (see e.g. [https://crates.io/crates/llvm-sys](https://crates.io/crates/llvm-sys) and [https://llvm.org/docs/GettingStarted.html#local-llvm-configuration](https://llvm.org/docs/GettingStarted.html#local-llvm-configuration)). To make sure: `set | grep LLVM` + +## License + +The Solidity compiler is distributed under the terms of either + +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +## Official Links + +- [Website](https://zksync.io/) +- [GitHub](https://github.com/matter-labs) +- [Twitter](https://twitter.com/zksync) +- [Twitter for Devs](https://twitter.com/zkSyncDevs) +- [Discord](https://discord.gg/nMaPGrDDwk) + +## Disclaimer + +zkSync Era has been through extensive testing and audits, and although it is live, it is still in alpha state and +will undergo further audits and bug bounty programs. We would love to hear our community's thoughts and suggestions +about it! +It's important to note that forking it now could potentially lead to missing important +security updates, critical features, and performance improvements. diff --git a/benchmark_analyzer/Cargo.toml b/benchmark_analyzer/Cargo.toml new file mode 100644 index 00000000..f20311f6 --- /dev/null +++ b/benchmark_analyzer/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "benchmark-analyzer" +version = "1.3.1" +authors = [ + "Alex Zarudnyy ", +] +license = "MIT OR Apache-2.0" +edition = "2021" +description = "The zkEVM benchmarks analyzer" + +[[bin]] +name = "benchmark-analyzer" +path = "src/benchmark_analyzer/main.rs" + +[dependencies] +structopt = { version = "0.3", default-features = false } +anyhow = "1.0" +colored = "2.0" + +serde = { version = "1.0", features = [ "derive" ] } +serde_json = "1.0" +libmath = "0.2" diff --git a/benchmark_analyzer/src/benchmark/group/element.rs b/benchmark_analyzer/src/benchmark/group/element.rs new file mode 100644 index 00000000..df2db48c --- /dev/null +++ b/benchmark_analyzer/src/benchmark/group/element.rs @@ -0,0 +1,26 @@ +//! +//! The benchmark element. +//! + +use serde::Deserialize; +use serde::Serialize; + +/// +/// The benchmark element. +/// +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Element { + /// The contract size, `Some` for contracts deploys. + pub size: Option, + /// The number of cycles. + pub cycles: usize, +} + +impl Element { + /// + /// A shortcut constructor. + /// + pub fn new(size: Option, cycles: usize) -> Self { + Self { size, cycles } + } +} diff --git a/benchmark_analyzer/src/benchmark/group/mod.rs b/benchmark_analyzer/src/benchmark/group/mod.rs new file mode 100644 index 00000000..6ff7e7d5 --- /dev/null +++ b/benchmark_analyzer/src/benchmark/group/mod.rs @@ -0,0 +1,118 @@ +//! +//! The benchmark group representation. +//! + +pub mod element; +pub mod results; + +use std::collections::BTreeMap; + +use serde::Deserialize; +use serde::Serialize; + +use self::element::Element; +use self::results::Results; + +/// +/// The benchmark group representation. +/// +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct Group { + /// The group elements. + pub elements: BTreeMap, +} + +impl Group { + /// + /// Compares two benchmark groups. + /// + pub fn compare<'a>(reference: &'a Self, candidate: &'a Self) -> Results<'a> { + let elements_number = reference.elements.len(); + + let mut size_factors = Vec::with_capacity(elements_number); + let mut size_min = 1.0; + let mut size_max = 1.0; + let mut size_negatives = Vec::with_capacity(elements_number); + let mut size_positives = Vec::with_capacity(elements_number); + let mut size_total_reference: u64 = 0; + let mut size_total_candidate: u64 = 0; + + let mut cycles_factors = Vec::with_capacity(elements_number); + let mut cycles_min = 1.0; + let mut cycles_max = 1.0; + let mut cycles_negatives = Vec::with_capacity(elements_number); + let mut cycles_positives = Vec::with_capacity(elements_number); + let mut cycles_total_reference: u64 = 0; + let mut cycles_total_candidate: u64 = 0; + + for (path, reference) in reference.elements.iter() { + let candidate = match candidate.elements.get(path.as_str()) { + Some(candidate) => candidate, + None => continue, + }; + + cycles_total_reference += reference.cycles as u64; + cycles_total_candidate += candidate.cycles as u64; + let cycles_factor = (candidate.cycles as f64) / (reference.cycles as f64); + if cycles_factor > 1.0 { + cycles_negatives.push((cycles_factor, path.as_str())); + } + if cycles_factor < 1.0 { + cycles_positives.push((cycles_factor, path.as_str())); + } + if cycles_factor < cycles_min { + cycles_min = cycles_factor; + } + if cycles_factor > cycles_max { + cycles_max = cycles_factor; + } + cycles_factors.push(cycles_factor); + + let reference_size = match reference.size { + Some(size) => size, + None => continue, + }; + let candidate_size = match candidate.size { + Some(size) => size, + None => continue, + }; + size_total_reference += reference_size as u64; + size_total_candidate += candidate_size as u64; + let size_factor = (candidate_size as f64) / (reference_size as f64); + if size_factor > 1.0 { + size_negatives.push((size_factor, path.as_str())); + } + if size_factor < 1.0 { + size_positives.push((size_factor, path.as_str())); + } + if size_factor < size_min { + size_min = size_factor; + } + if size_factor > size_max { + size_max = size_factor; + } + size_factors.push(size_factor); + } + + let size_geomean = math::mean::geometric(size_factors.as_slice()); + let size_total = (size_total_candidate as f64) / (size_total_reference as f64); + + let cycles_geomean = math::mean::geometric(cycles_factors.as_slice()); + let cycles_total = (cycles_total_candidate as f64) / (cycles_total_reference as f64); + + Results::new( + size_geomean, + size_min, + size_max, + size_total, + size_negatives, + size_positives, + cycles_geomean, + cycles_min, + cycles_max, + cycles_total, + cycles_negatives, + cycles_positives, + ) + } +} diff --git a/benchmark_analyzer/src/benchmark/group/results.rs b/benchmark_analyzer/src/benchmark/group/results.rs new file mode 100644 index 00000000..3be79465 --- /dev/null +++ b/benchmark_analyzer/src/benchmark/group/results.rs @@ -0,0 +1,243 @@ +//! +//! The benchmark group results. +//! + +use colored::Colorize; +use std::cmp; + +/// +/// The benchmark group results. +/// +#[derive(Debug)] +pub struct Results<'a> { + /// The size geometric mean. + pub size_mean: f64, + /// The size best result. + pub size_best: f64, + /// The size worst result. + pub size_worst: f64, + /// The size total decrease result. + pub size_total: f64, + /// The size negative result test names. + pub size_negatives: Vec<(f64, &'a str)>, + /// The size positive result test names. + pub size_positives: Vec<(f64, &'a str)>, + + /// The cycles geometric mean. + pub cycles_mean: f64, + /// The cycles best result. + pub cycles_best: f64, + /// The cycles worst result. + pub cycles_worst: f64, + /// The cycles total decrease result. + pub cycles_total: f64, + /// The cycles negative result test names. + pub cycles_negatives: Vec<(f64, &'a str)>, + /// The cycles positive result test names. + pub cycles_positives: Vec<(f64, &'a str)>, +} + +impl<'a> Results<'a> { + /// + /// A shortcut constructor. + /// + #[allow(clippy::too_many_arguments)] + pub fn new( + size_mean: f64, + size_best: f64, + size_worst: f64, + size_total: f64, + size_negatives: Vec<(f64, &'a str)>, + size_positives: Vec<(f64, &'a str)>, + + cycles_mean: f64, + cycles_best: f64, + cycles_worst: f64, + cycles_total: f64, + cycles_negatives: Vec<(f64, &'a str)>, + cycles_positives: Vec<(f64, &'a str)>, + ) -> Self { + Self { + size_mean, + size_best, + size_worst, + size_total, + size_negatives, + size_positives, + + cycles_mean, + cycles_best, + cycles_worst, + cycles_total, + cycles_negatives, + cycles_positives, + } + } + + /// + /// Sorts the worst results. + /// + pub fn sort_worst(&mut self) { + self.size_negatives.sort_by(|a, b| { + if a.0 > b.0 { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Equal + } + }); + self.cycles_negatives.sort_by(|a, b| { + if a.0 > b.0 { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Equal + } + }); + self.size_positives.sort_by(|a, b| { + if a.0 < b.0 { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Equal + } + }); + self.cycles_positives.sort_by(|a, b| { + if a.0 < b.0 { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Equal + } + }); + } + + /// + /// Writes the worst benchmark results top to the terminal. + /// + pub fn print_worst_results(&self, count: usize, group_name: &str) { + println!( + "Group '{}' size (-%) worst {} out of {}:", + group_name, + count, + self.size_negatives.len() + ); + for (value, path) in self.size_negatives.iter().take(count) { + println!("{:010}: {}", Self::format_geomean(*value), path); + } + println!(); + println!( + "Group '{}' cycles (-%) worst {} out of {}:", + group_name, + count, + self.cycles_negatives.len() + ); + for (value, path) in self.cycles_negatives.iter().take(count) { + println!("{:010}: {}", Self::format_geomean(*value), path); + } + println!(); + println!( + "Group '{}' size (-%) best {} out of {}:", + group_name, + count, + self.size_positives.len() + ); + for (value, path) in self.size_positives.iter().take(count) { + println!("{:010}: {}", Self::format_geomean(*value), path); + } + println!(); + println!( + "Group '{}' cycles (-%) best {} out of {}:", + group_name, + count, + self.cycles_positives.len() + ); + for (value, path) in self.cycles_positives.iter().take(count) { + println!("{:010}: {}", Self::format_geomean(*value), path); + } + println!(); + } + + /// + /// Formats and colorizes a mean value. + /// + fn format_geomean(value: f64) -> colored::ColoredString { + if value > 1.0 { + format!("{:7.3}", 100.0 - value * 100.0).bright_red() + } else if value == 1.0 { + format!("{:7.3}", 100.0 - value * 100.0).white() + } else { + format!("{:7.3}", 100.0 - value * 100.0).green() + } + } + + /// + /// Prints the results to a file. + /// + pub fn write_all(&self, w: &mut W, group_name: &str) -> anyhow::Result<()> + where + W: std::io::Write, + { + writeln!( + w, + "╔═╡ {} ╞{}╡ {} ╞═╗", + "Size (-%)".bright_white(), + "═".repeat(cmp::max(24 - group_name.len(), 0)), + group_name.bright_white() + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Mean".bright_white(), + Self::format_geomean(self.size_mean) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Best".bright_white(), + Self::format_geomean(self.size_best) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Worst".bright_white(), + Self::format_geomean(self.size_worst) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Total".bright_white(), + Self::format_geomean(self.size_total) + )?; + writeln!( + w, + "╠═╡ {} ╞{}╡ {} ╞═╣", + "Cycles (-%)".bright_white(), + "═".repeat(cmp::max(22 - group_name.len(), 0)), + group_name.bright_white() + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Mean".bright_white(), + Self::format_geomean(self.cycles_mean) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Best".bright_white(), + Self::format_geomean(self.cycles_best) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Worst".bright_white(), + Self::format_geomean(self.cycles_worst) + )?; + writeln!( + w, + "║ {:33} {:07} ║", + "Total".bright_white(), + Self::format_geomean(self.cycles_total) + )?; + writeln!(w, "╚═══════════════════════════════════════════╝")?; + + Ok(()) + } +} diff --git a/benchmark_analyzer/src/benchmark/mod.rs b/benchmark_analyzer/src/benchmark/mod.rs new file mode 100644 index 00000000..483413ae --- /dev/null +++ b/benchmark_analyzer/src/benchmark/mod.rs @@ -0,0 +1,66 @@ +//! +//! The benchmark representation. +//! + +pub mod group; + +use std::collections::BTreeMap; +use std::path::PathBuf; + +use serde::Deserialize; +use serde::Serialize; + +use self::group::results::Results; +use self::group::Group; + +/// +/// The benchmark representation. +/// +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct Benchmark { + /// The benchmark groups. + pub groups: BTreeMap, +} + +impl Benchmark { + /// + /// Compares two benchmarks. + /// + pub fn compare<'a>(reference: &'a Self, candidate: &'a Self) -> BTreeMap<&'a str, Results<'a>> { + let mut results = BTreeMap::new(); + + for (group_name, reference) in reference.groups.iter() { + let candidate = match candidate.groups.get(group_name) { + Some(candidate) => candidate, + None => continue, + }; + + let group_results = Group::compare(reference, candidate); + results.insert(group_name.as_str(), group_results); + } + + results + } + + /// + /// Writes the benchmark to a file. + /// + pub fn write_to_file(self, path: PathBuf) -> anyhow::Result<()> { + let contents = serde_json::to_string(&self).expect("Always valid"); + std::fs::write(path.as_path(), contents) + .map_err(|error| anyhow::anyhow!("Benchmark file {:?} reading: {}", path, error))?; + Ok(()) + } +} + +impl TryFrom for Benchmark { + type Error = anyhow::Error; + + fn try_from(path: PathBuf) -> Result { + let text = std::fs::read_to_string(path.as_path()) + .map_err(|error| anyhow::anyhow!("Benchmark file {:?} reading: {}", path, error))?; + let json: Self = serde_json::from_str(text.as_str()) + .map_err(|error| anyhow::anyhow!("Benchmark file {:?} parsing: {}", path, error))?; + Ok(json) + } +} diff --git a/benchmark_analyzer/src/benchmark_analyzer/arguments.rs b/benchmark_analyzer/src/benchmark_analyzer/arguments.rs new file mode 100644 index 00000000..7d3996de --- /dev/null +++ b/benchmark_analyzer/src/benchmark_analyzer/arguments.rs @@ -0,0 +1,35 @@ +//! +//! The benchmark analyzer arguments. +//! + +use std::path::PathBuf; + +use structopt::StructOpt; + +/// +/// The benchmark analyzer arguments. +/// +#[derive(Debug, StructOpt)] +#[structopt(name = "benchmark-analyzer", about = "The zkEVM benchmark analyzer")] +pub struct Arguments { + /// The reference build benchmark. + #[structopt(long = "reference")] + pub reference: PathBuf, + + /// The candidate build benchmark. + #[structopt(long = "candidate")] + pub candidate: PathBuf, + + /// The output file. If unset, the result is printed to `stdout`. + #[structopt(short = "o", long = "output-file")] + pub output_path: Option, +} + +impl Arguments { + /// + /// A shortcut constructor. + /// + pub fn new() -> Self { + Self::from_args() + } +} diff --git a/benchmark_analyzer/src/benchmark_analyzer/main.rs b/benchmark_analyzer/src/benchmark_analyzer/main.rs new file mode 100644 index 00000000..1ba8bca5 --- /dev/null +++ b/benchmark_analyzer/src/benchmark_analyzer/main.rs @@ -0,0 +1,48 @@ +//! +//! The benchmark analyzer binary. +//! + +pub(crate) mod arguments; + +use std::io::Write; + +use self::arguments::Arguments; + +/// +/// The application entry point. +/// +fn main() -> anyhow::Result<()> { + let arguments = Arguments::new(); + + let reference = benchmark_analyzer::Benchmark::try_from(arguments.reference)?; + let candidate = benchmark_analyzer::Benchmark::try_from(arguments.candidate)?; + + let groups_results = benchmark_analyzer::Benchmark::compare(&reference, &candidate); + + match arguments.output_path { + Some(output_path) => { + let mut file = std::fs::File::create(output_path)?; + for (group_name, mut results) in groups_results.into_iter() { + results.sort_worst(); + results.print_worst_results(100, group_name); + results.write_all(&mut file, group_name)?; + writeln!(file)?; + println!(); + println!(); + } + } + None => { + let mut stdout = std::io::stdout(); + for (group_name, mut results) in groups_results.into_iter() { + results.sort_worst(); + results.print_worst_results(100, group_name); + results.write_all(&mut stdout, group_name)?; + writeln!(stdout)?; + println!(); + println!(); + } + } + } + + Ok(()) +} diff --git a/benchmark_analyzer/src/lib.rs b/benchmark_analyzer/src/lib.rs new file mode 100644 index 00000000..f01677a8 --- /dev/null +++ b/benchmark_analyzer/src/lib.rs @@ -0,0 +1,14 @@ +//! +//! The benchmark analyzer library. +//! + +pub(crate) mod benchmark; + +pub use self::benchmark::group::element::Element as BenchmarkElement; +pub use self::benchmark::group::Group as BenchmarkGroup; +pub use self::benchmark::Benchmark; + +/// +/// The all elements group name. +/// +pub const BENCHMARK_ALL_GROUP_NAME: &str = "All"; diff --git a/compiler_tester/Cargo.toml b/compiler_tester/Cargo.toml new file mode 100644 index 00000000..afc5690d --- /dev/null +++ b/compiler_tester/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "compiler-tester" +version = "1.3.1" +authors = [ + "Alex Zarudnyy ", + "Anton Dyadyuk ", +] +license = "MIT OR Apache-2.0" +edition = "2021" +description = "zkEVM Compiler Integration Testing Framework" + +[[bin]] +name = "compiler-tester" +path = "src/compiler_tester/main.rs" + +[lib] +doctest = false + +[dependencies] +structopt = { version = "0.3", default-features = false } +shell-words = "1.1" +anyhow = "1.0" +colored = "2.0" + +serde = { version = "1.0", features = [ "derive" ] } +serde_json = "1.0" +serde_yaml = "0.9" +md5 = "0.7" +hex = "0.4" +sha3 = "0.10" +ron = "0.8" +regex = "1.7" +glob = "0.3" +semver = { version = "1.0", features = [ "serde" ] } +itertools = "0.10" +rayon = "1.6" +lazy_static = "1.4" +bincode = "1.3" +web3 = { version= "0.18.0", default-features = false, features = ["http-rustls-tls", "test", "signing"] } + +zkevm-assembly = { git = "https://github.com/matter-labs/era-zkEVM-assembly", branch = "main" } +zkevm_opcode_defs = { git = "https://github.com/matter-labs/era-zkevm_opcode_defs", branch = "main" } +zkevm_tester = { git = "https://github.com/matter-labs/era-zkevm_tester", branch = "v1.3.1" } + +compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", rev = "a6c5b02e4f149f82f1c3821a6f258363308abd2a" } +compiler-llvm-context = { git = "https://github.com/matter-labs/era-compiler-llvm-context", rev = "a1e29ba41fc081ff67d967c11d5e110d38e5b1ac" } +compiler-solidity = { git = "https://github.com/matter-labs/era-compiler-solidity", tag = "v1.3.6" } +compiler-vyper = { git = "https://github.com/matter-labs/era-compiler-vyper", tag = "v1.3.3" } + +solidity-adapter = { path = "../solidity_adapter" } +benchmark-analyzer = { path = "../benchmark_analyzer" } + +[dependencies.tokio] +version = "1.25" +default-features = false +features = ["rt-multi-thread"] + +[dependencies.reqwest] +version = "0.11" +default-features = false +features = ["blocking"] + +[dependencies.inkwell] +git = "https://github.com/matter-labs-forks/inkwell" +branch = "llvm-15" +default-features = false +features = ["llvm15-0", "no-libffi-linking", "target-syncvm"] diff --git a/compiler_tester/src/compiler_tester/arguments.rs b/compiler_tester/src/compiler_tester/arguments.rs new file mode 100644 index 00000000..d3ad35c5 --- /dev/null +++ b/compiler_tester/src/compiler_tester/arguments.rs @@ -0,0 +1,91 @@ +//! +//! The compiler tester arguments. +//! + +use std::path::PathBuf; + +use structopt::StructOpt; + +/// +/// The compiler tester arguments. +/// +#[derive(Debug, StructOpt)] +#[structopt( + name = "compiler-tester", + about = "zkEVM Compiler Integration Testing Framework" +)] +pub struct Arguments { + /// The logging level. + #[structopt(short = "v", long = "verbose")] + pub verbosity: bool, + + /// Suppresses the output completely. + #[structopt(short = "q", long = "quiet")] + pub quiet: bool, + + /// Saves all IRs produced by compilers to `./debug/` directory. + #[structopt(short = "D", long = "debug")] + pub debug: bool, + + /// Saves all JSON traces produced by VM to `./trace/` directory. + /// If passed twice, dumps zkEVM instructions and registers to the terminal. + #[structopt(short = "T", long = "trace", parse(from_occurrences))] + pub trace: usize, + + /// Runs tests only in modes that contain any string from the specified ones. + #[structopt(short = "m", long = "mode")] + pub modes: Vec, + + /// Runs only tests whose name contains any string from the specified ones. + #[structopt(short = "p", long = "path")] + pub paths: Vec, + + /// Runs only tests from the specified groups. + #[structopt(short = "g", long = "group")] + pub groups: Vec, + + /// The benchmark output path, if requested. + #[structopt(short = "b", long = "benchmark")] + pub benchmark: Option, + + /// Sets the number of threads, which execute the tests concurrently. + #[structopt(short = "t", long = "threads")] + pub threads: Option, + + /// Whether to dump the debug data for system contracts. + #[structopt(long = "dump-system")] + pub dump_system: bool, + + /// Whether the deployer should be disabled. + #[structopt(long = "disable-deployer")] + pub disable_deployer: bool, + + /// Whether the msg.value simulator should be disabled. + #[structopt(long = "disable-value-simulator")] + pub disable_value_simulator: bool, + + /// Path to the default `solc` binaries download configuration file. + #[structopt(long = "solc-bin-config-path")] + pub solc_bin_config_path: Option, + + /// Path to the default `vyper` binaries download configuration file. + #[structopt(long = "vyper-bin-config-path")] + pub vyper_bin_config_path: Option, + + /// Whether to load the system contracts builds from the specified file. + #[structopt(long = "load-system-contracts")] + pub load_system_contracts: Option, + + /// Whether to save the system contracts builds to the specified file. + #[structopt(long = "save-system-contracts")] + pub save_system_contracts: Option, +} + +impl Arguments { + /// + /// A shortcut constructor. + /// + pub fn new() -> Self { + Self::from_args() + } +} diff --git a/compiler_tester/src/compiler_tester/main.rs b/compiler_tester/src/compiler_tester/main.rs new file mode 100644 index 00000000..99bf063c --- /dev/null +++ b/compiler_tester/src/compiler_tester/main.rs @@ -0,0 +1,166 @@ +//! +//! The compiler tester binary. +//! + +pub(crate) mod arguments; + +use std::path::PathBuf; +use std::str::FromStr; +use std::time::Instant; + +use colored::Colorize; + +use self::arguments::Arguments; + +/// +/// The application entry point. +/// +fn main() { + match main_inner(Arguments::new()) { + Ok(()) => std::process::exit(0), + Err(error) => { + eprintln!("{error:?}"); + std::process::exit(1) + } + } +} + +/// +/// The entry point wrapper used for proper error handling. +/// +fn main_inner(arguments: Arguments) -> anyhow::Result<()> { + println!( + " {} {} v{} (LLVM build {})", + "Starting".bright_green().bold(), + env!("CARGO_PKG_DESCRIPTION"), + env!("CARGO_PKG_VERSION"), + inkwell::support::get_commit_id().to_string(), + ); + + let debug_config = if arguments.debug { + std::fs::create_dir_all(compiler_tester::DEBUG_DIRECTORY)?; + Some(compiler_llvm_context::DebugConfig::new(PathBuf::from_str( + compiler_tester::DEBUG_DIRECTORY, + )?)) + } else { + None + }; + + if arguments.trace > 0 { + std::fs::create_dir_all(compiler_tester::TRACE_DIRECTORY)?; + } + zkevm_tester::runners::compiler_tests::set_tracing_mode( + zkevm_tester::runners::compiler_tests::VmTracingOptions::from_u64(arguments.trace as u64), + ); + zkevm_assembly::set_encoding_mode(zkevm_assembly::RunningVmEncodingMode::Testing); + + if let Some(threads) = arguments.threads { + rayon::ThreadPoolBuilder::new() + .num_threads(threads) + .build_global() + .expect("Thread pool configuration failure"); + } + + let summary = compiler_tester::Summary::new(arguments.verbosity, arguments.quiet).wrap(); + + inkwell::support::enable_llvm_pretty_stack_trace(); + compiler_llvm_context::initialize_target(); + + let filters = compiler_tester::Filters::new(arguments.paths, arguments.modes, arguments.groups); + let system_contract_debug_config = if arguments.dump_system { + debug_config.clone() + } else { + None + }; + + let compiler_tester = compiler_tester::CompilerTester::new( + summary.clone(), + filters, + debug_config, + vec![ + arguments + .solc_bin_config_path + .unwrap_or_else(|| PathBuf::from("./configs/solc-bin-default.json")), + arguments + .vyper_bin_config_path + .unwrap_or_else(|| PathBuf::from("./configs/vyper-bin-default.json")), + ], + PathBuf::from("./configs/solc-bin-system-contracts.json"), + system_contract_debug_config, + arguments.load_system_contracts, + arguments.save_system_contracts, + )?; + + let run_time_start = Instant::now(); + println!( + " {} tests with {} worker threads", + "Running".bright_green().bold(), + rayon::current_num_threads(), + ); + + match ( + arguments.disable_deployer, + arguments.disable_value_simulator, + ) { + (true, true) => compiler_tester.run::()?, + (true, false) => compiler_tester.run::()?, + (false, true) => compiler_tester.run::()?, + (false, false) => compiler_tester.run::()?, + } + + let summary = compiler_tester::Summary::unwrap_arc(summary); + print!("{summary}"); + println!( + " {} running tests in {}m{:02}s", + "Finished".bright_green().bold(), + run_time_start.elapsed().as_secs() / 60, + run_time_start.elapsed().as_secs() % 60, + ); + + if let Some(path) = arguments.benchmark { + let benchmark = summary.benchmark(); + benchmark.write_to_file(path)?; + } + + if !summary.is_successful() { + anyhow::bail!(""); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_manually() { + zkevm_tester::runners::compiler_tests::set_tracing_mode( + zkevm_tester::runners::compiler_tests::VmTracingOptions::ManualVerbose, + ); + + let arguments = Arguments { + verbosity: false, + quiet: false, + trace: 2, + modes: vec!["Y+M3I+B3 0.8.17".to_owned()], + paths: vec![ + "tests/solidity/complex/solidity_by_example/simple/import/test.json".to_owned(), + ], + groups: vec![], + benchmark: None, + threads: Some(1), + llvm_options: None, + dump_system: false, + debug_output_directory: None, + disable_deployer: false, + disable_value_simulator: false, + solc_bin_config_path: None, + vyper_bin_config_path: None, + load_system_contracts: None, + save_system_contracts: None, + }; + + main_inner(arguments).expect("Manual testing failed"); + } +} diff --git a/compiler_tester/src/compilers/build.rs b/compiler_tester/src/compilers/build.rs new file mode 100644 index 00000000..351bc984 --- /dev/null +++ b/compiler_tester/src/compilers/build.rs @@ -0,0 +1,105 @@ +//! +//! The zkEVM contract build. +//! + +use std::str::FromStr; + +use serde::Deserialize; +use serde::Serialize; + +/// +/// The zkEVM contract build. +/// +#[derive(Debug, Clone)] +pub struct Build { + /// The contract assembly. + pub assembly: zkevm_assembly::Assembly, + /// The bytecode hash. + pub bytecode_hash: web3::types::U256, +} + +impl Build { + /// + /// A shortcut constructor. + /// + pub fn new(assembly: zkevm_assembly::Assembly) -> anyhow::Result { + let bytecode: Vec<[u8; compiler_common::BYTE_LENGTH_FIELD]> = assembly + .clone() + .compile_to_bytecode() + .map_err(|error| anyhow::anyhow!("Compiling to bytecode error: {}", error))?; + let bytecode_hash = + zkevm_assembly::zkevm_opcode_defs::bytecode_to_code_hash(bytecode.as_slice()) + .map(hex::encode) + .map_err(|_error| anyhow::anyhow!("Bytecode hash computation error"))?; + let bytecode_hash = web3::types::U256::from_str(bytecode_hash.as_str()) + .map_err(|error| anyhow::anyhow!("Contract hash is invalid: {}", error))?; + + Ok(Self { + assembly, + bytecode_hash, + }) + } + + /// + /// A shortcut constructor. + /// + pub fn new_with_hash( + assembly: zkevm_assembly::Assembly, + bytecode_hash: String, + ) -> anyhow::Result { + let bytecode_hash = web3::types::U256::from_str(bytecode_hash.as_str()) + .map_err(|error| anyhow::anyhow!("Bytecode hash is invalid: {}", error))?; + + Ok(Self { + assembly, + bytecode_hash, + }) + } +} + +/// +/// The helper struct for Build serialization/deserialization. +/// +#[derive(Serialize, Deserialize)] +struct BuildHelper { + /// The bytecode hash. + bytecode_hash: web3::types::U256, + /// The contract assembly string. + assembly: String, + /// The contract metadata hash. + metadata_hash: [u8; 32], +} + +impl Serialize for Build { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + BuildHelper { + bytecode_hash: self.bytecode_hash, + assembly: self.assembly.assembly_code.clone(), + metadata_hash: self.assembly.metadata_hash, + } + .serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Build { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Deserialize::deserialize(deserializer).and_then(|helper: BuildHelper| { + Ok(Self { + bytecode_hash: helper.bytecode_hash, + assembly: zkevm_assembly::Assembly::from_string( + helper.assembly, + helper.metadata_hash, + ) + .map_err(|error| { + serde::de::Error::custom(format!("Assembly deserialization error: {error}")) + })?, + }) + }) + } +} diff --git a/compiler_tester/src/compilers/cache/mod.rs b/compiler_tester/src/compilers/cache/mod.rs new file mode 100644 index 00000000..b030e088 --- /dev/null +++ b/compiler_tester/src/compilers/cache/mod.rs @@ -0,0 +1,106 @@ +//! +//! The cache of compiled tests. +//! + +pub mod value; + +use std::collections::HashMap; +use std::hash::Hash; +use std::sync::Arc; +use std::sync::Condvar; +use std::sync::Mutex; +use std::sync::RwLock; +use std::sync::RwLockReadGuard; + +use self::value::Value; + +/// +/// The cache of compiled tests. +/// +pub struct Cache +where + K: Eq + Hash, +{ + /// The cache inner data structure. + inner: RwLock>>, +} + +impl Cache +where + K: Eq + Hash, +{ + /// + /// Creates an empty cache instance. + /// + pub fn new() -> Self { + Self { + inner: RwLock::new(HashMap::new()), + } + } + + /// + /// Start computing the cache value, returns a write lock. + /// + pub fn start(&self, key: K) -> Option, Condvar)>> { + let mut inner = self.inner.write().expect("Sync"); + + if inner.contains_key(&key) { + return None; + } + + let waiter = Value::::waiter(); + + inner.insert(key, Value::Waiter(waiter.clone())); + + Some(waiter) + } + + /// + /// Waits until value will be computed. + /// + pub fn wait(&self, key: &K) { + let waiter = if let Value::Waiter(waiter) = self.read().get(key).expect("Always valid") { + waiter.clone() + } else { + return; + }; + let _guard = waiter.1.wait(waiter.0.lock().expect("Sync")); + } + + /// + /// Finishes computing the cache value, returns a write lock. + /// + /// # Panics + /// + /// If the value is not being computed. + /// + pub fn finish(&self, key: K, value: V, waiter: Arc<(Mutex<()>, Condvar)>) { + let mut inner = self.inner.write().expect("Sync"); + + assert!( + matches!( + inner + .insert(key, Value::Value(value)) + .expect("The value is not being computed"), + Value::Waiter(_) + ), + "The value is already computed" + ); + + waiter.1.notify_all(); + } + + /// + /// Checks if value for the key is cached. + /// + pub fn contains(&self, key: &K) -> bool { + self.inner.read().expect("Sync").contains_key(key) + } + + /// + /// Locks the cache for reading. + /// + pub fn read(&self) -> RwLockReadGuard>> { + self.inner.read().expect("Sync") + } +} diff --git a/compiler_tester/src/compilers/cache/value.rs b/compiler_tester/src/compilers/cache/value.rs new file mode 100644 index 00000000..fca52908 --- /dev/null +++ b/compiler_tester/src/compilers/cache/value.rs @@ -0,0 +1,40 @@ +//! +//! The compilers cache value. +//! + +use std::sync::Arc; +use std::sync::Condvar; +use std::sync::Mutex; + +/// +/// The compilers cache value. +/// +pub enum Value { + /// The value is being computed. + Waiter(Arc<(Mutex<()>, Condvar)>), + /// The value is already computed. + Value(T), +} + +impl Value { + /// + /// A shortcut waiter constructor. + /// + pub fn waiter() -> Arc<(Mutex<()>, Condvar)> { + Arc::new((Mutex::new(()), Condvar::new())) + } + + /// + /// Unwraps the value. + /// + /// # Panics + /// + /// If the value is being waited for. + /// + pub fn unwrap_value(&self) -> &T { + match self { + Self::Value(value) => value, + _ => panic!("Not a value"), + } + } +} diff --git a/compiler_tester/src/compilers/downloader/config/binary/mod.rs b/compiler_tester/src/compilers/downloader/config/binary/mod.rs new file mode 100644 index 00000000..cd4d7b82 --- /dev/null +++ b/compiler_tester/src/compilers/downloader/config/binary/mod.rs @@ -0,0 +1,24 @@ +//! +//! The compiler downloader binary config. +//! + +pub mod protocol; + +use serde::Deserialize; + +use self::protocol::Protocol; + +/// +/// The compiler downloader binary config. +/// +#[derive(Debug, Deserialize)] +pub struct Binary { + /// Whether downloading the binary is enabled. + pub is_enabled: bool, + /// The downloading protocol. + pub protocol: Protocol, + /// The downloaded data source. + pub source: String, + /// The downloaded binary file destination. + pub destination: String, +} diff --git a/compiler_tester/src/compilers/downloader/config/binary/protocol.rs b/compiler_tester/src/compilers/downloader/config/binary/protocol.rs new file mode 100644 index 00000000..78f15404 --- /dev/null +++ b/compiler_tester/src/compilers/downloader/config/binary/protocol.rs @@ -0,0 +1,22 @@ +//! +//! The compiler downloader binary download protocol. +//! + +use serde::Deserialize; + +/// +/// The compiler downloader binary download protocol. +/// +#[derive(Debug, Deserialize)] +#[allow(clippy::upper_case_acronyms)] +pub enum Protocol { + /// The local file copy. + #[serde(rename = "file")] + File, + /// Download via HTTPS. + #[serde(rename = "https")] + HTTPS, + /// Use the solc-bin JSON list. + #[serde(rename = "solc-bin-list")] + SolcBinList, +} diff --git a/compiler_tester/src/compilers/downloader/config/mod.rs b/compiler_tester/src/compilers/downloader/config/mod.rs new file mode 100644 index 00000000..1f6ffd95 --- /dev/null +++ b/compiler_tester/src/compilers/downloader/config/mod.rs @@ -0,0 +1,49 @@ +//! +//! The compiler downloader config. +//! + +pub mod binary; + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use serde::Deserialize; + +use self::binary::Binary; + +/// +/// The compiler downloader config. +/// +#[derive(Debug, Deserialize)] +pub struct Config { + /// The compiler binaries to download. + pub binaries: BTreeMap, + /// The compiler platform directory names. + pub platforms: Option>, +} + +impl Config { + /// + /// Returns the remote platform directory name for the specified platform. + /// + pub fn get_remote_platform_directory(&self) -> anyhow::Result { + let platforms = match self.platforms { + Some(ref platform) => platform, + None => anyhow::bail!("Platforms are not defined"), + }; + + Ok(if cfg!(target_os = "linux") { + platforms + .get("linux") + .cloned() + .ok_or_else(|| anyhow::anyhow!("Linux platform directory is not defined"))? + } else if cfg!(target_os = "macos") { + platforms + .get("macos") + .cloned() + .ok_or_else(|| anyhow::anyhow!("MacOS platform directory is not defined"))? + } else { + anyhow::bail!("Unsupported platform!") + }) + } +} diff --git a/compiler_tester/src/compilers/downloader/mod.rs b/compiler_tester/src/compilers/downloader/mod.rs new file mode 100644 index 00000000..3f54f95c --- /dev/null +++ b/compiler_tester/src/compilers/downloader/mod.rs @@ -0,0 +1,136 @@ +//! +//! The compiler downloader. +//! + +pub mod config; +pub mod solc_list; + +use std::os::unix::fs::PermissionsExt; +use std::path::Path; +use std::path::PathBuf; +use std::str::FromStr; + +use colored::Colorize; + +use self::config::binary::protocol::Protocol; +use self::config::Config; +use self::solc_list::SolcList; + +/// +/// The compiler downloader. +/// +#[derive(Debug, Default)] +pub struct Downloader { + /// The solc-bin JSON list metadata. + solc_list: Option, +} + +impl Downloader { + /// + /// Downloads the compilers described in the config. + /// + pub fn download(mut self, config_path: &Path) -> anyhow::Result { + let config_file = std::fs::File::open(config_path).map_err(|error| { + anyhow::anyhow!("Binaries download config opening error: {}", error) + })?; + let config_reader = std::io::BufReader::new(config_file); + let config: Config = serde_json::from_reader(config_reader).map_err(|error| { + anyhow::anyhow!("Binaries download config parsing error: {}", error) + })?; + + let platform_directory = config.get_remote_platform_directory()?; + + for (version, binary) in config.binaries.iter() { + if !binary.is_enabled { + continue; + } + + let source_path = binary + .source + .replace("${PLATFORM}", platform_directory.as_str()); + + let destination_path = PathBuf::from_str(binary.destination.as_str()) + .map_err(|_| anyhow::anyhow!("Binary `{}` destination is invalid", source_path))?; + + let data = match binary.protocol { + Protocol::File => { + if source_path == destination_path.to_string_lossy() { + continue; + } + + println!( + " {} binary `{}` => `{}`", + "Copying".bright_green().bold(), + source_path, + binary.destination, + ); + + std::fs::copy(source_path.as_str(), binary.destination.as_str()) + .map_err(|error| anyhow::anyhow!("Binary copying error: {}", error))?; + continue; + } + Protocol::HTTPS => { + if destination_path.exists() { + continue; + } + + let source_url = + reqwest::Url::from_str(source_path.as_str()).expect("Always valid"); + println!( + " {} binary `{}` => `{}`", + "Downloading".bright_green().bold(), + source_url, + binary.destination, + ); + reqwest::blocking::get(source_url)?.bytes()? + } + Protocol::SolcBinList => { + if destination_path.exists() { + continue; + } + + let solc_list_path = PathBuf::from(source_path.as_str()); + let solc_list = self.solc_list.get_or_insert_with(|| { + SolcList::try_from(solc_list_path.as_path()) + .expect("solc-bin JSON list downloading error") + }); + if solc_list.releases.is_empty() { + return Ok(config); + } + + let source_binary_name = + match solc_list.releases.get(version.to_string().as_str()) { + Some(source_binary_name) => source_binary_name, + None => anyhow::bail!( + "Binary for version v{} not found in the solc JSON list", + version + ), + }; + let mut source_path = solc_list_path; + source_path.pop(); + source_path.push(source_binary_name); + + let source_url = + reqwest::Url::from_str(source_path.to_str().expect("Always valid")) + .expect("Always valid"); + println!( + " {} binary `{}` => `{}`", + "Downloading".bright_green().bold(), + source_url, + binary.destination, + ); + reqwest::blocking::get(source_url)?.bytes()? + } + }; + + let mut destination_folder = destination_path.clone(); + destination_folder.pop(); + std::fs::create_dir_all(destination_folder)?; + + std::fs::write(&destination_path, data)?; + std::fs::set_permissions(&destination_path, std::fs::Permissions::from_mode(0o755))?; + } + + Ok(config) + } +} diff --git a/compiler_tester/src/compilers/downloader/solc_list.rs b/compiler_tester/src/compilers/downloader/solc_list.rs new file mode 100644 index 00000000..a40cf009 --- /dev/null +++ b/compiler_tester/src/compilers/downloader/solc_list.rs @@ -0,0 +1,35 @@ +//! +//! The Solidity compiler JSON list metadata. +//! + +use std::collections::BTreeMap; +use std::path::Path; +use std::str::FromStr; + +use colored::Colorize; +use serde::Deserialize; + +/// +/// The Solidity compiler JSON list metadata. +/// +#[derive(Debug, Deserialize)] +pub struct SolcList { + /// The compiler releases. + pub releases: BTreeMap, +} + +impl TryFrom<&Path> for SolcList { + type Error = anyhow::Error; + + fn try_from(path: &Path) -> Result { + let url = + reqwest::Url::from_str(path.to_str().expect("Always valid")).expect("Always valid"); + println!( + " {} solc-bin JSON `{}`", + "Downloading".bright_green().bold(), + url + ); + let list: SolcList = reqwest::blocking::get(url)?.json()?; + Ok(list) + } +} diff --git a/compiler_tester/src/compilers/llvm.rs b/compiler_tester/src/compilers/llvm.rs new file mode 100644 index 00000000..1b0ad419 --- /dev/null +++ b/compiler_tester/src/compilers/llvm.rs @@ -0,0 +1,152 @@ +//! +//! The LLVM compiler. +//! + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use super::build::Build as zkEVMContractBuild; +use super::mode::llvm::Mode as LLVMMode; +use super::mode::Mode; +use super::Compiler; + +/// +/// The LLVM compiler. +/// +pub struct LLVMCompiler { + /// The name-to-code source files mapping. + sources: Vec<(String, String)>, + /// The compiler debug config. + debug_config: Option, +} + +lazy_static::lazy_static! { + /// + /// The LLVM compiler supported modes. + /// + static ref MODES: Vec = { + compiler_llvm_context::OptimizerSettings::combinations() + .into_iter() + .map(|llvm_optimizer_settings| LLVMMode::new(llvm_optimizer_settings).into()) + .collect::>() + }; +} + +impl LLVMCompiler { + /// + /// Compiles the source. + /// + fn compile_source( + &self, + source_code: &str, + name: &str, + mode: &LLVMMode, + ) -> anyhow::Result { + let target_machine = + compiler_llvm_context::TargetMachine::new(&mode.llvm_optimizer_settings)?; + + let llvm = inkwell::context::Context::create(); + let memory_buffer = inkwell::memory_buffer::MemoryBuffer::create_from_memory_range_copy( + source_code.as_bytes(), + name, + ); + let module = llvm + .create_module_from_ir(memory_buffer) + .map_err(|error| anyhow::anyhow!(error.to_string()))?; + + if let Some(ref debug_config) = self.debug_config { + debug_config.dump_llvm_ir_unoptimized(name, &module)?; + } + module + .verify() + .map_err(|error| anyhow::anyhow!("Unoptimized LLVM IR verification: {}", error))?; + + let optimizer = compiler_llvm_context::Optimizer::new( + target_machine, + mode.llvm_optimizer_settings.clone(), + ); + optimizer + .run(&module) + .map_err(|error| anyhow::anyhow!(error.to_string()))?; + if let Some(ref debug_config) = self.debug_config { + debug_config.dump_llvm_ir_optimized(name, &module)?; + } + module + .verify() + .map_err(|error| anyhow::anyhow!("Optimized LLVM IR verification: {}", error))?; + + let assembly_text = match optimizer.target_machine().write_to_memory_buffer(&module) { + Ok(assembly) => String::from_utf8_lossy(assembly.as_slice()).to_string(), + Err(error) => { + anyhow::bail!("LLVM module compiling: {}", error); + } + }; + + if let Some(ref debug_config) = self.debug_config { + debug_config.dump_assembly(name, assembly_text.as_str())?; + } + + let assembly = zkevm_assembly::Assembly::try_from(assembly_text) + .map_err(|error| anyhow::anyhow!(error))?; + + zkEVMContractBuild::new(assembly) + } +} + +impl Compiler for LLVMCompiler { + fn new( + sources: Vec<(String, String)>, + _libraries: BTreeMap>, + debug_config: Option, + _is_system_mode: bool, + ) -> Self { + Self { + sources, + debug_config, + } + } + + fn modes() -> Vec { + MODES.clone() + } + + fn compile( + &self, + mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result> { + let mode = LLVMMode::unwrap(mode); + self.sources + .iter() + .map(|(path, source)| { + self.compile_source(source, path, mode) + .map(|build| (path.to_owned(), build)) + }) + .collect() + } + + fn last_contract( + &self, + _mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result { + Ok(self + .sources + .last() + .ok_or_else(|| anyhow::anyhow!("Sources is empty"))? + .0 + .clone()) + } + + fn has_many_contracts() -> bool { + false + } + + fn check_pragmas(&self, _mode: &Mode) -> bool { + true + } + + fn check_ethereum_tests_params(_mode: &Mode, _params: &solidity_adapter::Params) -> bool { + true + } +} diff --git a/compiler_tester/src/compilers/mod.rs b/compiler_tester/src/compilers/mod.rs new file mode 100644 index 00000000..5107b230 --- /dev/null +++ b/compiler_tester/src/compilers/mod.rs @@ -0,0 +1,82 @@ +//! +//! The contract compilers for different languages. +//! + +pub mod build; +pub mod cache; +pub mod downloader; +pub mod llvm; +pub mod mode; +pub mod solidity; +pub mod vyper; +pub mod yul; +pub mod zkevm; + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use self::build::Build as zkEVMContractBuild; +use self::mode::Mode; + +/// +/// The compiler trait. +/// +pub trait Compiler: Send + Sync + 'static { + /// + /// The constructor. + /// + fn new( + sources: Vec<(String, String)>, + libraries: BTreeMap>, + debug_config: Option, + is_system_mode: bool, + ) -> Self; + + /// + /// Returns supported compiler modes. + /// + fn modes() -> Vec; + + /// + /// Compile all the sources. + /// + fn compile( + &self, + mode: &Mode, + is_system_contracts_mode: bool, + ) -> anyhow::Result>; + + /// + /// Returns selector by entry. + /// + fn selector( + &self, + _mode: &Mode, + _contract_path: &str, + entry: &str, + _is_system_contracts_mode: bool, + ) -> anyhow::Result { + u32::from_str_radix(entry, compiler_common::BASE_HEXADECIMAL) + .map_err(|err| anyhow::anyhow!("Invalid entry value: {}", err)) + } + + /// + /// Returns the last contract name. + /// + fn last_contract(&self, mode: &Mode, is_system_contracts_mode: bool) -> anyhow::Result; + + /// + /// Returns true if the one source file can contains many contracts, false otherwise. + /// + fn has_many_contracts() -> bool; + + /// + /// Checks the versions in the source code pragmas. + /// + fn check_pragmas(&self, _mode: &Mode) -> bool; + + /// + /// Checks the Ethereum tests params compatability. + /// + fn check_ethereum_tests_params(_mode: &Mode, _params: &solidity_adapter::Params) -> bool; +} diff --git a/compiler_tester/src/compilers/mode/llvm.rs b/compiler_tester/src/compilers/mode/llvm.rs new file mode 100644 index 00000000..2fd95e0f --- /dev/null +++ b/compiler_tester/src/compilers/mode/llvm.rs @@ -0,0 +1,48 @@ +//! +//! The compiler tester LLVM mode. +//! + +use super::Mode as ModeWrapper; + +/// +/// The compiler tester LLVM mode. +/// +#[derive(Debug, Clone)] +pub struct Mode { + /// The optimizer settings. + pub llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings, +} + +impl Mode { + /// The language name. + pub const LANGUAGE: &'static str = "LLVM"; + + /// + /// A shortcut constructor. + /// + pub fn new(llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings) -> Self { + Self { + llvm_optimizer_settings, + } + } + + /// + /// Unwrap mode. + /// + /// # Panics + /// + /// Will panic if the inner is non-LLVM mode. + /// + pub fn unwrap(mode: &ModeWrapper) -> &Self { + match mode { + ModeWrapper::LLVM(mode) => mode, + _ => panic!("Non-llvm mode"), + } + } +} + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:>8} {}", Self::LANGUAGE, self.llvm_optimizer_settings,) + } +} diff --git a/compiler_tester/src/compilers/mode/mod.rs b/compiler_tester/src/compilers/mode/mod.rs new file mode 100644 index 00000000..36116df3 --- /dev/null +++ b/compiler_tester/src/compilers/mode/mod.rs @@ -0,0 +1,90 @@ +//! +//! The compiler mode. +//! + +pub mod llvm; +pub mod solidity; +pub mod vyper; +pub mod yul; +pub mod zkevm; + +use self::llvm::Mode as LLVMMode; +use self::solidity::Mode as SolidityMode; +use self::vyper::Mode as VyperMode; +use self::yul::Mode as YulMode; +use self::zkevm::Mode as zkEVMMode; + +/// +/// The compiler mode. +/// +#[derive(Debug, Clone)] +#[allow(non_camel_case_types)] +#[allow(clippy::upper_case_acronyms)] +pub enum Mode { + /// The `Yul` mode. + Yul(YulMode), + /// The `Solidity` mode. + Solidity(SolidityMode), + /// The `LLVM` mode. + LLVM(LLVMMode), + /// The `zkEVM` mode. + zkEVM(zkEVMMode), + /// The `Vyper` mode. + Vyper(VyperMode), +} + +impl Mode { + /// + /// Checks if the self is compatible with version filter. + /// + pub fn check_version(&self, versions: &semver::VersionReq) -> bool { + let version = match self { + Mode::Solidity(mode) => &mode.solc_version, + Mode::Vyper(mode) => &mode.vyper_version, + _ => return false, + }; + versions.matches(version) + } +} + +impl From for Mode { + fn from(inner: YulMode) -> Self { + Self::Yul(inner) + } +} + +impl From for Mode { + fn from(inner: SolidityMode) -> Self { + Self::Solidity(inner) + } +} + +impl From for Mode { + fn from(inner: LLVMMode) -> Self { + Self::LLVM(inner) + } +} + +impl From for Mode { + fn from(inner: zkEVMMode) -> Self { + Self::zkEVM(inner) + } +} + +impl From for Mode { + fn from(inner: VyperMode) -> Self { + Self::Vyper(inner) + } +} + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Yul(inner) => write!(f, "{inner}"), + Self::Solidity(inner) => write!(f, "{inner}"), + Self::LLVM(inner) => write!(f, "{inner}"), + Self::zkEVM(inner) => write!(f, "{inner}"), + Self::Vyper(inner) => write!(f, "{inner}"), + } + } +} diff --git a/compiler_tester/src/compilers/mode/solidity.rs b/compiler_tester/src/compilers/mode/solidity.rs new file mode 100644 index 00000000..3e498a10 --- /dev/null +++ b/compiler_tester/src/compilers/mode/solidity.rs @@ -0,0 +1,73 @@ +//! +//! The compiler tester Solidity mode. +//! + +use super::Mode as ModeWrapper; + +/// +/// The compiler tester Solidity mode. +/// +#[derive(Debug, Clone)] +pub struct Mode { + /// The Solidity compiler version. + pub solc_version: semver::Version, + /// The Solidity compiler output type. + pub solc_pipeline: compiler_solidity::SolcPipeline, + /// Whether to run the Solidity compiler optimizer. + pub solc_optimize: bool, + /// The optimizer settings. + pub llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings, +} + +impl Mode { + /// The language name. + pub const LANGUAGE: &'static str = "Solidity"; + + /// + /// A shortcut constructor. + /// + pub fn new( + solc_version: semver::Version, + solc_pipeline: compiler_solidity::SolcPipeline, + solc_optimize: bool, + llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings, + ) -> Self { + Self { + solc_version, + solc_pipeline, + solc_optimize, + llvm_optimizer_settings, + } + } + + /// + /// Unwrap mode. + /// + /// # Panics + /// + /// Will panic if the inner is non-Solidity mode. + /// + pub fn unwrap(mode: &ModeWrapper) -> &Self { + match mode { + ModeWrapper::Solidity(mode) => mode, + _ => panic!("Non-Solidity mode"), + } + } +} + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{:>8} {}{}{} {}", + Self::LANGUAGE, + match self.solc_pipeline { + compiler_solidity::SolcPipeline::Yul => "Y", + compiler_solidity::SolcPipeline::EVMLA => "E", + }, + if self.solc_optimize { '+' } else { '-' }, + self.llvm_optimizer_settings, + self.solc_version, + ) + } +} diff --git a/compiler_tester/src/compilers/mode/vyper.rs b/compiler_tester/src/compilers/mode/vyper.rs new file mode 100644 index 00000000..b0a413c3 --- /dev/null +++ b/compiler_tester/src/compilers/mode/vyper.rs @@ -0,0 +1,65 @@ +//! +//! The compiler tester Vyper mode. +//! + +use super::Mode as ModeWrapper; + +/// +/// The compiler tester Vyper mode. +/// +#[derive(Debug, Clone)] +pub struct Mode { + /// The Vyper compiler version. + pub vyper_version: semver::Version, + /// Whether to run the Vyper compiler optimizer. + pub vyper_optimize: bool, + /// The optimizer settings. + pub llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings, +} + +impl Mode { + /// The language name. + pub const LANGUAGE: &'static str = "Vyper"; + + /// + /// A shortcut constructor. + /// + pub fn new( + vyper_version: semver::Version, + vyper_optimize: bool, + llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings, + ) -> Self { + Self { + vyper_version, + vyper_optimize, + llvm_optimizer_settings, + } + } + + /// + /// Unwrap mode. + /// + /// # Panics + /// + /// Will panic if the inner is non-Vyper mode. + /// + pub fn unwrap(mode: &ModeWrapper) -> &Self { + match mode { + ModeWrapper::Vyper(mode) => mode, + _ => panic!("Non-Vyper mode"), + } + } +} + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{:>8} V{}{} {}", + Self::LANGUAGE, + if self.vyper_optimize { '+' } else { '-' }, + self.llvm_optimizer_settings, + self.vyper_version, + ) + } +} diff --git a/compiler_tester/src/compilers/mode/yul.rs b/compiler_tester/src/compilers/mode/yul.rs new file mode 100644 index 00000000..b94114ce --- /dev/null +++ b/compiler_tester/src/compilers/mode/yul.rs @@ -0,0 +1,48 @@ +//! +//! The compiler tester Yul mode. +//! + +use super::Mode as ModeWrapper; + +/// +/// The compiler tester Yul mode. +/// +#[derive(Debug, Clone)] +pub struct Mode { + /// The optimizer settings. + pub llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings, +} + +impl Mode { + /// The language name. + pub const LANGUAGE: &'static str = "Yul"; + + /// + /// A shortcut constructor. + /// + pub fn new(llvm_optimizer_settings: compiler_llvm_context::OptimizerSettings) -> Self { + Self { + llvm_optimizer_settings, + } + } + + /// + /// Unwrap mode. + /// + /// # Panics + /// + /// Will panic if the inner is non-Yul mode. + /// + pub fn unwrap(mode: &ModeWrapper) -> &Self { + match mode { + ModeWrapper::Yul(mode) => mode, + _ => panic!("Non-Yul mode"), + } + } +} + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:>8} {}", Self::LANGUAGE, self.llvm_optimizer_settings,) + } +} diff --git a/compiler_tester/src/compilers/mode/zkevm.rs b/compiler_tester/src/compilers/mode/zkevm.rs new file mode 100644 index 00000000..d0c04a9a --- /dev/null +++ b/compiler_tester/src/compilers/mode/zkevm.rs @@ -0,0 +1,20 @@ +//! +//! The compiler tester zkEVM mode. +//! + +/// +/// The compiler tester zkEVM mode. +/// +#[derive(Debug, Default, Clone)] +pub struct Mode {} + +impl Mode { + /// The language name. + pub const LANGUAGE: &'static str = "zkEVM"; +} + +impl std::fmt::Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:>8}", "zkEVM") + } +} diff --git a/compiler_tester/src/compilers/solidity/cached_project.rs b/compiler_tester/src/compilers/solidity/cached_project.rs new file mode 100644 index 00000000..c571ac37 --- /dev/null +++ b/compiler_tester/src/compilers/solidity/cached_project.rs @@ -0,0 +1,34 @@ +//! +//! The Solidity compiler cached projects. +//! + +use std::collections::BTreeMap; + +/// +/// The Solidity compiler cached projects. +/// +pub struct CachedProject { + /// The Solidity project. + pub project: compiler_solidity::Project, + /// The method identifiers. + pub method_identifiers: BTreeMap>, + /// The last contract name. + pub last_contract: String, +} + +impl CachedProject { + /// + /// A shortcut constructor. + /// + pub fn new( + project: compiler_solidity::Project, + method_identifiers: BTreeMap>, + last_contract: String, + ) -> Self { + Self { + project, + method_identifiers, + last_contract, + } + } +} diff --git a/compiler_tester/src/compilers/solidity/mod.rs b/compiler_tester/src/compilers/solidity/mod.rs new file mode 100644 index 00000000..3a579ac0 --- /dev/null +++ b/compiler_tester/src/compilers/solidity/mod.rs @@ -0,0 +1,488 @@ +//! +//! The Solidity compiler wrapper. +//! + +pub mod cached_project; +pub mod subprocess_mode; + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::path::Path; + +use itertools::Itertools; + +use super::build::Build as zkEVMContractBuild; +use super::cache::Cache; +use super::mode::solidity::Mode as SolidityMode; +use super::mode::Mode; +use super::Compiler; + +use self::cached_project::CachedProject; +use self::subprocess_mode::SubprocessMode; + +/// +/// The Solidity compiler wrapper. +/// +pub struct SolidityCompiler { + /// The name-to-code source files mapping. + sources: Vec<(String, String)>, + /// The `solc` process output cache. + cache: Cache>, + /// The libraries addresses. + libraries: BTreeMap>, + /// The debug config. + debug_config: Option, + /// The system mode flag. + is_system_mode: bool, +} + +lazy_static::lazy_static! { + /// + /// The Solidity compiler supported modes. + /// + /// All compilers must be downloaded before initialization. + /// + static ref MODES: Vec = { + let mut solc_pipeline_versions = Vec::new(); + for pipeline in [ + compiler_solidity::SolcPipeline::Yul, + compiler_solidity::SolcPipeline::EVMLA, + ] { + for version in SolidityCompiler::all_versions(pipeline).expect("`solc` versions analysis error") { + solc_pipeline_versions.push((pipeline, version)) + } + } + + compiler_llvm_context::OptimizerSettings::combinations() + .into_iter() + .cartesian_product(solc_pipeline_versions) + .cartesian_product(vec![false, true]) + .map( + |((llvm_optimizer_settings, (solc_pipeline, solc_version)), solc_optimize)| { + SolidityMode::new( + solc_version, + solc_pipeline, + solc_optimize, + llvm_optimizer_settings, + ) + .into() + }, + ) + .collect::>() + }; +} + +impl SolidityCompiler { + /// The compiler binaries directory. + const DIRECTORY: &'static str = "solc-bin/"; + + /// The solc allow paths argument value. + const SOLC_ALLOW_PATHS: &'static str = "tests"; + + /// + /// Returns the `solc` compiler path by version. + /// + fn get_solc_by_version(version: &semver::Version) -> compiler_solidity::SolcCompiler { + compiler_solidity::SolcCompiler::new(format!("{}/solc-{}", Self::DIRECTORY, version)) + } + + /// + /// Returns the system contract `solc` compiler path. + /// + fn get_system_contract_solc() -> compiler_solidity::SolcCompiler { + compiler_solidity::SolcCompiler::new(format!("{}/solc-system-contracts", Self::DIRECTORY)) + } + + /// + /// Returns the compiler versions downloaded for the specified compilation pipeline. + /// + fn all_versions( + pipeline: compiler_solidity::SolcPipeline, + ) -> anyhow::Result> { + let mut versions = Vec::new(); + for entry in std::fs::read_dir("./solc-bin/")? { + let entry = entry?; + let path = entry.path(); + let entry_type = entry.file_type().map_err(|error| { + anyhow::anyhow!( + "File `{}` type getting error: {}", + path.to_string_lossy(), + error + ) + })?; + if !entry_type.is_file() { + anyhow::bail!( + "Invalid `solc` binary file type: {}", + path.to_string_lossy() + ); + } + + let file_name = entry.file_name().to_string_lossy().to_string(); + let version_str = match file_name.strip_prefix("solc-") { + Some(version_str) => version_str, + None => continue, + }; + let version: semver::Version = match version_str.parse() { + Ok(version) => version, + Err(_) => continue, + }; + if compiler_solidity::SolcPipeline::Yul == pipeline && version.minor < 8 { + continue; + } + versions.push(version); + } + Ok(versions) + } + + /// + /// Processes a project and stores its representation in the cache. + /// + fn compute_cache(&self, subprocess_mode: &SubprocessMode, is_system_contracts_mode: bool) { + if !self.cache.contains(subprocess_mode) { + if let Some(waiter) = self.cache.start(subprocess_mode.clone()) { + self.cache.finish( + subprocess_mode.clone(), + self.get_cached_project(subprocess_mode, is_system_contracts_mode), + waiter, + ); + }; + } + } + + /// + /// Processes a project and returns its representation for the cache. + /// + fn get_cached_project( + &self, + subprocess_mode: &SubprocessMode, + is_system_contracts_mode: bool, + ) -> anyhow::Result { + let solc = if is_system_contracts_mode { + Self::get_system_contract_solc() + } else { + Self::get_solc_by_version(&subprocess_mode.version) + }; + + let output_selection = + compiler_solidity::SolcStandardJsonInputSettingsSelection::new_required( + subprocess_mode.pipeline, + ); + + let optimizer = compiler_solidity::SolcStandardJsonInputSettingsOptimizer::new( + subprocess_mode.optimize, + None, + ); + + let solc_input = compiler_solidity::SolcStandardJsonInput::try_from_sources( + self.sources.clone().into_iter().collect(), + self.libraries.clone(), + output_selection, + optimizer, + ) + .map_err(|error| anyhow::anyhow!("Failed to build solc input standard json: {}", error))?; + + let allow_paths = Path::new(Self::SOLC_ALLOW_PATHS) + .canonicalize() + .expect("Always valid") + .to_string_lossy() + .to_string(); + + let mut solc_output = solc.standard_json(solc_input, None, vec![], Some(allow_paths))?; + + if let Some(errors) = solc_output.errors.as_deref() { + let mut has_errors = false; + let mut error_messages = Vec::with_capacity(errors.len()); + + for error in errors.iter() { + if error.severity.as_str() == "error" { + has_errors = true; + error_messages.push(error.formatted_message.to_owned()); + } + } + + if has_errors { + anyhow::bail!("Errors found: {:?}", error_messages); + } + } + + let last_contract = solc_output + .sources + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Sources not found in the output")) + .and_then(|sources| { + for (path, _source) in self.sources.iter().rev() { + match sources + .get(path) + .ok_or_else(|| anyhow::anyhow!("Last source not found in the output"))? + .ast + .as_ref() + .ok_or_else(|| anyhow::anyhow!("AST not found for the last source"))? + .last_contract_name() + { + Ok(name) => return Ok(format!("{path}:{name}")), + Err(_error) => continue, + } + } + anyhow::bail!("Last contract not found in all contracts") + }) + .map_err(|error| { + anyhow::anyhow!( + "Failed to get the last contract: {}, output errors: {:?}", + error, + solc_output.errors + ) + })?; + + let files = solc_output + .contracts + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Contracts not found in the output"))?; + + let mut method_identifiers = BTreeMap::new(); + for (path, contracts) in files.iter() { + for (name, contract) in contracts.iter() { + let mut contract_identifiers = BTreeMap::new(); + for (entry, selector) in contract + .evm + .as_ref() + .ok_or_else(|| anyhow::anyhow!("EVM for contract {}:{} not found", path, name))? + .method_identifiers + .as_ref() + .ok_or_else(|| { + anyhow::anyhow!( + "Method identifiers for contract {}:{} not found", + path, + name + ) + })? + .iter() + { + contract_identifiers.insert(entry.clone(), selector.clone()); + } + method_identifiers.insert(format!("{path}:{name}"), contract_identifiers); + } + } + + let project = solc_output.try_to_project( + self.sources + .clone() + .into_iter() + .collect::>(), + self.libraries.clone(), + subprocess_mode.pipeline, + &subprocess_mode.version, + self.debug_config.as_ref(), + )?; + + Ok(CachedProject::new( + project, + method_identifiers, + last_contract, + )) + } +} + +impl Compiler for SolidityCompiler { + fn new( + sources: Vec<(String, String)>, + libraries: BTreeMap>, + debug_config: Option, + is_system_mode: bool, + ) -> Self { + Self { + sources, + libraries, + cache: Cache::new(), + debug_config, + is_system_mode, + } + } + + fn modes() -> Vec { + MODES.clone() + } + + fn compile( + &self, + mode: &Mode, + is_system_contracts_mode: bool, + ) -> anyhow::Result> { + let mode = SolidityMode::unwrap(mode); + + let subprocess_mode = SubprocessMode::new( + mode.solc_version.clone(), + mode.solc_pipeline, + mode.solc_optimize, + ); + + self.compute_cache(&subprocess_mode, is_system_contracts_mode); + + let project = { + self.cache.wait(&subprocess_mode); + let lock = self.cache.read(); + let cached_project = lock + .get(&subprocess_mode) + .expect("Always valid") + .unwrap_value(); + cached_project + .as_ref() + .map_err(|error| anyhow::anyhow!(error.to_string()))? + .project + .clone() + }; + + let target_machine = + compiler_llvm_context::TargetMachine::new(&mode.llvm_optimizer_settings)?; + + let builds = project + .compile_all( + target_machine, + mode.llvm_optimizer_settings.clone(), + self.is_system_mode, + self.debug_config.clone(), + )? + .contracts + .into_iter() + .map(|(name, contract)| { + Ok(( + name, + zkEVMContractBuild::new_with_hash( + contract.build.assembly, + contract.build.bytecode_hash, + )?, + )) + }) + .collect::>>()?; + + Ok(builds) + } + + fn selector( + &self, + mode: &Mode, + contract_path: &str, + entry: &str, + is_system_contracts_mode: bool, + ) -> anyhow::Result { + let mode = SolidityMode::unwrap(mode); + + let subprocess_mode = SubprocessMode::new( + mode.solc_version.clone(), + mode.solc_pipeline, + mode.solc_optimize, + ); + + self.compute_cache(&subprocess_mode, is_system_contracts_mode); + + self.cache.wait(&subprocess_mode); + let lock = self.cache.read(); + let cached_project = lock + .get(&subprocess_mode) + .expect("Always valid") + .unwrap_value(); + + let method_identifiers = &cached_project + .as_ref() + .map_err(|error| anyhow::anyhow!(error.to_string()))? + .method_identifiers; + + let contract_identifiers = method_identifiers + .get(contract_path) + .ok_or_else(|| anyhow::anyhow!("Contract not found"))?; + + contract_identifiers + .iter() + .find_map(|(name, selector)| { + if name.starts_with(entry) { + Some( + u32::from_str_radix(selector, compiler_common::BASE_HEXADECIMAL).map_err( + |error| { + anyhow::anyhow!( + "Invalid selector from the Solidity compiler: {}", + error + ) + }, + ), + ) + } else { + None + } + }) + .ok_or_else(|| anyhow::anyhow!("Hash of the method `{}` not found", entry))? + } + + fn last_contract(&self, mode: &Mode, is_system_contracts_mode: bool) -> anyhow::Result { + let mode = SolidityMode::unwrap(mode); + + let subprocess_mode = SubprocessMode::new( + mode.solc_version.clone(), + mode.solc_pipeline, + mode.solc_optimize, + ); + + self.compute_cache(&subprocess_mode, is_system_contracts_mode); + + self.cache.wait(&subprocess_mode); + let lock = self.cache.read(); + let cached_project = lock + .get(&subprocess_mode) + .expect("Always valid") + .unwrap_value(); + + cached_project + .as_ref() + .map_err(|error| anyhow::anyhow!(error.to_string())) + .map(|cached_project| cached_project.last_contract.clone()) + } + + fn has_many_contracts() -> bool { + true + } + + fn check_pragmas(&self, mode: &Mode) -> bool { + let mode = SolidityMode::unwrap(mode); + + self.sources.iter().all(|(_, source_code)| { + match source_code.lines().find_map(|line| { + let mut split = line.split_whitespace(); + if let (Some("pragma"), Some("solidity")) = (split.next(), split.next()) { + let version = split.join(",").replace(';', ""); + semver::VersionReq::parse(version.as_str()).ok() + } else { + None + } + }) { + Some(pragma_version_req) => pragma_version_req.matches(&mode.solc_version), + None => true, + } + }) + } + + fn check_ethereum_tests_params(mode: &Mode, params: &solidity_adapter::Params) -> bool { + if !params.evm_version.matches_any(&[ + solidity_adapter::EVM::TangerineWhistle, + solidity_adapter::EVM::SpuriousDragon, + solidity_adapter::EVM::Byzantium, + solidity_adapter::EVM::Constantinople, + solidity_adapter::EVM::Petersburg, + solidity_adapter::EVM::Istanbul, + solidity_adapter::EVM::Berlin, + solidity_adapter::EVM::London, + solidity_adapter::EVM::Paris, + ]) { + return false; + } + + let mode = SolidityMode::unwrap(mode); + + match mode.solc_pipeline { + compiler_solidity::SolcPipeline::Yul => { + params.compile_via_yul != solidity_adapter::CompileViaYul::False + && params.abi_encoder_v1_only != solidity_adapter::ABIEncoderV1Only::True + } + compiler_solidity::SolcPipeline::EVMLA => { + params.compile_via_yul != solidity_adapter::CompileViaYul::True + } + } + } +} diff --git a/compiler_tester/src/compilers/solidity/subprocess_mode.rs b/compiler_tester/src/compilers/solidity/subprocess_mode.rs new file mode 100644 index 00000000..577f45b9 --- /dev/null +++ b/compiler_tester/src/compilers/solidity/subprocess_mode.rs @@ -0,0 +1,33 @@ +//! +//! The Solidity subprocess compiler mode. +//! + +/// +/// The Solidity subprocess compiler mode. +/// +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SubprocessMode { + /// The Solidity compiler version. + pub version: semver::Version, + /// The Solidity compiler output type. + pub pipeline: compiler_solidity::SolcPipeline, + /// Whether to run the Solidity compiler optimizer. + pub optimize: bool, +} + +impl SubprocessMode { + /// + /// A shortcut constructor. + /// + pub fn new( + solc_version: semver::Version, + solc_pipeline: compiler_solidity::SolcPipeline, + solc_optimize: bool, + ) -> Self { + Self { + version: solc_version, + pipeline: solc_pipeline, + optimize: solc_optimize, + } + } +} diff --git a/compiler_tester/src/compilers/vyper/cached_project.rs b/compiler_tester/src/compilers/vyper/cached_project.rs new file mode 100644 index 00000000..cedc6fb6 --- /dev/null +++ b/compiler_tester/src/compilers/vyper/cached_project.rs @@ -0,0 +1,20 @@ +//! +//! The Vyper compiler cached projects. +//! + +/// +/// The Vyper compiler cached projects. +/// +pub struct CachedProject { + /// The Vyper project. + pub project: compiler_vyper::Project, +} + +impl CachedProject { + /// + /// A shortcut constructor. + /// + pub fn new(project: compiler_vyper::Project) -> Self { + Self { project } + } +} diff --git a/compiler_tester/src/compilers/vyper/mod.rs b/compiler_tester/src/compilers/vyper/mod.rs new file mode 100644 index 00000000..6903248c --- /dev/null +++ b/compiler_tester/src/compilers/vyper/mod.rs @@ -0,0 +1,358 @@ +//! +//! The Vyper compiler wrapper. +//! + +pub mod cached_project; +pub mod subprocess_mode; + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::path::Path; +use std::path::PathBuf; +use std::str::FromStr; + +use itertools::Itertools; +use sha3::Digest; + +use super::build::Build as zkEVMContractBuild; +use super::cache::Cache; +use super::mode::vyper::Mode as VyperMode; +use super::mode::Mode; +use super::Compiler; + +use self::cached_project::CachedProject; +use self::subprocess_mode::SubprocessMode; + +/// +/// The Vyper compiler wrapper. +/// +pub struct VyperCompiler { + /// The name-to-code source files mapping. + sources: Vec<(String, String)>, + /// The vyper process output cache. + cache: Cache>, + /// The compiler debug config. + debug_config: Option, +} + +lazy_static::lazy_static! { + /// + /// The Vyper compiler supported modes. + /// + static ref MODES: Vec = { + let vyper_versions = VyperCompiler::all_versions().expect("`vyper` versions analysis error"); + + compiler_llvm_context::OptimizerSettings::combinations() + .into_iter() + .cartesian_product(vyper_versions) + .cartesian_product(vec![false, true]) + .map( + |((llvm_optimizer_settings, vyper_version), vyper_optimize)| { + VyperMode::new(vyper_version, vyper_optimize, llvm_optimizer_settings).into() + }, + ) + .collect::>() + }; +} + +impl VyperCompiler { + /// The compiler binaries directory. + pub const DIRECTORY: &'static str = "vyper-bin/"; + + /// + /// Returns the Vyper compiler instance by version. + /// + fn get_vyper_by_version(version: &semver::Version) -> compiler_vyper::VyperCompiler { + compiler_vyper::VyperCompiler::new(format!("{}/vyper-{}", Self::DIRECTORY, version)) + } + + /// + /// Returns the downloaded compiler versions. + /// + fn all_versions() -> anyhow::Result> { + let mut versions = Vec::new(); + for entry in std::fs::read_dir("./vyper-bin/")? { + let entry = entry?; + let path = entry.path(); + let entry_type = entry.file_type().map_err(|error| { + anyhow::anyhow!( + "File `{}` type getting error: {}", + path.to_string_lossy(), + error + ) + })?; + if !entry_type.is_file() { + anyhow::bail!( + "Invalid `vyper` binary file type: {}", + path.to_string_lossy() + ); + } + + let file_name = entry.file_name().to_string_lossy().to_string(); + let version_str = match file_name.strip_prefix("vyper-") { + Some(version_str) => version_str, + None => continue, + }; + let version: semver::Version = match version_str.parse() { + Ok(version) => version, + Err(_) => continue, + }; + versions.push(version); + } + Ok(versions) + } + + /// + /// Starts building the project into cache if it has not been built yet. + /// + fn start_building(&self, mode: &SubprocessMode) { + if !self.cache.contains(mode) { + if let Some(waiter) = self.cache.start(mode.clone()) { + self.cache + .finish(mode.clone(), self.get_build(mode), waiter); + }; + } + } + + /// + /// Gets the cached project data from the cache. + /// + fn get_build(&self, mode: &SubprocessMode) -> anyhow::Result { + let vyper = Self::get_vyper_by_version(&mode.version); + + let paths = self + .sources + .iter() + .map(|(path, _)| { + PathBuf::from_str(path).map_err(|error| anyhow::anyhow!("Invalid path: {}", error)) + }) + .collect::>>()?; + + let project = vyper.batch(&mode.version, paths, mode.optimize)?; + + Ok(CachedProject::new(project)) + } + + /// + /// Prints LLL IR if the flag is set. + /// + fn dump_lll( + &self, + debug_config: &compiler_llvm_context::DebugConfig, + mode: &VyperMode, + ) -> anyhow::Result<()> { + let vyper = Self::get_vyper_by_version(&mode.vyper_version); + + let lll = self + .sources + .iter() + .map(|(path_str, _)| { + let path = Path::new(path_str.as_str()); + vyper + .lll_debug(path, mode.vyper_optimize) + .map(|lll| (path_str.to_string(), lll)) + }) + .collect::>>()?; + + for (path, lll) in lll.iter() { + debug_config.dump_lll(path, lll)?; + } + + Ok(()) + } +} + +impl Compiler for VyperCompiler { + fn new( + sources: Vec<(String, String)>, + _libraries: BTreeMap>, + debug_config: Option, + _is_system_mode: bool, + ) -> Self { + Self { + sources, + cache: Cache::new(), + debug_config, + } + } + + fn modes() -> Vec { + MODES.clone() + } + + fn compile( + &self, + mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result> { + let mode = VyperMode::unwrap(mode); + + if let Some(ref debug_config) = self.debug_config { + self.dump_lll(debug_config, mode)?; + } + + let subprocess_mode = SubprocessMode::new(mode.vyper_version.clone(), mode.vyper_optimize); + + self.start_building(&subprocess_mode); + + let cached_project = { + self.cache.wait(&subprocess_mode); + let lock = self.cache.read(); + let cached_project = lock + .get(&subprocess_mode) + .expect("Always valid") + .unwrap_value(); + cached_project + .as_ref() + .map_err(|error| anyhow::anyhow!(error.to_string()))? + .project + .clone() + }; + + let target_machine = + compiler_llvm_context::TargetMachine::new(&mode.llvm_optimizer_settings)?; + let build = cached_project.compile( + target_machine, + mode.llvm_optimizer_settings.clone(), + self.debug_config.clone(), + )?; + + let mut forwarder_needed = false; + let mut builds = build + .contracts + .into_iter() + .map(|(path, contract)| { + if contract + .build + .factory_dependencies + .contains_key(compiler_vyper::FORWARDER_CONTRACT_HASH.as_str()) + { + forwarder_needed = true; + } + Ok(( + path, + zkEVMContractBuild::new_with_hash( + contract.build.assembly, + contract.build.bytecode_hash, + )?, + )) + }) + .collect::>>()?; + + if forwarder_needed { + builds.insert( + compiler_vyper::FORWARDER_CONTRACT_NAME.to_owned(), + zkEVMContractBuild::new_with_hash( + zkevm_assembly::Assembly::from_string( + compiler_vyper::FORWARDER_CONTRACT_ASSEMBLY.to_owned(), + sha3::Keccak256::digest( + compiler_vyper::FORWARDER_CONTRACT_ASSEMBLY.as_bytes(), + ) + .into(), + ) + .map_err(|error| anyhow::anyhow!("Vyper forwarder assembly: {}", error))?, + compiler_vyper::FORWARDER_CONTRACT_HASH.clone(), + ) + .map_err(|error| anyhow::anyhow!("Vyper forwarder: {}", error))?, + ); + } + + Ok(builds) + } + + fn selector( + &self, + mode: &Mode, + contract_path: &str, + entry: &str, + _is_system_contract_mode: bool, + ) -> anyhow::Result { + let mode = VyperMode::unwrap(mode); + + let subprocess_mode = SubprocessMode::new(mode.vyper_version.clone(), mode.vyper_optimize); + + self.start_building(&subprocess_mode); + + self.cache.wait(&subprocess_mode); + let lock = self.cache.read(); + let cached_project = lock + .get(&subprocess_mode) + .expect("Always valid") + .unwrap_value(); + + let cached_project = cached_project + .as_ref() + .map_err(|error| anyhow::anyhow!(error.to_string()))?; + + let contract_identifiers = match cached_project + .project + .contracts + .get(contract_path) + .ok_or_else(|| anyhow::anyhow!("Contract {} not found", contract_path))? + { + compiler_vyper::Contract::Vyper(inner) => &inner.abi, + compiler_vyper::Contract::LLVMIR(_inner) => panic!("Only used in the Vyper CLI"), + }; + + contract_identifiers + .iter() + .find_map(|(name, hash)| { + if name.starts_with(entry) { + Some( + u32::from_str_radix(&hash[2..], compiler_common::BASE_HEXADECIMAL).map_err( + |error| { + anyhow::anyhow!( + "Invalid selector from the Vyper compiler: {}", + error + ) + }, + ), + ) + } else { + None + } + }) + .ok_or_else(|| anyhow::anyhow!("Hash of the method `{}` not found", entry))? + } + + fn last_contract( + &self, + _mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result { + Ok(self + .sources + .last() + .ok_or_else(|| anyhow::anyhow!("Sources is empty"))? + .0 + .clone()) + } + + fn has_many_contracts() -> bool { + false + } + + fn check_pragmas(&self, mode: &Mode) -> bool { + let mode = VyperMode::unwrap(mode); + + self.sources.iter().all(|(_, source_code)| { + match source_code.lines().find_map(|line| { + let mut split = line.split_whitespace(); + if let (Some("#"), Some("@version"), Some(version)) = + (split.next(), split.next(), split.next()) + { + semver::VersionReq::parse(version).ok() + } else { + None + } + }) { + Some(pragma_version_req) => pragma_version_req.matches(&mode.vyper_version), + None => true, + } + }) + } + + fn check_ethereum_tests_params(_mode: &Mode, _params: &solidity_adapter::Params) -> bool { + true + } +} diff --git a/compiler_tester/src/compilers/vyper/subprocess_mode.rs b/compiler_tester/src/compilers/vyper/subprocess_mode.rs new file mode 100644 index 00000000..189ee3e7 --- /dev/null +++ b/compiler_tester/src/compilers/vyper/subprocess_mode.rs @@ -0,0 +1,23 @@ +//! +//! The Vyper subprocess compiler mode. +//! + +/// +/// The Vyper subprocess compiler mode. +/// +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SubprocessMode { + /// The Vyper compiler version. + pub version: semver::Version, + /// Whether to run the Vyper compiler optimizer. + pub optimize: bool, +} + +impl SubprocessMode { + /// + /// A shortcut constructor. + /// + pub fn new(version: semver::Version, optimize: bool) -> Self { + Self { version, optimize } + } +} diff --git a/compiler_tester/src/compilers/yul.rs b/compiler_tester/src/compilers/yul.rs new file mode 100644 index 00000000..41342f11 --- /dev/null +++ b/compiler_tester/src/compilers/yul.rs @@ -0,0 +1,118 @@ +//! +//! The Yul compiler. +//! + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use super::build::Build as zkEVMContractBuild; +use super::mode::yul::Mode as YulMode; +use super::mode::Mode; +use super::Compiler; + +/// +/// The Yul compiler. +/// +pub struct YulCompiler { + /// The name-to-code source files mapping. + sources: Vec<(String, String)>, + /// The compiler debug config. + debug_config: Option, + /// The compiler system mode flag. + is_system_mode: bool, +} + +lazy_static::lazy_static! { + /// + /// The Yul compiler supported modes. + /// + static ref MODES: Vec = { + compiler_llvm_context::OptimizerSettings::combinations() + .into_iter() + .map(|llvm_optimizer_settings| YulMode::new(llvm_optimizer_settings).into()) + .collect::>() + }; +} + +impl Compiler for YulCompiler { + fn new( + sources: Vec<(String, String)>, + _libraries: BTreeMap>, + debug_config: Option, + is_system_mode: bool, + ) -> Self { + Self { + sources, + debug_config, + is_system_mode, + } + } + + fn modes() -> Vec { + MODES.clone() + } + + fn compile( + &self, + mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result> { + let mode = YulMode::unwrap(mode); + + self.sources + .iter() + .map(|(path, source)| { + let project = compiler_solidity::Project::try_from_yul_string( + path.as_str(), + source.as_str(), + )?; + + let target_machine = + compiler_llvm_context::TargetMachine::new(&mode.llvm_optimizer_settings)?; + let contract = project + .compile_all( + target_machine, + mode.llvm_optimizer_settings.clone(), + self.is_system_mode, + self.debug_config.clone(), + )? + .contracts + .remove(path) + .ok_or_else(|| { + anyhow::anyhow!("Contract `{}` not found in yul project", path) + })?; + + let build = zkEVMContractBuild::new_with_hash( + contract.build.assembly, + contract.build.bytecode_hash, + )?; + Ok((path.to_owned(), build)) + }) + .collect() + } + + fn last_contract( + &self, + _mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result { + Ok(self + .sources + .last() + .ok_or_else(|| anyhow::anyhow!("Sources is empty"))? + .0 + .clone()) + } + + fn has_many_contracts() -> bool { + false + } + + fn check_pragmas(&self, _mode: &Mode) -> bool { + true + } + + fn check_ethereum_tests_params(_mode: &Mode, _params: &solidity_adapter::Params) -> bool { + true + } +} diff --git a/compiler_tester/src/compilers/zkevm.rs b/compiler_tester/src/compilers/zkevm.rs new file mode 100644 index 00000000..d13b083c --- /dev/null +++ b/compiler_tester/src/compilers/zkevm.rs @@ -0,0 +1,76 @@ +//! +//! The zkEVM compiler. +//! + +use std::collections::BTreeMap; +use std::collections::HashMap; + +use super::build::Build as zkEVMContractBuild; +use super::mode::zkevm::Mode as ZKEVMMode; +use super::mode::Mode; +use super::Compiler; + +/// +/// The zkEVM compiler. +/// +#[allow(non_camel_case_types)] +pub struct zkEVMCompiler { + /// The name-to-code source files mapping. + sources: Vec<(String, String)>, +} + +impl Compiler for zkEVMCompiler { + fn new( + sources: Vec<(String, String)>, + _libraries: BTreeMap>, + _debug_config: Option, + _is_system_mode: bool, + ) -> Self { + Self { sources } + } + + fn modes() -> Vec { + vec![ZKEVMMode::default().into()] + } + + fn compile( + &self, + _mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result> { + self.sources + .iter() + .map(|(path, source_code)| { + zkevm_assembly::Assembly::try_from(source_code.to_owned()) + .map_err(anyhow::Error::new) + .and_then(zkEVMContractBuild::new) + .map(|build| (path.to_string(), build)) + }) + .collect() + } + + fn last_contract( + &self, + _mode: &Mode, + _is_system_contract_mode: bool, + ) -> anyhow::Result { + Ok(self + .sources + .last() + .ok_or_else(|| anyhow::anyhow!("Sources is empty"))? + .0 + .clone()) + } + + fn has_many_contracts() -> bool { + false + } + + fn check_pragmas(&self, _mode: &Mode) -> bool { + true + } + + fn check_ethereum_tests_params(_mode: &Mode, _params: &solidity_adapter::Params) -> bool { + true + } +} diff --git a/compiler_tester/src/deployers/address_predictor.rs b/compiler_tester/src/deployers/address_predictor.rs new file mode 100644 index 00000000..3f9508ff --- /dev/null +++ b/compiler_tester/src/deployers/address_predictor.rs @@ -0,0 +1,73 @@ +//! +//! The deploy address predictor. +//! + +use std::collections::HashMap; +use std::str::FromStr; + +/// +/// The deploy address predictor. +/// +#[derive(Debug, Clone)] +pub struct AddressPredictor { + /// The accounts create nonces. + nonces: HashMap, +} + +impl AddressPredictor { + /// The create prefix. + const CREATE_PREFIX: &'static str = + "63bae3a9951d38e8a3fbb7b70909afc1200610fc5bc55ade242f815974674f23"; // keccak256("zksyncCreate") +} + +impl AddressPredictor { + /// + /// Create new address predictor instance. + /// + pub fn new() -> Self { + Self { + nonces: HashMap::new(), + } + } + + /// + /// Get the next deploy address (without nonce incrementing). + /// + pub fn advance_next_address(&self, caller: &web3::types::Address) -> web3::types::Address { + let nonce = self.nonces.get(caller).cloned().unwrap_or_default(); + + let mut bytes = web3::types::H256::from_str(Self::CREATE_PREFIX) + .expect("Invalid constant create prefix") + .as_bytes() + .to_vec(); + bytes.extend( + [0; compiler_common::BYTE_LENGTH_FIELD - compiler_common::BYTE_LENGTH_ETH_ADDRESS], + ); + bytes.extend(caller.to_fixed_bytes()); + bytes.extend([0; compiler_common::BYTE_LENGTH_FIELD - std::mem::size_of::()]); + bytes.extend(nonce.to_be_bytes()); + + let address = web3::types::Address::from_slice( + &web3::signing::keccak256(bytes.as_slice()) + [compiler_common::BYTE_LENGTH_FIELD - compiler_common::BYTE_LENGTH_ETH_ADDRESS..], + ); + address + } + + /// + /// Increments caller nonce. + /// + pub fn increment_nonce(&mut self, caller: web3::types::Address) { + let nonce = self.nonces.entry(caller).or_insert(0); + *nonce += 1; + } + + /// + /// Get the next deploy address (increments nonce when called). + /// + pub fn next_address(&mut self, caller: web3::types::Address) -> web3::types::Address { + let address = self.advance_next_address(&caller); + self.increment_nonce(caller); + address + } +} diff --git a/compiler_tester/src/deployers/mod.rs b/compiler_tester/src/deployers/mod.rs new file mode 100644 index 00000000..061d43ea --- /dev/null +++ b/compiler_tester/src/deployers/mod.rs @@ -0,0 +1,33 @@ +//! +//! The contract deployers. +//! + +pub mod address_predictor; +pub mod native_deployer; +pub mod system_contract_deployer; + +use crate::zkevm::execution_result::ExecutionResult; +use crate::zkevm::zkEVM; + +/// +/// The deployer trait. +/// +pub trait Deployer { + /// + /// Create new deployer instance. + /// + fn new() -> Self; + + /// + /// Deploy a contract. + /// + fn deploy( + &mut self, + test_name: String, + caller: web3::types::Address, + bytecode_hash: web3::types::U256, + constructor_calldata: Vec, + value: Option, + vm: &mut zkEVM, + ) -> anyhow::Result; +} diff --git a/compiler_tester/src/deployers/native_deployer.rs b/compiler_tester/src/deployers/native_deployer.rs new file mode 100644 index 00000000..2f00b09b --- /dev/null +++ b/compiler_tester/src/deployers/native_deployer.rs @@ -0,0 +1,178 @@ +//! +//! The native deployer implementation. +//! + +use std::collections::HashMap; + +use web3::contract::tokens::Tokenizable; + +use crate::test::case::input::output::Output; +use crate::test::case::input::value::Value; +use crate::zkevm::execution_result::ExecutionResult; +use crate::zkevm::zkEVM; + +use super::address_predictor::AddressPredictor; +use super::Deployer; + +/// +/// The native deployer implementation. +/// +#[derive(Debug, Clone)] +pub struct NativeDeployer { + /// The address predictor instance for computing the contracts addresses. + address_predictor: AddressPredictor, +} + +impl NativeDeployer { + /// The immutables mapping position in contract. + const IMMUTABLES_MAPPING_POSITION: web3::types::U256 = web3::types::U256::zero(); +} + +impl Deployer for NativeDeployer { + fn new() -> Self { + Self { + address_predictor: AddressPredictor::new(), + } + } + + fn deploy( + &mut self, + test_name: String, + caller: web3::types::Address, + bytecode_hash: web3::types::U256, + constructor_calldata: Vec, + value: Option, + vm: &mut zkEVM, + ) -> anyhow::Result { + let address = self.address_predictor.advance_next_address(&caller); + + vm.add_deployed_contract(address, bytecode_hash, None); + + let context_u128_value = if let Some(value) = value { + vm.mint_ether(address, web3::types::U256::from(value)); + value + } else { + 0 + }; + + let result = vm.run( + test_name, + address, + caller, + context_u128_value, + constructor_calldata, + zkevm_tester::runners::compiler_tests::VmLaunchOption::Constructor, + )?; + + if result.output.exception { + if let Some(value) = value { + vm.burn_ether(address, web3::types::U256::from(value)); + } + vm.remove_deployed_contract(address); + return Ok(result); + } + + self.address_predictor.increment_nonce(caller); + + Self::set_immutables(address, &result.output.return_data, vm)?; + + let return_data = vec![Value::Certain(web3::types::U256::from_big_endian( + address.as_bytes(), + ))]; + + Ok(ExecutionResult::new( + Output::new(return_data, false, result.output.events), + result.cycles, + )) + } +} + +impl NativeDeployer { + /// + /// Writes the contract immutables to a storage. + /// + fn set_immutables( + address: web3::types::Address, + encoded_data: &[Value], + vm: &mut zkEVM, + ) -> anyhow::Result<()> { + let return_data = encoded_data + .iter() + .flat_map(|value| { + let mut bytes = [0u8; compiler_common::BYTE_LENGTH_FIELD]; + value.unwrap_certain_as_ref().to_big_endian(&mut bytes); + bytes + }) + .collect::>(); + + let r#type = + web3::ethabi::ParamType::Array(Box::new(web3::ethabi::ParamType::Tuple(vec![ + web3::ethabi::ParamType::Uint(256), + web3::ethabi::ParamType::FixedBytes(32), + ]))); + let mut immutables = web3::ethabi::decode(&[r#type], return_data.as_slice()) + .map_err(|err| anyhow::anyhow!("Failed to decode immutables: {:?}", err))?; + + assert_eq!(immutables.len(), 1); + let immutables = match immutables.remove(0) { + web3::ethabi::Token::Array(immutables) => immutables, + _ => unreachable!(), + }; + + let mut immutables_storage = HashMap::new(); + for immutable in immutables { + let (immutable_index, immutable_value) = match immutable { + web3::ethabi::Token::Tuple(elements) => { + assert_eq!(elements.len(), 2); + let mut elements_iter = elements.into_iter(); + ( + elements_iter.next().expect("Always valid"), + elements_iter.next().expect("Always valid"), + ) + } + _ => unreachable!(), + }; + + let immutable_index = + web3::types::U256::from_token(immutable_index).expect("Always valid"); + let immutable_value = + web3::types::H256::from_token(immutable_value).expect("Always valid"); + + let immutable_position = Self::get_position_of_immutable(address, immutable_index); + + let storage_key = zkevm_tester::runners::compiler_tests::StorageKey { + address: web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_IMMUTABLE_SIMULATOR.into(), + ), + key: immutable_position, + }; + + immutables_storage.insert(storage_key, immutable_value); + } + + vm.populate_storage(immutables_storage); + + Ok(()) + } + + /// + /// Returns the immutable position in the contract storage. + /// + fn get_position_of_immutable( + address: web3::types::Address, + index: web3::types::U256, + ) -> web3::types::U256 { + let mut key = web3::types::H256::from(address).to_fixed_bytes().to_vec(); + key.extend([0u8; compiler_common::BYTE_LENGTH_FIELD]); + Self::IMMUTABLES_MAPPING_POSITION + .to_big_endian(&mut key[compiler_common::BYTE_LENGTH_FIELD..]); + let key = web3::signing::keccak256(key.as_slice()).to_vec(); + + let mut nested_key = vec![0u8; compiler_common::BYTE_LENGTH_FIELD]; + index.to_big_endian(&mut nested_key[..]); + nested_key.extend(key); + let nested_key = web3::signing::keccak256(nested_key.as_slice()); + + web3::types::U256::from(nested_key) + } +} diff --git a/compiler_tester/src/deployers/system_contract_deployer.rs b/compiler_tester/src/deployers/system_contract_deployer.rs new file mode 100644 index 00000000..b912835c --- /dev/null +++ b/compiler_tester/src/deployers/system_contract_deployer.rs @@ -0,0 +1,118 @@ +//! +//! The system contract deployer implementation. +//! + +use crate::zkevm::execution_result::ExecutionResult; +use crate::zkevm::zkEVM; + +use super::Deployer; + +/// +/// The system contract deployer implementation. +/// +#[derive(Debug, Clone)] +pub struct SystemContractDeployer; + +impl SystemContractDeployer { + /// The create method selector. + const CREATE_METHOD_SELECTOR: u32 = 0x9c4d535b; // keccak256("create(bytes32,bytes32,bytes)") +} + +impl Deployer for SystemContractDeployer { + fn new() -> Self { + Self + } + + fn deploy( + &mut self, + test_name: String, + caller: web3::types::Address, + bytecode_hash: web3::types::U256, + constructor_calldata: Vec, + value: Option, + vm: &mut zkEVM, + ) -> anyhow::Result { + let context_u128_value; + let vm_launch_option; + let mut entry_address = web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into(), + ); + + if M { + context_u128_value = 0; + + let mut r3 = None; + let mut r4 = None; + let mut r5 = None; + if let Some(value) = value { + let value = web3::types::U256::from(value); + vm.mint_ether(caller, value); + + r3 = Some(value); + r4 = Some(web3::types::U256::from( + zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER, + )); + r5 = Some(web3::types::U256::from(u8::from( + compiler_llvm_context::SYSTEM_CALL_BIT, + ))); + + entry_address = web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_MSG_VALUE.into(), + ); + } + + vm_launch_option = zkevm_tester::runners::compiler_tests::VmLaunchOption::ManualCallABI( + zkevm_tester::runners::compiler_tests::FullABIParams { + is_constructor: false, + is_system_call: true, + r3_value: r3, + r4_value: r4, + r5_value: r5, + }, + ); + } else { + if let Some(value) = value { + context_u128_value = value; + vm.mint_ether( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into(), + ), + web3::types::U256::from(value), + ); + } else { + context_u128_value = 0; + } + + vm_launch_option = zkevm_tester::runners::compiler_tests::VmLaunchOption::ManualCallABI( + zkevm_tester::runners::compiler_tests::FullABIParams { + is_constructor: false, + is_system_call: true, + r3_value: None, + r4_value: None, + r5_value: None, + }, + ); + } + + let mut calldata = Vec::with_capacity( + constructor_calldata.len() + compiler_common::BYTE_LENGTH_FIELD * 4 + 4, + ); + calldata.extend(Self::CREATE_METHOD_SELECTOR.to_be_bytes().to_vec()); + calldata.extend([0u8; 2 * compiler_common::BYTE_LENGTH_FIELD]); + bytecode_hash.to_big_endian(&mut calldata[compiler_common::BYTE_LENGTH_FIELD + 4..]); + calldata.extend(web3::types::H256::from_low_u64_be(96).as_bytes()); + calldata.extend( + web3::types::H256::from_low_u64_be(constructor_calldata.len() as u64).as_bytes(), + ); + calldata.extend(constructor_calldata); + + vm.run( + test_name, + entry_address, + caller, + context_u128_value, + calldata, + vm_launch_option, + ) + } +} diff --git a/compiler_tester/src/directories/ethereum/mod.rs b/compiler_tester/src/directories/ethereum/mod.rs new file mode 100644 index 00000000..45929265 --- /dev/null +++ b/compiler_tester/src/directories/ethereum/mod.rs @@ -0,0 +1,76 @@ +//! +//! The Ethereum tests directory. +//! + +pub mod test; + +use std::path::Path; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::Compiler; +use crate::filters::Filters; +use crate::Summary; + +use super::TestsDirectory; + +use self::test::EthereumTest; + +/// +/// The Ethereum tests directory. +/// +pub struct EthereumDirectory {} + +impl EthereumDirectory { + /// + /// The index file name. + /// + const INDEX_NAME: &'static str = "index.yaml"; +} + +impl TestsDirectory for EthereumDirectory +where + C: Compiler, +{ + type Test = EthereumTest; + + fn all_tests( + directory_path: &Path, + _extension: &'static str, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> anyhow::Result> { + let mut index_path = directory_path.to_path_buf(); + index_path.push(Self::INDEX_NAME); + let index_data = std::fs::read_to_string(index_path)?; + let index: solidity_adapter::FSEntity = serde_yaml::from_str(index_data.as_str())?; + let tests = index + .into_enabled_list(directory_path) + .into_iter() + .filter_map(|test| { + EthereumTest::new(test, summary.clone(), debug_config.clone(), filters) + }) + .collect(); + + Ok(tests) + } + + fn single_test( + directory_path: &Path, + test_path: &Path, + _extension: &'static str, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> anyhow::Result> { + let mut index_path = directory_path.to_path_buf(); + index_path.push(Self::INDEX_NAME); + let index_data = std::fs::read_to_string(index_path.as_path())?; + let index: solidity_adapter::FSEntity = serde_yaml::from_str(index_data.as_str())?; + index + .into_enabled_test(directory_path, test_path) + .ok_or_else(|| anyhow::anyhow!("Test not found")) + .map(|test| EthereumTest::new(test, summary, debug_config, filters)) + } +} diff --git a/compiler_tester/src/directories/ethereum/test.rs b/compiler_tester/src/directories/ethereum/test.rs new file mode 100644 index 00000000..512f1c28 --- /dev/null +++ b/compiler_tester/src/directories/ethereum/test.rs @@ -0,0 +1,287 @@ +//! +//! The Ethereum compiler test. +//! + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::compilers::Compiler; +use crate::deployers::address_predictor::AddressPredictor; +use crate::directories::Buildable; +use crate::filters::Filters; +use crate::summary::Summary; +use crate::test::case::Case; +use crate::test::instance::Instance; +use crate::test::Test; + +/// +/// The Ethereum compiler test. +/// +pub struct EthereumTest +where + C: Compiler, +{ + /// The test name. + test_name: String, + /// The index test entity. + index_entity: solidity_adapter::EnabledTest, + /// The test compiler. + compiler: C, + /// The test function calls. + calls: Vec, + /// The test params. + params: solidity_adapter::Params, + /// The main contract address. + contract_address: web3::types::Address, + /// The libraries addresses. + libraries_addresses: HashMap, + /// The last source name. + last_source: String, +} + +impl EthereumTest +where + C: Compiler, +{ + /// + /// Try to create new test. + /// + pub fn new( + index_entity: solidity_adapter::EnabledTest, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> Option { + let test_name = index_entity.path.to_string_lossy().to_string(); + + if !filters.check_group(&index_entity.group) { + return None; + } + + if !filters.check_case_path(&test_name) { + return None; + } + + let test = match solidity_adapter::Test::try_from(index_entity.path.as_path()) { + Ok(test) => test, + Err(error) => { + Summary::invalid(summary, None, test_name, error); + return None; + } + }; + + let solidity_adapter::Test { + sources, + mut calls, + params, + } = test; + + if !calls + .iter() + .any(|call| matches!(call, solidity_adapter::FunctionCall::Constructor { .. })) + { + let constructor = solidity_adapter::FunctionCall::Constructor { + calldata: vec![], + value: None, + events: vec![], + gas_options: vec![], + }; + let constructor_insert_index = calls + .iter() + .position(|call| !matches!(call, solidity_adapter::FunctionCall::Library { .. })) + .unwrap_or(calls.len()); + calls.insert(constructor_insert_index, constructor); + } + + let last_source = match sources.last() { + Some(last_source) => last_source.0.clone(), + None => { + Summary::invalid( + summary, + None, + test_name, + anyhow::anyhow!("Sources is empty"), + ); + return None; + } + }; + + let mut address_predictor = AddressPredictor::new(); + + let mut contract_address = None; + let mut caller = solidity_adapter::account_address(solidity_adapter::DEFAULT_ACCOUNT_INDEX); + + let mut libraries_for_compiler = BTreeMap::new(); + let mut libraries_addresses = HashMap::new(); + + for call in calls.iter() { + match call { + solidity_adapter::FunctionCall::Constructor { .. } => { + if contract_address.is_some() { + Summary::invalid( + summary, + None, + test_name, + anyhow::anyhow!("Two constructors in test"), + ); + return None; + } + contract_address = Some(address_predictor.next_address(caller)); + } + solidity_adapter::FunctionCall::Library { name, source } => { + let source = source.clone().unwrap_or_else(|| last_source.clone()); + let address = address_predictor.next_address(caller); + libraries_for_compiler + .entry(source.clone()) + .or_insert_with(BTreeMap::new) + .insert( + name.clone(), + format!("0x{}", crate::utils::address_as_string(&address)), + ); + libraries_addresses.insert(format!("{source}:{name}"), address); + } + solidity_adapter::FunctionCall::Account { input, expected } => { + let address = solidity_adapter::account_address(*input); + if !expected.eq(&address) { + Summary::invalid( + summary, + None, + test_name, + anyhow::anyhow!( + "Expected address: {}, but found {}", + expected, + address + ), + ); + return None; + } + caller = address; + } + _ => {} + } + } + + let contract_address = contract_address.expect("Always valid"); + + Some(Self { + test_name, + index_entity, + compiler: C::new(sources, libraries_for_compiler, debug_config, false), + calls, + params, + contract_address, + libraries_addresses, + last_source, + }) + } +} + +impl Buildable for EthereumTest +where + C: Compiler, +{ + fn build(&self, mode: Mode, summary: Arc>, filters: &Filters) -> Option { + let test_name = self.index_entity.path.to_string_lossy().to_string(); + + if !filters.check_mode(&mode) { + return None; + } + + if let Some(filters) = self.index_entity.modes.as_ref() { + if !Filters::check_mode_filters(&mode, filters.as_slice()) { + return None; + } + } + + if let Some(versions) = self.index_entity.version.as_ref() { + if !mode.check_version(versions) { + return None; + } + } + + if !C::check_ethereum_tests_params(&mode, &self.params) { + return None; + } + + let main_contract = match self + .compiler + .last_contract(&mode, false) + .map_err(|error| anyhow::anyhow!("Failed to get main contract: {}", error)) + { + Ok(main_contract) => main_contract, + Err(error) => { + Summary::invalid(summary, Some(mode.clone()), test_name, error); + return None; + } + }; + + let mut main_contract_instance = None; + let mut libraries_instances = HashMap::with_capacity(self.libraries_addresses.len()); + + let builds = match self + .compiler + .compile(&mode, false) + .map_err(|error| anyhow::anyhow!("Failed to compile sources: {}", error)) + .and_then(|builds| { + let main_contract_build = builds.get(main_contract.as_str()).ok_or_else(|| { + anyhow::anyhow!("Main contract not found in the compiler build artifacts") + })?; + main_contract_instance = Some(Instance::new( + main_contract.clone(), + Some(self.contract_address), + main_contract_build.bytecode_hash, + )); + + for (library_name, library_address) in self.libraries_addresses.iter() { + let build = builds.get(library_name).ok_or_else(|| { + anyhow::anyhow!( + "Library {} not found in the compiler build artifacts", + library_name + ) + })?; + libraries_instances.insert( + library_name.clone(), + Instance::new( + library_name.clone(), + Some(*library_address), + build.bytecode_hash, + ), + ); + } + Ok(builds + .into_values() + .map(|build| (build.bytecode_hash, build.assembly)) + .collect()) + }) { + Ok(builds) => builds, + Err(error) => { + Summary::invalid(summary, Some(mode.clone()), test_name, error); + return None; + } + }; + + let case = match Case::from_ethereum::( + &self.calls, + &main_contract_instance.expect("Always valid"), + &libraries_instances, + &self.last_source, + ) { + Ok(case) => case, + Err(error) => { + Summary::invalid(summary, Some(mode), self.test_name.clone(), error); + return None; + } + }; + + Some(Test::new( + test_name, + self.index_entity.group.clone(), + mode, + builds, + vec![case], + )) + } +} diff --git a/compiler_tester/src/directories/matter_labs/mod.rs b/compiler_tester/src/directories/matter_labs/mod.rs new file mode 100644 index 00000000..8379a8d4 --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/mod.rs @@ -0,0 +1,108 @@ +//! +//! The Matter Labs compiler tests directory. +//! + +pub mod test; + +use std::fs; +use std::path::Path; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::Compiler; +use crate::filters::Filters; +use crate::summary::Summary; + +use super::TestsDirectory; + +use self::test::MatterLabsTest; + +/// +/// The Matter Labs compiler tests directory. +/// +pub struct MatterLabsDirectory {} + +impl TestsDirectory for MatterLabsDirectory +where + C: Compiler, +{ + type Test = MatterLabsTest; + + fn all_tests( + directory_path: &Path, + extension: &'static str, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> anyhow::Result> { + let mut tests = Vec::new(); + + for entry in fs::read_dir(directory_path)? { + let entry = entry?; + let path = entry.path(); + let entry_type = entry.file_type().map_err(|error| { + anyhow::anyhow!( + "Failed to get file(`{}`) type: {}", + path.to_string_lossy(), + error + ) + })?; + + if entry_type.is_dir() { + tests.extend(Self::all_tests( + &path, + extension, + summary.clone(), + debug_config.clone(), + filters, + )?); + continue; + } else if !entry_type.is_file() { + anyhow::bail!("Invalid file type: {}", path.to_string_lossy()); + } + + if entry.file_name().to_string_lossy().starts_with('.') { + continue; + } + + let file_extension = path.extension().ok_or_else(|| { + anyhow::anyhow!("Failed to get file extension: {}", path.to_string_lossy()) + })?; + if file_extension != extension { + continue; + } + + if let Some(test) = + MatterLabsTest::new(path, summary.clone(), debug_config.clone(), filters) + { + tests.push(test); + } + } + + Ok(tests) + } + + fn single_test( + directory_path: &Path, + test_path: &Path, + extension: &'static str, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> anyhow::Result> { + let file_extension = test_path.extension().ok_or_else(|| { + anyhow::anyhow!( + "Failed to get file extension: {}", + test_path.to_string_lossy() + ) + })?; + if file_extension != extension { + anyhow::bail!("Invalid file extension"); + } + + let mut path = directory_path.to_path_buf(); + path.push(test_path); + + Ok(MatterLabsTest::new(path, summary, debug_config, filters)) + } +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/input/calldata.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/calldata.rs new file mode 100644 index 00000000..e47660e3 --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/calldata.rs @@ -0,0 +1,23 @@ +//! +//! The Matter Labs compiler test metadata case input calldata. +//! + +use serde::Deserialize; + +/// +/// The Matter Labs compiler test metadata case input calldata. +/// +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum Calldata { + /// The single value. + Value(String), + /// The list of values. + List(Vec), +} + +impl Default for Calldata { + fn default() -> Self { + Self::List(vec![]) + } +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/mod.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/mod.rs new file mode 100644 index 00000000..28be74cc --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/mod.rs @@ -0,0 +1,59 @@ +//! +//! The Matter Labs compiler test metadata expected data variant. +//! + +pub mod variant; + +use serde::Deserialize; + +use crate::compilers::mode::Mode; + +use self::variant::Variant; + +/// +/// The Matter Labs compiler test metadata expected data variant. +/// +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum Expected { + /// The single expected data variant. + Single(Variant), + /// The several expected data variants, normally filtered by the compiler version and settings. + Multiple(Vec), +} + +impl Expected { + /// + /// Creates successful deployer call expected data. + /// + pub fn successful_deployer_expected(instance: String) -> Self { + Self::Single(Variant::Simple(vec![format!("{instance}.address")])) + } + + /// + /// Returns exception flag for specified mode. + /// + pub fn exception(&self, mode: &Mode) -> anyhow::Result { + let variants = match self { + Self::Single(variant) => vec![variant], + Self::Multiple(variants) => variants.iter().collect(), + }; + let variant = variants + .into_iter() + .find(|variant| { + let version = match variant { + Variant::Simple(_) => None, + Variant::Extended(inner) => inner.compiler_version.as_ref(), + }; + match version { + Some(version) => mode.check_version(version), + None => true, + } + }) + .ok_or_else(|| anyhow::anyhow!("Version not covered"))?; + Ok(match variant { + Variant::Simple(_) => false, + Variant::Extended(inner) => inner.exception, + }) + } +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/extended/event.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/extended/event.rs new file mode 100644 index 00000000..00c5298b --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/extended/event.rs @@ -0,0 +1,18 @@ +//! +//! The Matter Labs compiler test metadata expected event. +//! + +use serde::Deserialize; + +/// +/// The Matter Labs compiler test metadata expected event. +/// +#[derive(Debug, Clone, Deserialize)] +pub struct Event { + /// The emitter contract address. + pub address: Option, + /// The indexed topics. + pub topics: Vec, + /// The ordinary values. + pub values: Vec, +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/extended/mod.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/extended/mod.rs new file mode 100644 index 00000000..c18af6ab --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/extended/mod.rs @@ -0,0 +1,26 @@ +//! +//! The Matter Labs compiler test metadata extended expected data. +//! + +pub mod event; + +use serde::Deserialize; + +use self::event::Event; + +/// +/// The Matter Labs compiler test metadata extended expected data. +/// +#[derive(Debug, Default, Clone, Deserialize)] +pub struct Extended { + /// The return data values. + pub return_data: Vec, + /// The emitted events. + #[serde(default)] + pub events: Vec, + /// Whether an exception is expected, + #[serde(default)] + pub exception: bool, + /// The compiler version filter. + pub compiler_version: Option, +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/mod.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/mod.rs new file mode 100644 index 00000000..d8aee59e --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/expected/variant/mod.rs @@ -0,0 +1,21 @@ +//! +//! The Matter Labs compiler test metadata expected data variant. +//! + +pub mod extended; + +use serde::Deserialize; + +use self::extended::Extended; + +/// +/// The Matter Labs compiler test metadata expected data variant. +/// +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum Variant { + /// The return values only list. + Simple(Vec), + /// The extended snapshot data testing. + Extended(Extended), +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/input/mod.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/mod.rs new file mode 100644 index 00000000..5d57dbad --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/mod.rs @@ -0,0 +1,64 @@ +//! +//! The Matter Labs compiler test metadata case input. +//! + +pub mod calldata; +pub mod expected; +pub mod storage; + +use std::collections::HashMap; + +use serde::Deserialize; + +use crate::directories::matter_labs::test::default_caller_address; +use crate::directories::matter_labs::test::simple_tests_instance; + +use self::calldata::Calldata; +use self::expected::Expected; +use self::storage::Storage; + +/// +/// The Matter Labs compiler test metadata case input. +/// +#[derive(Debug, Clone, Deserialize)] +pub struct Input { + /// The comment to an entry. + pub comment: Option, + /// The contract instance. + #[serde(default = "simple_tests_instance")] + pub instance: String, + /// The caller address. + #[serde(default = "default_caller_address")] + pub caller: String, + /// The contract method name. + /// `#deployer` for the deployer call + /// `#fallback` for the fallback + pub method: String, + /// The passed calldata. + pub calldata: Calldata, + /// The passed value. + pub value: Option, + /// The initial contracts storage. + #[serde(default)] + pub storage: HashMap, + /// The expected return data. + pub expected: Option, +} + +impl Input { + /// + /// Creates a deployer call with empty constructor calldata. + /// + pub fn empty_deployer_call(instance: String) -> Self { + Self { + comment: None, + instance: instance.clone(), + calldata: Calldata::default(), + value: None, + caller: default_caller_address(), + expected: Some(Expected::successful_deployer_expected(instance)), + method: "#deployer".to_string(), + storage: HashMap::new(), + } + } +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/input/storage.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/storage.rs new file mode 100644 index 00000000..b93cd46e --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/input/storage.rs @@ -0,0 +1,25 @@ +//! +//! The Matter Labs compiler test metadata case input contract storage. +//! + +use std::collections::HashMap; + +use serde::Deserialize; + +/// +/// The Matter Labs compiler test metadata case input contract storage. +/// +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +pub enum Storage { + /// The list, where the key starts from 0. + List(Vec), + /// The map with actual explicit keys. + Map(HashMap), +} + +impl Default for Storage { + fn default() -> Self { + Self::Map(HashMap::new()) + } +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/case/mod.rs b/compiler_tester/src/directories/matter_labs/test/metadata/case/mod.rs new file mode 100644 index 00000000..fb86f128 --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/case/mod.rs @@ -0,0 +1,138 @@ +//! +//! The Matter Labs compiler test metadata case. +//! + +pub mod input; + +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::str::FromStr; + +use serde::Deserialize; + +use crate::compilers::mode::Mode; +use crate::deployers::address_predictor::AddressPredictor; + +use self::input::expected::Expected; +use self::input::Input; + +/// +/// The Matter Labs compiler test metadata case. +/// +#[derive(Debug, Clone, Deserialize)] +pub struct Case { + /// The comment to a case. + pub comment: Option, + /// The case name. + pub name: String, + /// The mode filter. + pub modes: Option>, + /// The case inputs. + pub inputs: Vec, + /// The expected return data. + pub expected: Expected, + /// If the test case must be ignored. + #[serde(default)] + pub ignore: bool, + /// Overrides the default number of cycles. + pub cycles: Option, +} + +impl Case { + /// + /// Validates deployer calls, adds libraries deployer calls, contracts deployer calls if they are not present. + /// + pub fn normalize_deployer_calls( + &mut self, + instances: &BTreeSet, + libraries: &[String], + ) -> anyhow::Result<()> { + let mut instances = instances.clone(); + for (index, input) in self.inputs.iter().enumerate() { + let instance = &input.instance; + if !input.method.eq("#deployer") { + continue; + }; + + if libraries.contains(instance) { + anyhow::bail!("Deployer call {} for library, note: libraries deployer calls generating automatically", index); + } + + if !instances.remove(instance) { + anyhow::bail!( + "Input {} is a second deployer call for the same instance or instance is invalid", + index + ); + } + } + + let mut inputs = Vec::with_capacity(libraries.len() + instances.len() + self.inputs.len()); + + for instance in libraries.iter() { + inputs.push(Input::empty_deployer_call(instance.clone())); + } + + for instance in instances { + if !libraries.contains(&instance) { + inputs.push(Input::empty_deployer_call(instance.clone())); + } + } + + inputs.append(&mut self.inputs); + self.inputs = inputs; + + Ok(()) + } + + /// + /// Copies the final expected data to the last input. + /// + pub fn normalize_expected(&mut self) { + if let Some(input) = self.inputs.last_mut() { + if input.expected.is_none() { + input.expected = Some(self.expected.clone()); + } + } + } + + /// + /// Returns all the instances addresses, except libraries. + /// + pub fn instances_addresses( + &self, + libraries: &BTreeSet, + address_predictor: &mut AddressPredictor, + mode: &Mode, + ) -> anyhow::Result> { + let mut instances_addresses = HashMap::new(); + for (index, input) in self.inputs.iter().enumerate() { + if !input.method.eq("#deployer") { + continue; + } + let instance = &input.instance; + if libraries.contains(instance) { + continue; + } + let exception = match input.expected.as_ref() { + Some(expected) => expected.exception(mode).map_err(|error| { + anyhow::anyhow!("Input {}(After adding deployer calls): {}", index, error) + })?, + None => false, + }; + if exception { + continue; + } + let caller = + web3::types::Address::from_str(input.caller.as_str()).map_err(|error| { + anyhow::anyhow!( + "Input {}(After adding deployer calls): Invalid caller: {}", + index, + error + ) + })?; + instances_addresses + .insert(instance.to_string(), address_predictor.next_address(caller)); + } + Ok(instances_addresses) + } +} diff --git a/compiler_tester/src/directories/matter_labs/test/metadata/mod.rs b/compiler_tester/src/directories/matter_labs/test/metadata/mod.rs new file mode 100644 index 00000000..d83fdf48 --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/metadata/mod.rs @@ -0,0 +1,56 @@ +//! +//! The Matter Labs compiler test metadata. +//! + +pub mod case; + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::str::FromStr; + +use serde::Deserialize; + +use self::case::Case; + +/// +/// The Matter Labs compiler test metadata. +/// +#[derive(Debug, Clone, Deserialize)] +pub struct Metadata { + /// The test cases. + pub cases: Vec, + /// The mode filter. + pub modes: Option>, + /// The test contracts (key is instance name, value is path). + #[serde(default)] + pub contracts: HashMap, + /// The test libraries for linking (key is path value, value is instance name). + #[serde(default)] + pub libraries: BTreeMap>, + /// If build contracts in system mode. + #[serde(default)] + pub system_mode: bool, + /// If the entire test file must be ignored. + #[serde(default)] + pub ignore: bool, + /// The test group. + pub group: Option, +} + +impl FromStr for Metadata { + type Err = anyhow::Error; + + fn from_str(string: &str) -> Result { + let json = string + .lines() + .filter_map(|line| { + line.strip_prefix("//!") + .or_else(|| line.strip_prefix(";!")) + .or_else(|| line.strip_prefix("#!")) + }) + .collect::>() + .join(""); + + serde_json::from_str(json.as_str()).or_else(|_| Ok(serde_json::from_str(string)?)) + } +} diff --git a/compiler_tester/src/directories/matter_labs/test/mod.rs b/compiler_tester/src/directories/matter_labs/test/mod.rs new file mode 100644 index 00000000..5f6b756b --- /dev/null +++ b/compiler_tester/src/directories/matter_labs/test/mod.rs @@ -0,0 +1,347 @@ +//! +//! The Matter Labs compiler test. +//! + +pub mod metadata; + +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::collections::HashMap; +use std::collections::HashSet; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::compilers::Compiler; +use crate::deployers::address_predictor::AddressPredictor; +use crate::directories::Buildable; +use crate::filters::Filters; +use crate::summary::Summary; +use crate::test::case::Case; +use crate::test::instance::Instance; +use crate::test::Test; + +use self::metadata::Metadata; + +/// The default simple contract name. +pub const SIMPLE_TESTS_CONTRACT_NAME: &str = "Test"; + +/// The default simple contract instance name. +pub const SIMPLE_TESTS_INSTANCE: &str = "Test"; + +/// The default address of the caller. +pub const DEFAULT_CALLER_ADDRESS: &str = "deadbeef01000000000000000000000000000000"; + +/// +/// Used for default initialization. +/// +pub fn simple_tests_instance() -> String { + SIMPLE_TESTS_INSTANCE.to_string() +} + +/// +/// Used for default initialization. +/// +pub fn default_caller_address() -> String { + DEFAULT_CALLER_ADDRESS.to_string() +} + +/// +/// The Matter Labs compiler test. +/// +pub struct MatterLabsTest +where + C: Compiler, +{ + /// The test name. + test_name: String, + /// The test compiler. + compiler: C, + /// The address predictor. + address_predictor: AddressPredictor, + /// The test metadata. + metadata: Metadata, + /// The libraries addresses. + libraries_instances: HashMap, +} + +impl MatterLabsTest +where + C: Compiler, +{ + /// + /// Try to create new test. + /// + pub fn new( + path: PathBuf, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> Option { + let test_name = path.to_string_lossy().to_string(); + + if !filters.check_test_path(&test_name) { + return None; + } + + let metadata_file_string = match std::fs::read_to_string(path.as_path()) { + Ok(metadata_file_string) => metadata_file_string, + Err(error) => { + Summary::invalid(summary, None, test_name, error); + return None; + } + }; + + let mut metadata = match Metadata::from_str(metadata_file_string.as_str()) + .map_err(|err| anyhow::anyhow!("Invalid metadata json: {}", err)) + { + Ok(metadata) => metadata, + Err(error) => { + Summary::invalid(summary, None, test_name, error); + return None; + } + }; + + if metadata.ignore { + Summary::ignored(summary, test_name); + return None; + } + + if !filters.check_group(&metadata.group) { + return None; + } + + let sources = if metadata.contracts.is_empty() { + let contract_name = if C::has_many_contracts() { + format!("{}:{}", path.to_string_lossy(), SIMPLE_TESTS_CONTRACT_NAME) + } else { + path.to_string_lossy().to_string() + }; + metadata + .contracts + .insert(SIMPLE_TESTS_INSTANCE.to_owned(), contract_name); + vec![(path.to_string_lossy().to_string(), metadata_file_string)] + } else { + let mut sources = HashMap::new(); + let mut paths = HashSet::with_capacity(metadata.contracts.len()); + for (_, path_string) in metadata.contracts.iter_mut() { + let mut file_path = path.clone(); + file_path.pop(); + let mut path_string_split = path_string.split(':'); + let file_relative_path = path_string_split.next().expect("Always exists"); + let contract_name = path_string_split.next(); + file_path.push(file_relative_path); + *path_string = if let Some(contract_name) = contract_name { + format!("{}:{}", file_path.to_string_lossy(), contract_name) + } else { + file_path.to_string_lossy().to_string() + }; + paths.insert(file_path.to_string_lossy().to_string()); + } + let mut test_directory_path = path.clone(); + test_directory_path.pop(); + for entry in + glob::glob(format!("{}/**/*.sol", test_directory_path.to_string_lossy()).as_str()) + .expect("Always valid") + .filter_map(Result::ok) + { + paths.insert(entry.to_string_lossy().to_string()); + } + + for path in paths.into_iter() { + let source_code = match std::fs::read_to_string(path.as_str()) + .map_err(|err| anyhow::anyhow!("Failed to read source code file: {}", err)) + { + Ok(source) => source, + Err(error) => { + Summary::invalid(summary, None, test_name, error); + return None; + } + }; + sources.insert(path, source_code); + } + sources.into_iter().collect() + }; + + let mut address_predictor = AddressPredictor::new(); + + let instances = metadata + .contracts + .keys() + .cloned() + .collect::>(); + + let mut libraries_instances = Vec::new(); + let mut libraries_for_compiler = BTreeMap::new(); + let mut libraries_instances_addresses = HashMap::new(); + + for (file, metadata_file_libraries) in metadata.libraries.iter() { + let mut file_path = path.clone(); + file_path.pop(); + file_path.push(file); + let mut file_libraries = BTreeMap::new(); + for (name, instance) in metadata_file_libraries.iter() { + let address = address_predictor.next_address( + web3::types::Address::from_str(DEFAULT_CALLER_ADDRESS) + .expect("Invalid default caller address constant"), + ); + file_libraries.insert( + name.to_owned(), + format!("0x{}", crate::utils::address_as_string(&address)), + ); + libraries_instances_addresses.insert(instance.to_owned(), address); + libraries_instances.push(instance.to_owned()); + } + libraries_for_compiler.insert(file_path.to_string_lossy().to_string(), file_libraries); + } + + let mut cases = Vec::with_capacity(metadata.cases.len()); + for mut case in metadata.cases.into_iter() { + let case_name = format!("{}::{}", test_name, case.name); + if case.ignore { + Summary::ignored(summary.clone(), case_name); + continue; + } + + if !filters.check_case_path(&case_name) { + continue; + } + + match case.normalize_deployer_calls(&instances, &libraries_instances) { + Ok(_) => {} + Err(error) => { + Summary::invalid(summary, None, test_name, error); + return None; + } + } + case.normalize_expected(); + cases.push(case); + } + + metadata.cases = cases; + + Some(Self { + test_name, + compiler: C::new( + sources, + libraries_for_compiler, + debug_config, + metadata.system_mode, + ), + address_predictor, + metadata, + libraries_instances: libraries_instances_addresses, + }) + } +} + +impl Buildable for MatterLabsTest +where + C: Compiler, +{ + fn build(&self, mode: Mode, summary: Arc>, filters: &Filters) -> Option { + if !filters.check_mode(&mode) { + return None; + } + + if let Some(filters) = self.metadata.modes.as_ref() { + if !Filters::check_mode_filters(&mode, filters.as_slice()) { + return None; + } + } + + if !self.compiler.check_pragmas(&mode) { + return None; + } + + let mut instances = HashMap::new(); + + let builds = match self + .compiler + .compile(&mode, false) + .map_err(|error| anyhow::anyhow!("Failed to compile sources: {}", error)) + .and_then(|builds| { + for (instance, path) in self.metadata.contracts.iter() { + let build = builds.get(path).ok_or_else(|| { + anyhow::anyhow!("{} not found in the compiler build artifacts", path) + })?; + let hash = build.bytecode_hash; + let address = self.libraries_instances.get(instance).cloned(); + instances.insert(instance.clone(), Instance::new(path.clone(), address, hash)); + } + Ok(builds + .into_values() + .map(|build| (build.bytecode_hash, build.assembly)) + .collect()) + }) { + Ok(builds) => builds, + Err(error) => { + Summary::invalid(summary, Some(mode.clone()), self.test_name.clone(), error); + return None; + } + }; + + let mut cases = Vec::with_capacity(self.metadata.cases.len()); + for case in self.metadata.cases.iter() { + let mut address_predictor = self.address_predictor.clone(); + + if let Some(filters) = case.modes.as_ref() { + if !Filters::check_mode_filters(&mode, filters.as_slice()) { + continue; + } + } + + let instances_addresses = match case + .instances_addresses( + &self + .libraries_instances + .keys() + .cloned() + .collect::>(), + &mut address_predictor, + &mode, + ) + .map_err(|error| { + anyhow::anyhow!( + "Case `{}` is invalid: Failed to compute instances addresses: {}", + case.name, + error + ) + }) { + Ok(addresses) => addresses, + Err(error) => { + Summary::invalid(summary, Some(mode.clone()), self.test_name.clone(), error); + return None; + } + }; + let mut instances = instances.clone(); + for (instance, address) in instances_addresses { + if let Some(instance) = instances.get_mut(&instance) { + instance.address = Some(address); + } + } + + let case = match Case::from_matter_labs(case, &mode, &instances, &self.compiler) + .map_err(|error| anyhow::anyhow!("Case `{}` is invalid: {}", case.name, error)) + { + Ok(case) => case, + Err(error) => { + Summary::invalid(summary, Some(mode.clone()), self.test_name.clone(), error); + return None; + } + }; + + cases.push(case); + } + + Some(Test::new( + self.test_name.clone(), + self.metadata.group.clone(), + mode, + builds, + cases, + )) + } +} diff --git a/compiler_tester/src/directories/mod.rs b/compiler_tester/src/directories/mod.rs new file mode 100644 index 00000000..ae1568f4 --- /dev/null +++ b/compiler_tester/src/directories/mod.rs @@ -0,0 +1,62 @@ +//! +//! The buildable compiler test trait. +//! + +pub mod ethereum; +pub mod matter_labs; + +use std::path::Path; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::compilers::Compiler; +use crate::filters::Filters; +use crate::summary::Summary; +use crate::test::Test; + +/// +/// The buildable compiler test trait. +/// +pub trait Buildable: Send + Sync + 'static { + /// + /// Builds the test. + /// + fn build(&self, mode: Mode, summary: Arc>, filters: &Filters) -> Option; +} + +/// +/// The compiler tests directory trait. +/// +pub trait TestsDirectory +where + C: Compiler, +{ + /// + /// The test type. + /// + type Test: Buildable; + + /// + /// Returns all directory tests. + /// + fn all_tests( + directory_path: &Path, + extension: &'static str, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> anyhow::Result>; + + /// + /// Returns a single test. + /// + fn single_test( + directory_path: &Path, + test_path: &Path, + extension: &'static str, + summary: Arc>, + debug_config: Option, + filters: &Filters, + ) -> anyhow::Result>; +} diff --git a/compiler_tester/src/filters.rs b/compiler_tester/src/filters.rs new file mode 100644 index 00000000..95aa632a --- /dev/null +++ b/compiler_tester/src/filters.rs @@ -0,0 +1,145 @@ +//! +//! The compiler tests filters. +//! + +use std::collections::HashSet; + +use crate::compilers::mode::Mode; + +/// +/// The compiler tests filters. +/// +pub struct Filters { + /// The path filters. + path_filters: Vec, + /// The mode filters. + mode_filters: Vec, + /// The group filters. + group_filters: HashSet, +} + +impl Filters { + /// + /// A shortcut constructor. + /// + pub fn new( + path_filters: Vec, + mode_filters: Vec, + group_filters: Vec, + ) -> Self { + Self { + path_filters, + mode_filters, + group_filters: group_filters.into_iter().collect(), + } + } + + /// + /// Check if the test path is compatible with the filters. + /// + pub fn check_test_path(&self, path: &str) -> bool { + self.path_filters.is_empty() + || self + .path_filters + .iter() + .any(|filter| path.contains(&filter[..filter.find("::").unwrap_or(filter.len())])) + } + + /// + /// Check if the test case path is compatible with the filters. + /// + pub fn check_case_path(&self, path: &str) -> bool { + self.path_filters.is_empty() || self.path_filters.iter().any(|filter| path.contains(filter)) + } + + /// + /// Check if the mode is compatible with the filters. + /// + pub fn check_mode(&self, mode: &Mode) -> bool { + self.mode_filters.is_empty() + || self + .mode_filters + .iter() + .any(|filter| Self::normalize_mode(mode, filter).contains(filter)) + } + + /// + /// Check if the test group is compatible with the filters. + /// + pub fn check_group(&self, group: &Option) -> bool { + if self.group_filters.is_empty() { + return true; + } + if let Some(group) = group { + !self.group_filters.contains(group) + } else { + false + } + } + + /// + /// Checks if the mode is compatible with the specified filters. + /// + pub fn check_mode_filters(mode: &Mode, filters: &[String]) -> bool { + if filters.is_empty() { + return true; + } + for filter in filters.iter() { + let mut split = filter.split_whitespace(); + let mode_filter = split.next().unwrap_or_default(); + let normalized_mode = Self::normalize_mode(mode, mode_filter); + if !normalized_mode.contains(mode_filter) { + continue; + } + + let version = match split.next() { + Some(version) => version, + None => return true, + }; + if let Ok(version_req) = semver::VersionReq::parse(version) { + if mode.check_version(&version_req) { + return true; + } + } + } + false + } + + /// + /// Normalizes the mode according to the filter. + /// + fn normalize_mode(mode: &Mode, filter: &str) -> String { + let mut current = mode.to_string(); + if filter.contains("Y*") { + current = regex::Regex::new("Y[-+]") + .expect("Always valid") + .replace_all(current.as_str(), "Y*") + .to_string(); + } + if filter.contains("E*") { + current = regex::Regex::new("E[-+]") + .expect("Always valid") + .replace_all(current.as_str(), "E*") + .to_string(); + } + if filter.contains("M^") { + current = regex::Regex::new("M[3z]") + .expect("Always valid") + .replace_all(current.as_str(), "M^") + .to_string(); + } + if filter.contains("M*") { + current = regex::Regex::new("M[0123sz]") + .expect("Always valid") + .replace_all(current.as_str(), "M*") + .to_string(); + } + if filter.contains("B*") { + current = regex::Regex::new("B[0123]") + .expect("Always valid") + .replace_all(current.as_str(), "B*") + .to_string(); + } + current + } +} diff --git a/compiler_tester/src/lib.rs b/compiler_tester/src/lib.rs new file mode 100644 index 00000000..62d190d9 --- /dev/null +++ b/compiler_tester/src/lib.rs @@ -0,0 +1,221 @@ +//! +//! The compiler tester library. +//! + +pub(crate) mod compilers; +pub(crate) mod deployers; +pub(crate) mod directories; +pub(crate) mod filters; +pub(crate) mod summary; +pub(crate) mod test; +pub(crate) mod utils; +pub(crate) mod zkevm; + +pub use deployers::native_deployer::NativeDeployer; +pub use deployers::system_contract_deployer::SystemContractDeployer; +pub use filters::Filters; +pub use summary::Summary; + +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; +use std::sync::Mutex; +use std::time::Instant; + +use colored::Colorize; +use itertools::Itertools; +use rayon::iter::IntoParallelIterator; +use rayon::iter::ParallelIterator; + +use crate::compilers::downloader::Downloader as CompilerDownloader; +use crate::compilers::llvm::LLVMCompiler; +use crate::compilers::mode::Mode; +use crate::compilers::solidity::SolidityCompiler; +use crate::compilers::vyper::VyperCompiler; +use crate::compilers::yul::YulCompiler; +use crate::compilers::zkevm::zkEVMCompiler; +use crate::compilers::Compiler; +use crate::deployers::Deployer; +use crate::directories::ethereum::EthereumDirectory; +use crate::directories::matter_labs::MatterLabsDirectory; +use crate::directories::Buildable; +use crate::directories::TestsDirectory; +use crate::zkevm::zkEVM; + +/// The debug directory path. +pub const DEBUG_DIRECTORY: &str = "./debug/"; + +/// The trace directory path. +pub const TRACE_DIRECTORY: &str = "./trace/"; + +/// +/// The compiler test generic representation. +/// +type Test = (Arc, Mode); + +/// +/// The compiler-tester. +/// +pub struct CompilerTester { + /// The summary. + summary: Arc>, + /// The filters. + filters: Filters, + /// The debug config. + debug_config: Option, + /// The initial zkEVM. + initial_vm: Arc, +} + +impl CompilerTester { + /// The Solidity simple tests directory. + const SOLIDITY_SIMPLE: &'static str = "tests/solidity/simple"; + /// The Solidity complex tests directory. + const SOLIDITY_COMPLEX: &'static str = "tests/solidity/complex"; + /// The Solidity Ethereum tests directory. + const SOLIDITY_ETHEREUM: &'static str = "tests/solidity/ethereum"; + + /// The Vyper simple tests directory. + const VYPER_SIMPLE: &'static str = "tests/vyper/simple"; + /// The Vyper complex tests directory. + const VYPER_COMPLEX: &'static str = "tests/vyper/complex"; + /// The Vyper Ethereum tests directory. + const VYPER_ETHEREUM: &'static str = "tests/vyper/ethereum"; + + /// The Yul simple tests directory. + const YUL_SIMPLE: &'static str = "tests/yul"; + + /// The LLVM simple tests directory. + const LLVM_SIMPLE: &'static str = "tests/llvm"; + + /// The zkEVM simple tests directory. + const ZKEVM_SIMPLE: &'static str = "tests/zkevm"; +} + +impl CompilerTester { + /// + /// A shortcut constructor. + /// + #[allow(clippy::too_many_arguments)] + pub fn new( + summary: Arc>, + filters: Filters, + debug_config: Option, + binary_download_config_paths: Vec, + system_contracts_download_config_path: PathBuf, + system_contracts_debug_config: Option, + system_contracts_path: Option, + system_contracts_save_path: Option, + ) -> anyhow::Result { + let download_time_start = Instant::now(); + println!(" {} compiler binaries", "Downloading".bright_green().bold()); + let system_contracts_download_config = CompilerDownloader::default() + .download(system_contracts_download_config_path.as_path())?; + for config_path in binary_download_config_paths.into_iter() { + CompilerDownloader::default().download(config_path.as_path())?; + } + println!( + " {} downloading compiler binaries in {}m{:02}s", + "Finished".bright_green().bold(), + download_time_start.elapsed().as_secs() / 60, + download_time_start.elapsed().as_secs() % 60, + ); + + let initial_vm = Arc::new(zkEVM::initialize( + system_contracts_download_config, + system_contracts_debug_config, + system_contracts_path, + system_contracts_save_path, + )?); + + Ok(Self { + summary, + filters, + debug_config, + initial_vm, + }) + } + + /// + /// Runs all the tests. + /// + pub fn run(self) -> anyhow::Result<()> + where + D: Deployer, + { + let mut tests = Vec::new(); + tests.extend(self.directory::( + Self::SOLIDITY_SIMPLE, + compiler_common::EXTENSION_SOLIDITY, + )?); + tests.extend(self.directory::( + Self::VYPER_SIMPLE, + compiler_common::EXTENSION_VYPER, + )?); + tests.extend(self.directory::( + Self::YUL_SIMPLE, + compiler_common::EXTENSION_YUL, + )?); + tests.extend(self.directory::( + Self::LLVM_SIMPLE, + compiler_common::EXTENSION_LLVM_SOURCE, + )?); + tests.extend(self.directory::( + Self::ZKEVM_SIMPLE, + compiler_common::EXTENSION_ZKEVM_ASSEMBLY, + )?); + + tests.extend(self.directory::( + Self::SOLIDITY_COMPLEX, + compiler_common::EXTENSION_JSON, + )?); + tests.extend(self.directory::( + Self::VYPER_COMPLEX, + compiler_common::EXTENSION_JSON, + )?); + + tests.extend(self.directory::( + Self::SOLIDITY_ETHEREUM, + compiler_common::EXTENSION_SOLIDITY, + )?); + tests.extend(self.directory::( + Self::VYPER_ETHEREUM, + compiler_common::EXTENSION_VYPER, + )?); + + let _: Vec<()> = tests + .into_par_iter() + .map(|(test, mode)| { + if let Some(test) = test.build(mode, self.summary.clone(), &self.filters) { + test.run::(self.summary.clone(), self.initial_vm.clone()); + } + }) + .collect(); + + Ok(()) + } + + /// + /// Returns all test from the specified directory. + /// + fn directory(&self, path: &str, extension: &'static str) -> anyhow::Result> + where + C: Compiler, + T: TestsDirectory, + { + Ok(T::all_tests( + Path::new(path), + extension, + self.summary.clone(), + self.debug_config.clone(), + &self.filters, + ) + .map_err(|error| { + anyhow::anyhow!("Failed to read the tests directory `{}`: {}", path, error) + })? + .into_iter() + .map(|test| Arc::new(test) as Arc) + .cartesian_product(C::modes()) + .collect()) + } +} diff --git a/compiler_tester/src/summary/element/mod.rs b/compiler_tester/src/summary/element/mod.rs new file mode 100644 index 00000000..6d880d29 --- /dev/null +++ b/compiler_tester/src/summary/element/mod.rs @@ -0,0 +1,110 @@ +//! +//! The compiler tester summary element. +//! + +pub mod outcome; + +use colored::Colorize; + +use crate::compilers::mode::Mode; + +use self::outcome::passed_variant::PassedVariant; +use self::outcome::Outcome; + +/// +/// The compiler tester summary element. +/// +#[derive(Debug)] +pub struct Element { + /// The mode. + pub mode: Option, + /// The test name. + pub name: String, + /// The test outcome. + pub outcome: Outcome, +} + +impl Element { + /// + /// A shortcut constructor. + /// + pub fn new(mode: Option, name: String, outcome: Outcome) -> Self { + Self { + mode, + name, + outcome, + } + } + + /// + /// Prints the element. + /// + pub fn print(&self, verbosity: bool) -> Option { + match self.outcome { + Outcome::Passed { .. } if !verbosity => return None, + Outcome::Ignored => return None, + _ => {} + } + + let outcome = match self.outcome { + Outcome::Passed { .. } => "PASSED".green(), + Outcome::Failed { .. } => "FAILED".bright_red(), + Outcome::Invalid { .. } => "INVALID".red(), + Outcome::Ignored => "IGNORED".bright_black(), + }; + + let details = match self.outcome { + Outcome::Passed { + ref variant, + ref group, + } => { + let mut details = Vec::new(); + if let PassedVariant::Deploy { size, .. } = variant { + details.push(format!("size {size}").bright_white().to_string()) + }; + match variant { + PassedVariant::Deploy { cycles, .. } | PassedVariant::Runtime { cycles } => { + details.push(format!("cycles {cycles}").bright_white().to_string()) + } + _ => {} + }; + if let Some(group) = group { + details.push(format!("group '{group}'").bright_white().to_string()) + }; + if details.is_empty() { + "".to_string() + } else { + format!("({})", details.join(", ")) + } + } + Outcome::Failed { + ref expected, + ref found, + ref calldata, + } => { + format!( + "(expected {}, found {}, calldata {})", + ron::ser::to_string_pretty(expected, ron::ser::PrettyConfig::default()) + .expect("Always valid"), + ron::ser::to_string_pretty(found, ron::ser::PrettyConfig::default()) + .expect("Always valid"), + calldata, + ) + } + Outcome::Invalid { ref error } => error.to_string(), + _ => String::new(), + }; + + Some(format!( + "{:24} {:>7} {} {}", + self.mode + .as_ref() + .map(|mode| mode.to_string()) + .unwrap_or_default() + .bright_white(), + outcome, + self.name, + details + )) + } +} diff --git a/compiler_tester/src/summary/element/outcome/mod.rs b/compiler_tester/src/summary/element/outcome/mod.rs new file mode 100644 index 00000000..f3c37790 --- /dev/null +++ b/compiler_tester/src/summary/element/outcome/mod.rs @@ -0,0 +1,78 @@ +//! +//! The compiler tester summary element outcome. +//! + +pub mod passed_variant; + +use crate::test::case::input::output::Output; + +use self::passed_variant::PassedVariant; + +/// +/// The compiler tester summary element outcome. +/// +#[derive(Debug)] +pub enum Outcome { + /// The `passed` outcome. + Passed { + /// The outcome variant. + variant: PassedVariant, + /// The test group name. + group: Option, + }, + /// The `failed` outcome. The output result is incorrect. + Failed { + /// The expected result. + expected: Output, + /// The actual result. + found: Output, + /// The calldata. + calldata: String, + }, + /// The `invalid` outcome. The test is incorrect. + Invalid { + /// The building error description. + error: String, + }, + /// The `ignored` outcome. The test is ignored. + Ignored, +} + +impl Outcome { + /// + /// A shortcut constructor. + /// + pub fn passed(group: Option, variant: PassedVariant) -> Self { + Self::Passed { group, variant } + } + + /// + /// A shortcut constructor. + /// + pub fn failed(expected: Output, found: Output, calldata: Vec) -> Self { + Self::Failed { + expected, + found, + calldata: hex::encode(calldata.as_slice()), + } + } + + /// + /// A shortcut constructor. + /// + pub fn invalid(error: S) -> Self + where + S: ToString, + { + Self::Invalid { + error: error.to_string(), + } + } + + /// + /// A shortcut constructor. + /// + pub fn ignored() -> Self { + Self::Ignored + } +} diff --git a/compiler_tester/src/summary/element/outcome/passed_variant.rs b/compiler_tester/src/summary/element/outcome/passed_variant.rs new file mode 100644 index 00000000..c81a5629 --- /dev/null +++ b/compiler_tester/src/summary/element/outcome/passed_variant.rs @@ -0,0 +1,24 @@ +//! +//! The compiler tester summary element passed outcome variant. +//! + +/// +/// The compiler tester summary element passed outcome variant. +/// +#[derive(Debug)] +pub enum PassedVariant { + /// The contract deploy. + Deploy { + /// The contract size in instructions. + size: usize, + /// The number of execution cycles. + cycles: usize, + }, + /// The contract call. + Runtime { + /// The number of execution cycles. + cycles: usize, + }, + /// The special function call. + Special, +} diff --git a/compiler_tester/src/summary/mod.rs b/compiler_tester/src/summary/mod.rs new file mode 100644 index 00000000..9d8a4904 --- /dev/null +++ b/compiler_tester/src/summary/mod.rs @@ -0,0 +1,324 @@ +//! +//! The compiler tester summary. +//! + +pub mod element; + +use std::sync::Arc; +use std::sync::Mutex; + +use colored::Colorize; + +use crate::compilers::mode::Mode; +use crate::test::case::input::output::Output; + +use self::element::outcome::passed_variant::PassedVariant; +use self::element::outcome::Outcome; +use self::element::Element; + +/// +/// The compiler tester summary. +/// +#[derive(Debug)] +pub struct Summary { + /// The summary elements. + elements: Vec, + /// The output verbosity. + verbosity: bool, + /// Whether the output is suppressed. + quiet: bool, + /// The passed tests counter. + passed: usize, + /// The failed tests counter. + failed: usize, + /// The invalid tests counter. + invalid: usize, + /// The ignored tests counter. + ignored: usize, +} + +impl Summary { + /// The elements vector default capacity. + pub const ELEMENTS_INITIAL_CAPACITY: usize = 65536; + + /// + /// A shortcut constructor. + /// + pub fn new(verbosity: bool, quiet: bool) -> Self { + Self { + elements: Vec::with_capacity(Self::ELEMENTS_INITIAL_CAPACITY), + verbosity, + quiet, + passed: 0, + failed: 0, + invalid: 0, + ignored: 0, + } + } + + /// + /// Whether the test run has been successful. + /// + pub fn is_successful(&self) -> bool { + for element in self.elements.iter() { + match element.outcome { + Outcome::Passed { .. } => continue, + Outcome::Failed { .. } => return false, + Outcome::Invalid { .. } => return false, + Outcome::Ignored => continue, + } + } + + true + } + + /// + /// Returns the benchmark structure. + /// + pub fn benchmark(&self) -> benchmark_analyzer::Benchmark { + let mut benchmark = benchmark_analyzer::Benchmark::default(); + benchmark.groups.insert( + benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME.to_string(), + benchmark_analyzer::BenchmarkGroup::default(), + ); + for element in self.elements.iter() { + let (size, cycles, group) = match &element.outcome { + Outcome::Passed { + variant: PassedVariant::Deploy { size, cycles }, + group, + } => (Some(*size), *cycles, group.clone()), + Outcome::Passed { + variant: PassedVariant::Runtime { cycles }, + group, + } => (None, *cycles, group.clone()), + _ => continue, + }; + let key = format!( + "{:24} {}", + element + .mode + .as_ref() + .map(|mode| mode.to_string()) + .unwrap_or_default(), + element.name + ); + let benchmark_element = benchmark_analyzer::BenchmarkElement::new(size, cycles); + if let Some(group) = group { + benchmark + .groups + .entry(group) + .or_default() + .elements + .insert(key.clone(), benchmark_element.clone()); + } + benchmark + .groups + .get_mut(benchmark_analyzer::BENCHMARK_ALL_GROUP_NAME) + .expect("Always exists") + .elements + .insert(key, benchmark_element); + } + benchmark + } + + /// + /// Wraps data into a synchronized shared reference. + /// + pub fn wrap(self) -> Arc> { + Arc::new(Mutex::new(self)) + } + + /// + /// Extracts the data from the synchronized shared reference. + /// + pub fn unwrap_arc(summary: Arc>) -> Self { + Arc::try_unwrap(summary) + .expect("Last shared reference") + .into_inner() + .expect("Last shared reference") + } + + /// + /// Adds an invalid outcome. + /// + pub fn invalid(summary: Arc>, mode: Option, name: String, error: S) + where + S: ToString, + { + let element = Element::new(mode, name, Outcome::invalid(error)); + summary.lock().expect("Sync").push_element(element); + } + + /// + /// Adds a failed outcome. + /// + pub fn failed( + summary: Arc>, + mode: Mode, + name: String, + expected: Output, + found: Output, + calldata: Vec, + ) { + let element = Element::new(Some(mode), name, Outcome::failed(expected, found, calldata)); + summary.lock().expect("Sync").push_element(element); + } + + /// + /// Adds an ignored outcome. + /// + pub fn ignored(summary: Arc>, name: String) { + let element = Element::new(None, name, Outcome::ignored()); + summary.lock().expect("Sync").push_element(element); + } + + /// + /// Adds a passed contract deploy outcome. + /// + pub fn passed_deploy( + summary: Arc>, + mode: Mode, + name: String, + group: Option, + size: usize, + cycles: usize, + ) { + let passed_variant = PassedVariant::Deploy { size, cycles }; + Self::passed(summary, mode, name, group, passed_variant); + } + + /// + /// Adds a passed contract call outcome. + /// + pub fn passed_runtime( + summary: Arc>, + mode: Mode, + name: String, + group: Option, + cycles: usize, + ) { + let passed_variant = PassedVariant::Runtime { cycles }; + Self::passed(summary, mode, name, group, passed_variant); + } + + /// + /// Adds a passed special function call outcome. + /// + pub fn passed_special( + summary: Arc>, + mode: Mode, + name: String, + group: Option, + ) { + let passed_variant = PassedVariant::Special; + Self::passed(summary, mode, name, group, passed_variant); + } + + /// + /// Adds a passed outcome. + /// + fn passed( + summary: Arc>, + mode: Mode, + name: String, + group: Option, + passed_variant: PassedVariant, + ) { + let element = Element::new(Some(mode), name, Outcome::passed(group, passed_variant)); + summary.lock().expect("Sync").push_element(element); + } + + /// + /// Pushes an element to the summary, printing it. + /// + fn push_element(&mut self, element: Element) { + if let Some(string) = element.print(self.verbosity) { + println!("{string}"); + } + + let is_executed = match element.outcome { + Outcome::Passed { .. } => { + self.passed += 1; + true + } + Outcome::Failed { .. } => { + self.failed += 1; + true + } + Outcome::Invalid { .. } => { + self.invalid += 1; + true + } + Outcome::Ignored => { + self.ignored += 1; + false + } + }; + + if is_executed { + let milestone = if self.verbosity { + usize::pow(10, 3) + } else { + usize::pow(10, 5) + }; + + if (self.passed + self.failed + self.invalid) % milestone == 0 { + println!("{self}"); + } + } + + self.elements.push(element); + } +} + +impl std::fmt::Display for Summary { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.quiet { + return Ok(()); + } + + writeln!( + f, + "╔═══════════════════╡ INTEGRATION TESTING ╞════════════════════╗" + )?; + writeln!( + f, + "║ ║" + )?; + writeln!( + f, + "║ {:7} {:10} ║", + "PASSED".green(), + self.passed.to_string().green(), + )?; + writeln!( + f, + "║ {:7} {:10} ║", + "FAILED".bright_red(), + self.failed.to_string().bright_red(), + )?; + writeln!( + f, + "║ {:7} {:10} ║", + "INVALID".red(), + self.invalid.to_string().red(), + )?; + writeln!( + f, + "║ {:7} {:10} ║", + "IGNORED".bright_black(), + self.ignored.to_string().bright_black(), + )?; + writeln!( + f, + "║ {:10} TESTS MILESTONE ║", + self.passed + self.failed + self.invalid, + )?; + writeln!( + f, + "╚══════════════════════════════════════════════════════════════╝" + )?; + + Ok(()) + } +} diff --git a/compiler_tester/src/test/case/input/balance.rs b/compiler_tester/src/test/case/input/balance.rs new file mode 100644 index 00000000..1335a0f5 --- /dev/null +++ b/compiler_tester/src/test/case/input/balance.rs @@ -0,0 +1,60 @@ +//! +//! The balance check input variant. +//! + +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::zkevm::zkEVM; +use crate::Summary; + +/// +/// The balance check input variant. +/// +#[derive(Debug, Clone)] +pub struct Balance { + /// The account address. + address: web3::types::Address, + /// The expected balance. + balance: web3::types::U256, +} + +impl Balance { + /// + /// A shortcut constructor. + /// + pub fn new(address: web3::types::Address, balance: web3::types::U256) -> Self { + Self { address, balance } + } +} + +impl Balance { + /// + /// Run the balance check input. + /// + pub fn run( + self, + summary: Arc>, + vm: &zkEVM, + mode: Mode, + test_group: Option, + name_prefix: String, + index: usize, + ) { + let name = format!("{name_prefix}[#balance_check:{index}]"); + let found = vm.get_balance(self.address); + if found == self.balance { + Summary::passed_special(summary, mode, name, test_group); + } else { + Summary::failed( + summary, + mode, + name, + self.balance.into(), + found.into(), + self.address.to_fixed_bytes().to_vec(), + ); + } + } +} diff --git a/compiler_tester/src/test/case/input/calldata.rs b/compiler_tester/src/test/case/input/calldata.rs new file mode 100644 index 00000000..1cc1dcbe --- /dev/null +++ b/compiler_tester/src/test/case/input/calldata.rs @@ -0,0 +1,70 @@ +//! +//! The test input calldata. +//! + +use std::collections::HashMap; + +use crate::directories::matter_labs::test::metadata::case::input::calldata::Calldata as MatterLabsTestInputCalldata; +use crate::test::case::input::value::Value; +use crate::test::instance::Instance; + +/// +/// The test input calldata. +/// +#[derive(Debug, Clone, Default)] +pub struct Calldata { + /// The inner calldata bytes. + pub inner: Vec, +} + +impl Calldata { + /// + /// Try convert from Matter Labs compiler test storage data. + /// + pub fn try_from_matter_labs( + calldata: &MatterLabsTestInputCalldata, + instances: &HashMap, + ) -> anyhow::Result { + let calldata = match calldata { + MatterLabsTestInputCalldata::Value(value) => { + let hex = value.strip_prefix("0x").ok_or_else(|| { + anyhow::anyhow!("Invalid calldata value, expected hex starting with `0x`") + })?; + + hex::decode(hex) + .map_err(|err| anyhow::anyhow!("Invalid calldata hex value: {}", err))? + } + MatterLabsTestInputCalldata::List(values) => { + let calldata_vec = Value::try_from_vec_matter_labs(values, instances) + .map_err(|err| anyhow::anyhow!("Invalid calldata: {}", err))?; + let mut calldata = Vec::with_capacity(values.len()); + for value in calldata_vec { + let value = match value { + Value::Certain(value) => value, + Value::Any => anyhow::bail!("* not allowed in calldata"), + }; + let mut bytes = [0u8; compiler_common::BYTE_LENGTH_FIELD]; + value.to_big_endian(&mut bytes); + calldata.extend(bytes); + } + calldata + } + }; + Ok(Self { inner: calldata }) + } + + /// + /// Insert the selector at the beginning of the calldata. + /// + pub fn add_selector(&mut self, selector: u32) { + let mut calldata_with_selector = selector.to_be_bytes().to_vec(); + calldata_with_selector.append(&mut self.inner); + self.inner = calldata_with_selector; + } +} + +impl From> for Calldata { + fn from(value: Vec) -> Self { + Self { inner: value } + } +} diff --git a/compiler_tester/src/test/case/input/deploy.rs b/compiler_tester/src/test/case/input/deploy.rs new file mode 100644 index 00000000..9aa13f9a --- /dev/null +++ b/compiler_tester/src/test/case/input/deploy.rs @@ -0,0 +1,115 @@ +//! +//! The contract call input variant. +//! + +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::deployers::Deployer; +use crate::zkevm::zkEVM; +use crate::Summary; + +use super::calldata::Calldata; +use super::output::Output; +use super::storage::Storage; + +/// +/// The contract call input variant. +/// +#[derive(Debug, Clone)] +pub struct Deploy { + /// The contract path. + path: String, + /// The contract hash. + hash: web3::types::U256, + /// The calldata. + calldata: Calldata, + /// The caller. + caller: web3::types::Address, + /// The value in wei. + value: Option, + /// The contracts storage to set before running. + storage: Storage, + /// The expected output. + expected: Output, +} + +impl Deploy { + /// + /// A shortcut constructor. + /// + pub fn new( + path: String, + hash: web3::types::U256, + calldata: Calldata, + caller: web3::types::Address, + value: Option, + storage: Storage, + expected: Output, + ) -> Self { + Self { + path, + hash, + calldata, + caller, + value, + storage, + expected, + } + } +} + +impl Deploy { + /// + /// Run the deploy input. + /// + pub fn run( + self, + summary: Arc>, + vm: &mut zkEVM, + mode: Mode, + deployer: &mut D, + test_group: Option, + name_prefix: String, + ) where + D: Deployer, + { + let name = format!("{}[#deployer:{}]", name_prefix, self.path,); + + vm.populate_storage(self.storage.inner); + let result = match deployer.deploy::( + name.clone(), + self.caller, + self.hash, + self.calldata.inner.clone(), + self.value, + vm, + ) { + Ok(result) => result, + Err(error) => { + Summary::invalid(summary, Some(mode), name, error); + return; + } + }; + if result.output == self.expected { + let build_size = match vm.get_contract_size(self.hash) { + Ok(size) => size, + Err(error) => { + Summary::invalid(summary, Some(mode), name, error); + return; + } + }; + Summary::passed_deploy(summary, mode, name, test_group, build_size, result.cycles); + } else { + Summary::failed( + summary, + mode, + name, + self.expected, + result.output, + self.calldata.inner, + ); + } + } +} diff --git a/compiler_tester/src/test/case/input/mod.rs b/compiler_tester/src/test/case/input/mod.rs new file mode 100644 index 00000000..c3075966 --- /dev/null +++ b/compiler_tester/src/test/case/input/mod.rs @@ -0,0 +1,315 @@ +//! +//! The test input. +//! + +pub mod balance; +pub mod calldata; +pub mod deploy; +pub mod output; +pub mod runtime; +pub mod storage; +pub mod storage_empty; +pub mod value; + +use std::collections::HashMap; +use std::str::FromStr; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::compilers::Compiler; +use crate::deployers::Deployer; +use crate::directories::matter_labs::test::metadata::case::input::Input as MatterLabsTestInput; +use crate::summary::Summary; +use crate::test::instance::Instance; +use crate::zkevm::zkEVM; + +use self::balance::Balance; +use self::calldata::Calldata; +use self::deploy::Deploy; +use self::output::Output; +use self::runtime::Runtime; +use self::storage::Storage; +use self::storage_empty::StorageEmpty; + +/// +/// The test input. +/// +#[derive(Debug, Clone)] +pub enum Input { + /// The contract call. + Runtime(Runtime), + /// The contract deploy. + Deploy(Deploy), + /// The storage empty check. + StorageEmpty(StorageEmpty), + /// Check account balance. + Balance(Balance), +} + +impl Input { + /// + /// Try convert from Matter Labs compiler test metadata input. + /// + pub fn try_from_matter_labs( + input: &MatterLabsTestInput, + mode: &Mode, + instances: &HashMap, + compiler: &C, + ) -> anyhow::Result + where + C: Compiler, + { + let caller = web3::types::Address::from_str(input.caller.as_str()) + .map_err(|error| anyhow::anyhow!("Invalid caller: {}", error))?; + + let value = match input.value.as_ref() { + Some(value) => Some(if let Some(value) = value.strip_suffix(" ETH") { + u128::from_str(value) + .map_err(|error| anyhow::anyhow!("Invalid value literal: {}", error))? + .checked_mul(10u128.pow(18)) + .ok_or_else(|| anyhow::anyhow!("Overflow: value too big"))? + } else if let Some(value) = value.strip_suffix(" wei") { + u128::from_str(value) + .map_err(|error| anyhow::anyhow!("Invalid value literal: {}", error))? + } else { + anyhow::bail!("Invalid value"); + }), + None => None, + }; + + let mut calldata = Calldata::try_from_matter_labs(&input.calldata, instances) + .map_err(|error| anyhow::anyhow!("Invalid calldata: {}", error))?; + + let expected = match input.expected.as_ref() { + Some(expected) => Output::try_from_matter_labs_expected(expected, mode, instances) + .map_err(|error| anyhow::anyhow!("Invalid expected: {}", error))?, + None => Output::default(), + }; + + let storage = Storage::try_from_matter_labs(&input.storage, instances) + .map_err(|error| anyhow::anyhow!("Invalid storage: {}", error))?; + + let instance = instances + .get(&input.instance) + .ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", input.instance))?; + + let input = match input.method.as_str() { + "#deployer" => Input::Deploy(Deploy::new( + instance.path.to_owned(), + instance.code_hash, + calldata, + caller, + value, + storage, + expected, + )), + "#fallback" => { + let address = instance.address.ok_or_else(|| { + anyhow::anyhow!( + "Instance `{}` was not successfully deployed", + input.instance + ) + })?; + + Input::Runtime(Runtime::new( + "#fallback".to_string(), + address, + calldata, + caller, + value, + storage, + expected, + )) + } + entry => { + let address = instance.address.ok_or_else(|| { + anyhow::anyhow!( + "Instance `{}` was not successfully deployed", + input.instance + ) + })?; + let path = instance.path.as_str(); + let selector = compiler + .selector(mode, path, entry, false) + .map_err(|error| anyhow::anyhow!("Failed to get selector: {}", error))?; + + calldata.add_selector(selector); + + Input::Runtime(Runtime::new( + entry.to_string(), + address, + calldata, + caller, + value, + storage, + expected, + )) + } + }; + + Ok(input) + } + + /// + /// Try convert from Ethereum compiler test metadata input. + /// + pub fn try_from_ethereum( + input: &solidity_adapter::FunctionCall, + main_contract_instance: &Instance, + libraries_instances: &HashMap, + last_source: &str, + caller: &web3::types::Address, + ) -> anyhow::Result> { + let main_contract_address = main_contract_instance + .address + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Internal error: main contract address is none"))?; + let input = match input { + solidity_adapter::FunctionCall::Constructor { + calldata, + value, + events, + .. + } => { + let value = match value { + Some(value) => Some( + (*value) + .try_into() + .map_err(|error| anyhow::anyhow!("Value is too big: {}", error))?, + ), + None => None, + }; + + let expected = Output::from_ethereum_expected( + &[web3::types::U256::from_big_endian( + main_contract_address.as_bytes(), + )], + false, + events, + main_contract_address, + ); + + Some(Input::Deploy(Deploy::new( + main_contract_instance.path.to_owned(), + main_contract_instance.code_hash, + calldata.clone().into(), + *caller, + value, + Storage::default(), + expected, + ))) + } + solidity_adapter::FunctionCall::Library { name, source } => { + let library = format!( + "{}:{}", + source.clone().unwrap_or_else(|| last_source.to_string()), + name + ); + let instance = libraries_instances.get(library.as_str()).ok_or_else(|| { + anyhow::anyhow!("Internal error: Library {} not found", library) + })?; + let hash = instance.code_hash; + let address = instance + .address + .ok_or_else(|| anyhow::anyhow!("Internal error: library address is none"))?; + + let expected = Output::from_ethereum_expected( + &[web3::types::U256::from_big_endian(address.as_bytes())], + false, + &[], + main_contract_address, + ); + + Some(Input::Deploy(Deploy::new( + instance.path.to_owned(), + hash, + Calldata::default(), + *caller, + None, + Storage::default(), + expected, + ))) + } + solidity_adapter::FunctionCall::Balance { + input, expected, .. + } => { + let address = input.unwrap_or(*main_contract_address); + Some(Input::Balance(Balance::new(address, *expected))) + } + solidity_adapter::FunctionCall::StorageEmpty { expected } => { + Some(Input::StorageEmpty(StorageEmpty::new(*expected))) + } + solidity_adapter::FunctionCall::Call { + method, + calldata, + value, + expected, + failure, + events, + .. + } => { + let value = match value { + Some(value) => Some( + (*value) + .try_into() + .map_err(|error| anyhow::anyhow!("Value is too big: {}", error))?, + ), + None => None, + }; + + let expected = Output::from_ethereum_expected( + expected, + *failure, + events, + main_contract_address, + ); + + Some(Input::Runtime(Runtime::new( + method.clone(), + *main_contract_address, + calldata.clone().into(), + *caller, + value, + Storage::default(), + expected, + ))) + } + _ => None, + }; + + Ok(input) + } + + /// + /// Run the input. + /// + #[allow(clippy::too_many_arguments)] + pub fn run( + self, + summary: Arc>, + vm: &mut zkEVM, + mode: Mode, + deployer: &mut D, + test_group: Option, + name_prefix: String, + index: usize, + ) where + D: Deployer, + { + match self { + Self::Runtime(runtime) => { + runtime.run::(summary, vm, mode, test_group, name_prefix, index) + } + Self::Deploy(deploy) => { + deploy.run::<_, M>(summary, vm, mode, deployer, test_group, name_prefix) + } + Self::StorageEmpty(storage_empty) => { + storage_empty.run(summary, vm, mode, test_group, name_prefix, index) + } + Self::Balance(balance_check) => { + balance_check.run(summary, vm, mode, test_group, name_prefix, index) + } + }; + } +} diff --git a/compiler_tester/src/test/case/input/output/event.rs b/compiler_tester/src/test/case/input/output/event.rs new file mode 100644 index 00000000..035f4a1c --- /dev/null +++ b/compiler_tester/src/test/case/input/output/event.rs @@ -0,0 +1,198 @@ +//! +//! The compiler test outcome event. +//! + +use std::collections::HashMap; +use std::str::FromStr; + +use serde::Serialize; + +use crate::directories::matter_labs::test::metadata::case::input::expected::variant::extended::event::Event as MatterLabsTestExpectedEvent; +use crate::test::instance::Instance; +use crate::test::case::input::value::Value; + +/// +/// The compiler test outcome event. +/// +#[derive(Debug, Serialize, Clone)] +pub struct Event { + /// The event address. + address: Option, + /// The event topics. + topics: Vec, + /// The event values. + values: Vec, +} + +impl Event { + /// + /// A shortcut constructor. + /// + pub fn new( + address: Option, + topics: Vec, + values: Vec, + ) -> Self { + Self { + address, + topics, + values, + } + } + + /// + /// Try convert from Matter Labs compiler test metadata expected event. + /// + pub fn try_from_matter_labs( + event: &MatterLabsTestExpectedEvent, + instances: &HashMap, + ) -> anyhow::Result { + let topics = Value::try_from_vec_matter_labs(&event.topics, instances) + .map_err(|error| anyhow::anyhow!("Invalid topics: {}", error))?; + let values = Value::try_from_vec_matter_labs(&event.values, instances) + .map_err(|error| anyhow::anyhow!("Invalid values: {}", error))?; + let address = match event.address.as_ref() { + Some(address) => Some( + if let Some(instance) = address.strip_suffix(".address") { + instances + .get(instance) + .ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", instance))? + .address + .ok_or_else(|| { + anyhow::anyhow!("Instance `{}` is not successfully deployed", instance) + }) + } else { + web3::types::Address::from_str(address) + .map_err(|error| anyhow::anyhow!("Invalid address literal: {}", error)) + } + .map_err(|error| anyhow::anyhow!("Invalid event address: {}", error))?, + ), + None => None, + }; + Ok(Self { + address, + topics, + values, + }) + } + + /// + /// Convert from Ethereum compiler test metadata expected event. + /// + pub fn from_ethereum_expected( + event: &solidity_adapter::Event, + contract_address: &web3::types::Address, + ) -> Self { + let topics = event + .topics + .iter() + .map(|topic| { + let mut topic_str = crate::utils::u256_as_string(topic); + topic_str = topic_str.replace( + solidity_adapter::DEFAULT_CONTRACT_ADDRESS, + &crate::utils::address_as_string(contract_address), + ); + Value::Certain( + web3::types::U256::from_str(&topic_str) + .expect("Solidity adapter default contract address constant is invalid"), + ) + }) + .collect(); + + let values = event + .expected + .iter() + .map(|value| { + let mut value_str = crate::utils::u256_as_string(value); + value_str = value_str.replace( + solidity_adapter::DEFAULT_CONTRACT_ADDRESS, + &crate::utils::address_as_string(contract_address), + ); + Value::Certain( + web3::types::U256::from_str(&value_str) + .expect("Solidity adapter default contract address constant is invalid"), + ) + }) + .collect(); + + Self { + // The address is ignored, as Ethereum tests expect other addresses + address: None, + topics, + values, + } + } +} + +impl From<&zkevm_tester::runners::events::SolidityLikeEvent> for Event { + fn from(event: &zkevm_tester::runners::events::SolidityLikeEvent) -> Self { + let mut topics: Vec = event + .topics + .iter() + .map(|topic| Value::Certain(web3::types::U256::from_big_endian(topic.as_slice()))) + .collect(); + + // Event are written by the system contract, and the first topic is the actual msg.sender + let address = crate::utils::u256_to_address(topics.remove(0).unwrap_certain_as_ref()); + + let values: Vec = event + .data + .chunks(compiler_common::BYTE_LENGTH_FIELD) + .map(|word| { + let value = if word.len() != compiler_common::BYTE_LENGTH_FIELD { + let mut word_padded = word.to_vec(); + word_padded.extend(vec![0u8; compiler_common::BYTE_LENGTH_FIELD - word.len()]); + web3::types::U256::from_big_endian(word_padded.as_slice()) + } else { + web3::types::U256::from_big_endian(word) + }; + Value::Certain(value) + }) + .collect(); + + Self { + address: Some(address), + topics, + values, + } + } +} + +impl PartialEq for Event { + fn eq(&self, other: &Self) -> bool { + if let (Some(address1), Some(address2)) = (self.address, other.address) { + if address1 != address2 { + return false; + } + }; + + if self.topics.len() != other.topics.len() { + return false; + } + if self.values.len() != other.values.len() { + return false; + } + + for index in 0..self.topics.len() { + if let (Value::Certain(value1), Value::Certain(value2)) = + (&self.topics[index], &other.topics[index]) + { + if value1 != value2 { + return false; + } + } + } + + for index in 0..self.values.len() { + if let (Value::Certain(value1), Value::Certain(value2)) = + (&self.values[index], &other.values[index]) + { + if value1 != value2 { + return false; + } + } + } + + true + } +} diff --git a/compiler_tester/src/test/case/input/output/mod.rs b/compiler_tester/src/test/case/input/output/mod.rs new file mode 100644 index 00000000..0f029941 --- /dev/null +++ b/compiler_tester/src/test/case/input/output/mod.rs @@ -0,0 +1,267 @@ +//! +//! The compiler test outcome data. +//! + +pub mod event; + +use std::collections::HashMap; +use std::str::FromStr; + +use serde::Serialize; + +use crate::compilers::mode::Mode; +use crate::directories::matter_labs::test::metadata::case::input::expected::variant::Variant as MatterLabsTestExpectedVariant; +use crate::directories::matter_labs::test::metadata::case::input::expected::Expected as MatterLabsTestExpected; +use crate::test::case::input::value::Value; +use crate::test::instance::Instance; + +use self::event::Event; + +/// +/// The compiler test outcome data. +/// +#[derive(Debug, Default, Serialize, Clone)] +pub struct Output { + /// The return data values. + pub return_data: Vec, + /// Whether an exception is thrown, + pub exception: bool, + /// The emitted events. + pub events: Vec, +} + +impl Output { + /// + /// A shortcut constructor. + /// + pub fn new(return_data: Vec, exception: bool, events: Vec) -> Self { + Self { + return_data, + exception, + events, + } + } + + /// + /// Try convert from Matter Labs compiler test metadata expected. + /// + pub fn try_from_matter_labs_expected( + expected: &MatterLabsTestExpected, + mode: &Mode, + instances: &HashMap, + ) -> anyhow::Result { + let variants = match expected { + MatterLabsTestExpected::Single(variant) => vec![variant], + MatterLabsTestExpected::Multiple(variants) => variants.iter().collect(), + }; + let variant = variants + .into_iter() + .find(|variant| { + let version = match variant { + MatterLabsTestExpectedVariant::Simple(_) => None, + MatterLabsTestExpectedVariant::Extended(inner) => { + inner.compiler_version.as_ref() + } + }; + match version { + Some(version) => mode.check_version(version), + None => true, + } + }) + .ok_or_else(|| anyhow::anyhow!("Version not covered"))?; + + let return_data = match variant { + MatterLabsTestExpectedVariant::Simple(expected) => expected, + MatterLabsTestExpectedVariant::Extended(expected) => &expected.return_data, + }; + let return_data = Value::try_from_vec_matter_labs(return_data, instances) + .map_err(|error| anyhow::anyhow!("Invalid return data: {}", error))?; + let (exception, events) = match variant { + MatterLabsTestExpectedVariant::Simple(_) => (false, Vec::new()), + MatterLabsTestExpectedVariant::Extended(expected) => ( + expected.exception, + expected + .events + .iter() + .enumerate() + .map(|(index, event)| { + Event::try_from_matter_labs(event, instances).map_err(|error| { + anyhow::anyhow!("Event {} is invalid: {}", index, error) + }) + }) + .collect::>>() + .map_err(|error| anyhow::anyhow!("Invalid events: {}", error))?, + ), + }; + + Ok(Self { + return_data, + exception, + events, + }) + } + + /// + /// Convert from Ethereum compiler test metadata expected. + /// + pub fn from_ethereum_expected( + expected: &[web3::types::U256], + exception: bool, + events: &[solidity_adapter::Event], + contract_address: &web3::types::Address, + ) -> Self { + let return_data = expected + .iter() + .map(|value| { + let mut value_str = crate::utils::u256_as_string(value); + value_str = value_str.replace( + solidity_adapter::DEFAULT_CONTRACT_ADDRESS, + &crate::utils::address_as_string(contract_address), + ); + Value::Certain( + web3::types::U256::from_str(&value_str) + .expect("Solidity adapter default contract address constant is invalid"), + ) + }) + .collect(); + + let events = events + .iter() + .map(|event| Event::from_ethereum_expected(event, contract_address)) + .collect(); + + Self { + return_data, + exception, + events, + } + } +} + +impl From for Output { + fn from(value: web3::types::U256) -> Self { + Self { + return_data: vec![Value::Certain(value)], + exception: false, + events: vec![], + } + } +} + +impl From for Output { + fn from(value: bool) -> Self { + let value = if value { + web3::types::U256::one() + } else { + web3::types::U256::zero() + }; + value.into() + } +} + +impl From<&zkevm_tester::runners::compiler_tests::VmSnapshot> for Output { + fn from(snapshot: &zkevm_tester::runners::compiler_tests::VmSnapshot) -> Self { + let events = snapshot + .events + .iter() + .filter(|event| { + let first_topic = event.topics.first().expect("Always exists"); + let address = crate::utils::bytes32_to_address(first_topic); + address + >= web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_UNRESTRICTED_SPACE, + ) + }) + .map(Event::from) + .collect(); + + match &snapshot.execution_result { + zkevm_tester::runners::compiler_tests::VmExecutionResult::Ok(return_data) => { + let return_data = return_data + .chunks(compiler_common::BYTE_LENGTH_FIELD) + .map(|word| { + let value = if word.len() != compiler_common::BYTE_LENGTH_FIELD { + let mut word_padded = word.to_vec(); + word_padded + .extend(vec![0u8; compiler_common::BYTE_LENGTH_FIELD - word.len()]); + web3::types::U256::from_big_endian(word_padded.as_slice()) + } else { + web3::types::U256::from_big_endian(word) + }; + Value::Certain(value) + }) + .collect(); + Self { + return_data, + exception: false, + events, + } + } + zkevm_tester::runners::compiler_tests::VmExecutionResult::Revert(return_data) => { + let return_data = return_data + .chunks(compiler_common::BYTE_LENGTH_FIELD) + .map(|word| { + let value = if word.len() != compiler_common::BYTE_LENGTH_FIELD { + let mut word_padded = word.to_vec(); + word_padded + .extend(vec![0u8; compiler_common::BYTE_LENGTH_FIELD - word.len()]); + web3::types::U256::from_big_endian(word_padded.as_slice()) + } else { + web3::types::U256::from_big_endian(word) + }; + Value::Certain(value) + }) + .collect(); + Self { + return_data, + exception: true, + events, + } + } + zkevm_tester::runners::compiler_tests::VmExecutionResult::Panic => Self { + return_data: vec![], + exception: true, + events, + }, + zkevm_tester::runners::compiler_tests::VmExecutionResult::MostLikelyDidNotFinish { + .. + } => Self { + return_data: vec![], + exception: true, + events, + }, + } + } +} + +impl PartialEq for Output { + fn eq(&self, other: &Self) -> bool { + if self.exception != other.exception { + return false; + } + if self.events.len() != other.events.len() { + return false; + } + if self.return_data.len() != other.return_data.len() { + return false; + } + + for index in 0..self.return_data.len() { + if let (Value::Certain(value1), Value::Certain(value2)) = + (&self.return_data[index], &other.return_data[index]) + { + if value1 != value2 { + return false; + } + } + } + + for index in 0..self.events.len() { + if self.events[index] != other.events[index] { + return false; + } + } + + true + } +} diff --git a/compiler_tester/src/test/case/input/runtime.rs b/compiler_tester/src/test/case/input/runtime.rs new file mode 100644 index 00000000..83e33ed5 --- /dev/null +++ b/compiler_tester/src/test/case/input/runtime.rs @@ -0,0 +1,103 @@ +//! +//! The contract call input variant. +//! + +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::zkevm::zkEVM; +use crate::Summary; + +use super::calldata::Calldata; +use super::output::Output; +use super::storage::Storage; + +/// +/// The contract call input variant. +/// +#[derive(Debug, Clone)] +pub struct Runtime { + /// The input name. + name: String, + /// The address. + address: web3::types::Address, + /// The calldata. + calldata: Calldata, + /// The caller. + caller: web3::types::Address, + /// The value in wei. + value: Option, + /// The contracts storage to set before running. + storage: Storage, + /// The expected output. + expected: Output, +} + +impl Runtime { + /// + /// A shortcut constructor. + /// + pub fn new( + name: String, + address: web3::types::Address, + calldata: Calldata, + caller: web3::types::Address, + value: Option, + storage: Storage, + expected: Output, + ) -> Self { + Self { + name, + address, + calldata, + caller, + value, + storage, + expected, + } + } +} + +impl Runtime { + /// + /// Run the call input. + /// + pub fn run( + self, + summary: Arc>, + vm: &mut zkEVM, + mode: Mode, + test_group: Option, + name_prefix: String, + index: usize, + ) { + let name = format!("{}[{}:{}]", name_prefix, self.name, index); + vm.populate_storage(self.storage.inner); + let result = match vm.contract_call::( + name.clone(), + self.address, + self.caller, + self.value, + self.calldata.inner.clone(), + ) { + Ok(result) => result, + Err(error) => { + Summary::invalid(summary, Some(mode), name, error); + return; + } + }; + if result.output == self.expected { + Summary::passed_runtime(summary, mode, name, test_group, result.cycles); + } else { + Summary::failed( + summary, + mode, + name, + self.expected, + result.output, + self.calldata.inner, + ); + } + } +} diff --git a/compiler_tester/src/test/case/input/storage.rs b/compiler_tester/src/test/case/input/storage.rs new file mode 100644 index 00000000..44460c4a --- /dev/null +++ b/compiler_tester/src/test/case/input/storage.rs @@ -0,0 +1,82 @@ +//! +//! The test input storage data. +//! + +use std::collections::HashMap; +use std::str::FromStr; + +use crate::directories::matter_labs::test::metadata::case::input::storage::Storage as MatterLabsTestContractStorage; +use crate::test::case::input::value::Value; +use crate::test::instance::Instance; + +/// +/// The test input storage data. +/// +#[derive(Debug, Clone, Default)] +pub struct Storage { + /// The inner storage hashmap data. + pub inner: HashMap, +} + +impl Storage { + /// + /// Try convert from Matter Labs compiler test storage data. + /// + pub fn try_from_matter_labs( + storage: &HashMap, + instances: &HashMap, + ) -> anyhow::Result { + let mut result_storage = HashMap::new(); + + for (address, contract_storage) in storage.iter() { + let address = if let Some(instance) = address.strip_suffix(".address") { + instances + .get(instance) + .ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", instance))? + .address + .ok_or_else(|| { + anyhow::anyhow!("Instance `{}` is not successfully deployed", instance) + }) + } else { + web3::types::Address::from_str(address) + .map_err(|error| anyhow::anyhow!("Invalid address literal: {}", error)) + } + .map_err(|error| anyhow::anyhow!("Invalid storage address: {}", error))?; + + let contract_storage = match contract_storage { + MatterLabsTestContractStorage::List(list) => list + .iter() + .enumerate() + .map(|(key, value)| (key.to_string(), value.clone())) + .collect(), + MatterLabsTestContractStorage::Map(map) => map.clone(), + }; + for (key, value) in contract_storage.into_iter() { + let key = match Value::try_from_matter_labs(key.as_str(), instances) + .map_err(|error| anyhow::anyhow!("Invalid storage key: {}", error))? + { + Value::Certain(value) => value, + Value::Any => anyhow::bail!("Storage key can not be `*`"), + }; + let key = zkevm_tester::runners::compiler_tests::StorageKey { address, key }; + + let value = match Value::try_from_matter_labs(value.as_str(), instances) + .map_err(|error| anyhow::anyhow!("Invalid storage value: {}", error))? + { + Value::Certain(value) => value, + Value::Any => anyhow::bail!("Storage value can not be `*`"), + }; + + let mut value_bytes = [0u8; compiler_common::BYTE_LENGTH_FIELD]; + value.to_big_endian(value_bytes.as_mut_slice()); + let value = web3::types::H256::from(value_bytes); + + result_storage.insert(key, value); + } + } + + Ok(Self { + inner: result_storage, + }) + } +} diff --git a/compiler_tester/src/test/case/input/storage_empty.rs b/compiler_tester/src/test/case/input/storage_empty.rs new file mode 100644 index 00000000..998df8b8 --- /dev/null +++ b/compiler_tester/src/test/case/input/storage_empty.rs @@ -0,0 +1,58 @@ +//! +//! The storage emptiness check input variant. +//! + +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::zkevm::zkEVM; +use crate::Summary; + +/// +/// The storage emptiness check input variant. +/// +#[derive(Debug, Clone)] +pub struct StorageEmpty { + /// Whether storage is empty. + is_empty: bool, +} + +impl StorageEmpty { + /// + /// A shortcut constructor. + /// + pub fn new(is_empty: bool) -> Self { + Self { is_empty } + } +} + +impl StorageEmpty { + /// + /// Run the storage empty check input. + /// + pub fn run( + self, + summary: Arc>, + vm: &zkEVM, + mode: Mode, + test_group: Option, + name_prefix: String, + index: usize, + ) { + let name = format!("{name_prefix}[#storage_empty_check:{index}]"); + let found = vm.is_storage_empty(); + if found == self.is_empty { + Summary::passed_special(summary, mode, name, test_group); + } else { + Summary::failed( + summary, + mode, + name, + self.is_empty.into(), + found.into(), + vec![], + ); + } + } +} diff --git a/compiler_tester/src/test/case/input/value.rs b/compiler_tester/src/test/case/input/value.rs new file mode 100644 index 00000000..5e4b997f --- /dev/null +++ b/compiler_tester/src/test/case/input/value.rs @@ -0,0 +1,112 @@ +//! +//! The compiler test value. +//! + +use std::collections::HashMap; +use std::str::FromStr; + +use serde::Serialize; +use serde::Serializer; + +use crate::test::instance::Instance; + +/// +/// The compiler test value. +/// +#[derive(Debug, Clone)] +pub enum Value { + /// Any value (used for expected data). + Any, + /// The certain value. + Certain(web3::types::U256), +} + +impl Value { + /// + /// Unwrap certain value as reference. + /// + /// # Panics + /// + /// Will panic if the value is any. + /// + pub fn unwrap_certain_as_ref(&self) -> &web3::types::U256 { + match self { + Self::Certain(value) => value, + Self::Any => panic!("Value in any"), + } + } + + /// + /// Try convert from Matter Labs compiler test metadata value. + /// + pub fn try_from_matter_labs( + value: &str, + instances: &HashMap, + ) -> anyhow::Result { + if value == "*" { + return Ok(Self::Any); + } + + let value = if let Some(instance) = value.strip_suffix(".address") { + web3::types::U256::from_big_endian( + instances + .get(instance) + .ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", instance))? + .address + .ok_or_else(|| { + anyhow::anyhow!("Instance `{}` was not successfully deployed", instance) + })? + .as_bytes(), + ) + } else if let Some(value) = value.strip_prefix('-') { + let value = web3::types::U256::from_dec_str(value) + .map_err(|error| anyhow::anyhow!("Invalid decimal literal after `-`: {}", error))?; + if value > web3::types::U256::one() << 255u8 { + anyhow::bail!("Decimal literal after `-` is too big"); + } + let value = value + .checked_sub(web3::types::U256::one()) + .ok_or_else(|| anyhow::anyhow!("`-0` is invalid literal"))?; + web3::types::U256::max_value() + .checked_sub(value) + .expect("Always valid") + } else if let Some(value) = value.strip_prefix("0x") { + web3::types::U256::from_str(value) + .map_err(|error| anyhow::anyhow!("Invalid hexadecimal literal: {}", error))? + } else { + web3::types::U256::from_dec_str(value) + .map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))? + }; + Ok(Self::Certain(value)) + } + + /// + /// Try convert into vec of self from vec of Matter Labs compiler test metadata values. + /// + pub fn try_from_vec_matter_labs( + values: &[String], + instances: &HashMap, + ) -> anyhow::Result> { + values + .iter() + .enumerate() + .map(|(index, value)| { + Self::try_from_matter_labs(value, instances) + .map_err(|error| anyhow::anyhow!("Value {} is invalid: {}", index, error)) + }) + .collect::>>() + } +} + +impl Serialize for Value { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let value_str = match self { + Value::Certain(value) => format!("0x{}", crate::utils::u256_as_string(value)), + Value::Any => "*".to_string(), + }; + serializer.serialize_str(&value_str) + } +} diff --git a/compiler_tester/src/test/case/mod.rs b/compiler_tester/src/test/case/mod.rs new file mode 100644 index 00000000..4d3cf578 --- /dev/null +++ b/compiler_tester/src/test/case/mod.rs @@ -0,0 +1,141 @@ +//! +//! The test case. +//! + +pub mod input; + +use std::collections::HashMap; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::compilers::Compiler; +use crate::deployers::Deployer; +use crate::directories::matter_labs::test::metadata::case::Case as MatterLabsTestCase; +use crate::summary::Summary; +use crate::test::instance::Instance; +use crate::zkevm::zkEVM; + +use self::input::Input; + +/// +/// The test case. +/// +#[derive(Debug, Clone)] +pub struct Case { + /// The case name. + name: Option, + /// The case inputs. + inputs: Vec, +} + +impl Case { + /// + /// A shortcut constructor. + /// + pub fn new(name: Option, inputs: Vec) -> Self { + Self { name, inputs } + } + + /// + /// Try convert from Matter Labs compiler test metadata case. + /// + pub fn from_matter_labs( + case: &MatterLabsTestCase, + mode: &Mode, + instances: &HashMap, + compiler: &C, + ) -> anyhow::Result + where + C: Compiler, + { + let mut inputs = Vec::with_capacity(case.inputs.capacity()); + + for (index, input) in case.inputs.iter().enumerate() { + let input = + Input::try_from_matter_labs(input, mode, instances, compiler).map_err(|error| { + anyhow::anyhow!( + "Input {}(After adding deployer calls) is invalid: {}", + index, + error + ) + })?; + inputs.push(input); + } + + Ok(Self::new(Some(case.name.clone()), inputs)) + } + + /// + /// Try convert from Ethereum compiler test metadata case. + /// + pub fn from_ethereum( + case: &[solidity_adapter::FunctionCall], + main_contract_instance: &Instance, + libraries_instances: &HashMap, + last_source: &str, + ) -> anyhow::Result + where + C: Compiler, + { + let mut inputs = Vec::new(); + let mut caller = solidity_adapter::account_address(solidity_adapter::DEFAULT_ACCOUNT_INDEX); + + for (index, input) in case.iter().enumerate() { + match input { + solidity_adapter::FunctionCall::Account { input, .. } => { + caller = solidity_adapter::account_address(*input); + } + input => { + if let Some(input) = Input::try_from_ethereum( + input, + main_contract_instance, + libraries_instances, + last_source, + &caller, + ) + .map_err(|error| { + anyhow::anyhow!("Failed to proccess {} input: {}", index, error) + })? { + inputs.push(input) + } + } + } + } + + Ok(Self { name: None, inputs }) + } + + /// + /// Run the case. + /// + pub fn run( + self, + summary: Arc>, + initial_vm: zkEVM, + mode: &Mode, + test_name: String, + test_group: Option, + ) where + D: Deployer, + { + let name = if let Some(case_name) = self.name { + format!("{test_name}::{case_name}") + } else { + test_name + }; + let mut vm = initial_vm; + let mut deployer = D::new(); + for (index, input) in self.inputs.into_iter().enumerate() { + input.run::<_, M>( + summary.clone(), + &mut vm, + mode.clone(), + &mut deployer, + test_group.clone(), + name.clone(), + index, + ) + } + } +} diff --git a/compiler_tester/src/test/instance.rs b/compiler_tester/src/test/instance.rs new file mode 100644 index 00000000..988867df --- /dev/null +++ b/compiler_tester/src/test/instance.rs @@ -0,0 +1,33 @@ +//! +//! The test contract instance used for building. +//! + +/// +/// The test contract instance used for building. +/// +#[derive(Debug, Clone)] +pub struct Instance { + /// The contract path. + pub path: String, + /// The instance address. + pub address: Option, + /// The contract bytecode hash. + pub code_hash: web3::types::U256, +} + +impl Instance { + /// + /// A shortcut constructor. + /// + pub fn new( + path: String, + address: Option, + code_hash: web3::types::U256, + ) -> Self { + Self { + path, + address, + code_hash, + } + } +} diff --git a/compiler_tester/src/test/mod.rs b/compiler_tester/src/test/mod.rs new file mode 100644 index 00000000..3573ab63 --- /dev/null +++ b/compiler_tester/src/test/mod.rs @@ -0,0 +1,73 @@ +//! +//! The test. +//! + +pub mod case; +pub mod instance; + +use std::collections::HashMap; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::compilers::mode::Mode; +use crate::deployers::Deployer; +use crate::zkevm::zkEVM; +use crate::Summary; + +use self::case::Case; + +/// +/// The test. +/// +pub struct Test { + /// The test name. + name: String, + /// The test group. + group: Option, + /// The test mode. + mode: Mode, + /// The contract builds. + builds: HashMap, + /// The test cases. + cases: Vec, +} + +impl Test { + /// + /// A shortcut constructor. + /// + pub fn new( + name: String, + group: Option, + mode: Mode, + builds: HashMap, + cases: Vec, + ) -> Self { + Self { + name, + group, + mode, + builds, + cases, + } + } + + /// + /// Run the test. + /// + pub fn run(self, summary: Arc>, initial_vm: Arc) + where + D: Deployer, + { + for case in self.cases { + let vm = zkEVM::clone_with_contracts(initial_vm.clone(), self.builds.clone()); + case.run::( + summary.clone(), + vm, + &self.mode, + self.name.clone(), + self.group.clone(), + ); + } + } +} diff --git a/compiler_tester/src/utils.rs b/compiler_tester/src/utils.rs new file mode 100644 index 00000000..44856777 --- /dev/null +++ b/compiler_tester/src/utils.rs @@ -0,0 +1,50 @@ +//! +//! The compiler-tester utils. +//! + +/// +/// Overrides the default formatting for `Address`, which replaces the middle with an ellipsis. +/// +pub fn address_as_string(value: &web3::types::Address) -> String { + hex::encode(value.as_bytes()) +} + +/// +/// Overrides the default formatting for `U256`, which replaces the middle with an ellipsis. +/// +pub fn u256_as_string(value: &web3::types::U256) -> String { + let mut bytes = vec![0; compiler_common::BYTE_LENGTH_FIELD]; + value.to_big_endian(&mut bytes); + hex::encode(bytes) +} + +/// +/// Converts `[u8; 32]` into `Address`. +/// +pub fn bytes32_to_address( + bytes: &[u8; compiler_common::BYTE_LENGTH_FIELD], +) -> web3::types::Address { + web3::types::Address::from_slice( + &bytes[bytes.len() - compiler_common::BYTE_LENGTH_ETH_ADDRESS..], + ) +} + +/// +/// Converts `U256` into `Address`. +/// +pub fn u256_to_address(value: &web3::types::U256) -> web3::types::Address { + let mut bytes = vec![0; compiler_common::BYTE_LENGTH_FIELD]; + value.to_big_endian(&mut bytes); + web3::types::Address::from_slice( + &bytes[bytes.len() - compiler_common::BYTE_LENGTH_ETH_ADDRESS..], + ) +} + +/// +/// Converts `U256` into `H256`. +/// +pub fn u256_to_h256(value: &web3::types::U256) -> web3::types::H256 { + let mut bytes = vec![0; compiler_common::BYTE_LENGTH_FIELD]; + value.to_big_endian(&mut bytes); + web3::types::H256::from_slice(bytes.as_slice()) +} diff --git a/compiler_tester/src/zkevm/execution_result.rs b/compiler_tester/src/zkevm/execution_result.rs new file mode 100644 index 00000000..4858aae2 --- /dev/null +++ b/compiler_tester/src/zkevm/execution_result.rs @@ -0,0 +1,34 @@ +//! +//! The zkEVM execution result. +//! + +use crate::test::case::input::output::Output; + +/// +/// The zkEVM execution result. +/// +#[derive(Debug, Clone)] +pub struct ExecutionResult { + /// The actual snapshot result data. + pub output: Output, + /// The number of executed cycles. + pub cycles: usize, +} + +impl ExecutionResult { + /// + /// A shortcut constructor. + /// + pub fn new(output: Output, cycles: usize) -> Self { + Self { output, cycles } + } +} + +impl From<&zkevm_tester::runners::compiler_tests::VmSnapshot> for ExecutionResult { + fn from(snapshot: &zkevm_tester::runners::compiler_tests::VmSnapshot) -> Self { + Self { + output: Output::from(snapshot), + cycles: snapshot.num_cycles_used, + } + } +} diff --git a/compiler_tester/src/zkevm/mod.rs b/compiler_tester/src/zkevm/mod.rs new file mode 100644 index 00000000..07b4dabd --- /dev/null +++ b/compiler_tester/src/zkevm/mod.rs @@ -0,0 +1,414 @@ +//! +//! The zkEVM wrapper. +//! + +pub mod execution_result; +pub mod system_context; +pub mod system_contracts; + +use std::collections::HashMap; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; + +use crate::compilers::downloader::config::Config as DownloaderConfig; + +use self::execution_result::ExecutionResult; +use self::system_context::SystemContext; +use self::system_contracts::SystemContracts; + +/// +/// The zkEVM wrapper. +/// +#[derive(Clone)] +#[allow(non_camel_case_types)] +pub struct zkEVM { + /// The storage state. + storage: HashMap, + /// The deployed contracts. + deployed_contracts: HashMap, + /// The default account abstraction contract code hash. + default_aa_code_hash: web3::types::U256, + /// The known contracts. + known_contracts: HashMap, +} + +impl zkEVM { + /// + /// Creates and initializes new zkEVM instance. + /// + pub fn initialize( + system_contracts_solc_downloader_config: DownloaderConfig, + system_contracts_debug_config: Option, + system_contracts_path: Option, + system_contracts_save_path: Option, + ) -> anyhow::Result { + let solc_version = system_contracts_solc_downloader_config + .binaries + .keys() + .next() + .ok_or_else(|| { + anyhow::anyhow!( + "zkEVM initializer could find the `solc` version for system contracts" + ) + })?; + let solc_version = semver::Version::parse(solc_version.as_str())?; + + let system_contracts = SystemContracts::load_or_build( + solc_version, + system_contracts_debug_config, + system_contracts_path, + system_contracts_save_path, + )?; + + let storage = SystemContext::create_storage(); + + let mut vm = Self { + storage, + deployed_contracts: HashMap::new(), + default_aa_code_hash: system_contracts.default_aa.bytecode_hash, + known_contracts: HashMap::new(), + }; + + vm.add_known_contract( + system_contracts.default_aa.assembly, + system_contracts.default_aa.bytecode_hash, + ); + + for (address, build) in system_contracts.deployed_contracts { + vm.add_deployed_contract(address, build.bytecode_hash, Some(build.assembly)); + } + + Ok(vm) + } + + /// + /// Clones the vm instance from arc and adds known contracts. + /// + /// TODO: check if can be made copyless + /// + pub fn clone_with_contracts( + vm: Arc, + known_contracts: HashMap, + ) -> Self { + let mut new_vm = (*vm).clone(); + for (bytecode_hash, assembly) in known_contracts.into_iter() { + new_vm.add_known_contract(assembly, bytecode_hash); + } + new_vm + } + + /// + /// Runs a contract call transaction. + /// + pub fn contract_call( + &mut self, + test_name: String, + entry_address: web3::types::Address, + caller: web3::types::Address, + value: Option, + calldata: Vec, + ) -> anyhow::Result { + let context_u128_value; + let mut entry_address = entry_address; + let vm_launch_option; + + if M { + context_u128_value = 0; + if let Some(value) = value { + self.mint_ether(caller, web3::types::U256::from(value)); + + let r3 = Some(web3::types::U256::from(value)); + let r4 = Some(web3::types::U256::from_big_endian(entry_address.as_bytes())); + let r5 = Some(web3::types::U256::from(u8::from( + compiler_llvm_context::SYSTEM_CALL_BIT, + ))); + + entry_address = web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_MSG_VALUE.into(), + ); + + vm_launch_option = + zkevm_tester::runners::compiler_tests::VmLaunchOption::ManualCallABI( + zkevm_tester::runners::compiler_tests::FullABIParams { + is_constructor: false, + is_system_call: true, + r3_value: r3, + r4_value: r4, + r5_value: r5, + }, + ); + } else { + vm_launch_option = zkevm_tester::runners::compiler_tests::VmLaunchOption::Call; + } + } else { + vm_launch_option = zkevm_tester::runners::compiler_tests::VmLaunchOption::Call; + if let Some(value) = value { + self.mint_ether(entry_address, web3::types::U256::from(value)); + context_u128_value = value; + } else { + context_u128_value = 0; + } + } + + self.run( + test_name, + entry_address, + caller, + context_u128_value, + calldata, + vm_launch_option, + ) + } + + /// + /// Runs the several contracts on the VM with the specified data and returns the result. + /// + pub fn run( + &mut self, + test_name: String, + entry_address: web3::types::Address, + caller: web3::types::Address, + u128_value: u128, + calldata: Vec, + vm_launch_option: zkevm_tester::runners::compiler_tests::VmLaunchOption, + ) -> anyhow::Result { + let mut trace_file_path = PathBuf::from_str("./trace/").expect("Always valid"); + let trace_file_name = regex::Regex::new("[^A-Za-z0-9]+") + .expect("Always valid") + .replace_all(test_name.as_str(), "_") + .to_string(); + trace_file_path.push(trace_file_name); + + let context = zkevm_tester::runners::compiler_tests::VmExecutionContext::new( + entry_address, + caller, + u128_value, + 0, + ); + + let snapshot = tokio::runtime::Runtime::new() + .expect("Tokio error") + .block_on( + zkevm_tester::runners::compiler_tests::run_vm_multi_contracts( + trace_file_path.to_string_lossy().to_string(), + self.deployed_contracts.clone(), + calldata, + self.storage.clone(), + entry_address, + Some(context), + vm_launch_option, + ::MAX, + self.known_contracts.clone(), + self.default_aa_code_hash, + ), + ) + .map_err(|error| anyhow::anyhow!("Internal error: failed to run vm: {}", error))?; + + let result = ExecutionResult::from(&snapshot); + self.storage = snapshot.storage; + for (address, assembly) in snapshot.deployed_contracts.into_iter() { + if self.deployed_contracts.contains_key(&address) { + continue; + } + + self.deployed_contracts.insert(address, assembly); + } + + Ok(result) + } + + /// + /// Performs the check for the storage emptiness, that is, if all its values, except for those + /// related to system contracts and auxiliary data inaccessible by the user code, are zeros. + /// + /// Mostly used by the Ethereum tests. + /// + pub fn is_storage_empty(&self) -> bool { + for (key, value) in self.storage.iter() { + if key.address + < web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_UNRESTRICTED_SPACE, + ) + { + continue; + } + + if !value.is_zero() { + return false; + } + } + + true + } + + /// + /// Mints some Ether value for the specified caller. + /// Is needed for payable calls simulation. + /// + pub fn mint_ether(&mut self, address: web3::types::Address, amount: web3::types::U256) { + let key = Self::balance_storage_key(address); + let old_amount = web3::types::U256::from_big_endian( + self.storage + .get(&key) + .cloned() + .unwrap_or_default() + .as_bytes(), + ); + let new_amount = old_amount + amount; + let new_amount = crate::utils::u256_to_h256(&new_amount); + self.storage.insert(key, new_amount); + } + + /// + /// Burns some Ether value for the specified caller. + /// + pub fn burn_ether(&mut self, address: web3::types::Address, amount: web3::types::U256) { + let key = Self::balance_storage_key(address); + let old_amount = web3::types::U256::from_big_endian( + self.storage + .get(&key) + .cloned() + .unwrap_or_default() + .as_bytes(), + ); + let new_amount = old_amount - amount; + let new_amount = crate::utils::u256_to_h256(&new_amount); + self.storage.insert(key, new_amount); + } + + /// + /// Returns the balance of the specified address. + /// + pub fn get_balance(&self, address: web3::types::Address) -> web3::types::U256 { + let key = Self::balance_storage_key(address); + let balance = self.storage.get(&key).copied().unwrap_or_default(); + web3::types::U256::from_big_endian(balance.as_bytes()) + } + + /// + /// Set contract as deployed on `address`. If `assembly` is none - trying to get assembly from known contracts. + /// + /// # Panics + /// + /// Will panic if some contract already deployed at `address` or `assembly` in none and contract is not found in known contracts. + /// + pub fn add_deployed_contract( + &mut self, + address: web3::types::Address, + bytecode_hash: web3::types::U256, + assembly: Option, + ) { + assert!( + !self.deployed_contracts.contains_key(&address), + "Contract at this address already exist" + ); + self.storage.insert( + zkevm_tester::runners::compiler_tests::StorageKey { + address: web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into(), + ), + key: web3::types::U256::from_big_endian(address.as_bytes()), + }, + crate::utils::u256_to_h256(&bytecode_hash), + ); + let assembly = match assembly { + Some(assembly) => assembly, + None => self + .known_contracts + .get(&bytecode_hash) + .expect("Contract not found in known contracts for deploy") + .clone(), + }; + self.deployed_contracts.insert(address, assembly); + } + + /// + /// Remove deployed contract. + /// + /// # Panics + /// + /// Will panic if any contract is not deployed at `address` + /// + pub fn remove_deployed_contract(&mut self, address: web3::types::Address) { + self.storage + .remove(&zkevm_tester::runners::compiler_tests::StorageKey { + address: web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into(), + ), + key: web3::types::U256::from_big_endian(address.as_bytes()), + }) + .expect("Contract not found"); + self.deployed_contracts + .remove(&address) + .expect("Contract not found"); + } + + /// + /// Adds values to storage. + /// + pub fn populate_storage( + &mut self, + values: HashMap, + ) { + self.storage.extend(values); + } + + /// + /// Returns known contract size by code_hash, None if not found. + /// + pub fn get_contract_size(&self, code_hash: web3::types::U256) -> anyhow::Result { + let mut assembly = self + .known_contracts + .get(&code_hash) + .cloned() + .expect("Always exists"); + Ok(assembly + .compile_to_bytecode_for_mode::<16, zkevm_opcode_defs::decoding::encoding_mode_testing::EncodingModeTesting>()?.into_iter().flatten().count()) + } + + /// + /// Gets the balance storage key for the specified address. + /// + fn balance_storage_key( + address: web3::types::Address, + ) -> zkevm_tester::runners::compiler_tests::StorageKey { + let mut key_preimage = Vec::with_capacity(compiler_common::BYTE_LENGTH_FIELD * 2); + key_preimage.extend(vec![ + 0u8; + compiler_common::BYTE_LENGTH_FIELD + - compiler_common::BYTE_LENGTH_ETH_ADDRESS + ]); + key_preimage.extend_from_slice(address.as_bytes()); + key_preimage.extend(vec![0u8; compiler_common::BYTE_LENGTH_FIELD]); + + let key_string = compiler_llvm_context::keccak256(key_preimage.as_slice()); + let key = web3::types::U256::from_str(key_string.as_str()).expect("Always valid"); + zkevm_tester::runners::compiler_tests::StorageKey { + address: web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_ETH_TOKEN.into(), + ), + key, + } + } + + /// + /// Adds known contract. + /// + fn add_known_contract( + &mut self, + assembly: zkevm_assembly::Assembly, + bytecode_hash: web3::types::U256, + ) { + self.storage.insert( + zkevm_tester::runners::compiler_tests::StorageKey { + address: web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_KNOWN_CODES_STORAGE.into(), + ), + key: bytecode_hash, + }, + web3::types::H256::from_low_u64_be(1), + ); + self.known_contracts.insert(bytecode_hash, assembly); + } +} diff --git a/compiler_tester/src/zkevm/system_context.rs b/compiler_tester/src/zkevm/system_context.rs new file mode 100644 index 00000000..b0434cfb --- /dev/null +++ b/compiler_tester/src/zkevm/system_context.rs @@ -0,0 +1,158 @@ +//! +//! The system context. +//! + +use std::collections::HashMap; +use std::ops::Add; +use std::str::FromStr; + +/// +/// The system context. +/// +pub struct SystemContext; + +impl SystemContext { + /// The system context chain id value position in the storage. + const SYSTEM_CONTEXT_CHAIN_ID_POSITION: u64 = 0; + + /// The system context origin value position in the storage. + const SYSTEM_CONTEXT_ORIGIN_POSITION: u64 = 1; + + /// The system context gas price value position in the storage. + const SYSTEM_CONTEXT_GAS_PRICE_POSITION: u64 = 2; + + /// The system context block gas limit value position in the storage. + const SYSTEM_CONTEXT_BLOCK_GAS_LIMIT_POSITION: u64 = 3; + + /// The system context coinbase value position in the storage. + const SYSTEM_CONTEXT_COINBASE_POSITION: u64 = 4; + + /// The system context difficulty value position in the storage. + const SYSTEM_CONTEXT_DIFFICULTY_POSITION: u64 = 5; + + /// The system context base fee value position in the storage. + const SYSTEM_CONTEXT_BASE_FEE_POSITION: u64 = 6; + + /// The system context current block info value position in the storage. + const SYSTEM_CONTEXT_BLOCK_INFO_POSITION: u64 = 7; + + /// The system context block hashes mapping position in the storage. + const SYSTEM_CONTEXT_BLOCK_HASH_POSITION: u64 = 8; + + /// The zkSync chain ID. + const CHAIND_ID: u64 = 280; + + /// The default origin for tests. + const TX_ORIGIN: &'static str = + "0x0000000000000000000000009292929292929292929292929292929292929292"; + + /// The default gas price for tests. + const GAS_PRICE: u64 = 3000000000; + + /// The default block gas limit for tests. + const BLOCK_GAS_LIMIT: u64 = (1 << 30); + + /// The default coin base for tests. + const COIN_BASE: &'static str = + "0x0000000000000000000000000000000000000000000000000000000000008001"; + + /// The default block difficulty for tests. + const BLOCK_DIFFICULTY: u64 = 2500000000000000; + + /// The default base fee for tests. + const BASE_FEE: u64 = 7; + + /// The default current block number for tests. + const CURRENT_BLOCK_NUMBER: u128 = 300; + + /// The default current block timestamp for tests. + const CURRENT_BLOCK_TIMESTAMP: u128 = 0xdeadbeef; + + /// The default zero block hash for tests. + const ZERO_BLOCK_HASH: &'static str = + "0x3737373737373737373737373737373737373737373737373737373737373737"; + + /// + /// Returns storage values for system context. + /// + pub fn create_storage( + ) -> HashMap { + let mut system_context_values = vec![ + ( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_CHAIN_ID_POSITION), + web3::types::H256::from_low_u64_be(Self::CHAIND_ID), + ), + ( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_ORIGIN_POSITION), + web3::types::H256::from_str(Self::TX_ORIGIN).expect("Always valid"), + ), + ( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_GAS_PRICE_POSITION), + web3::types::H256::from_low_u64_be(Self::GAS_PRICE), + ), + ( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_BLOCK_GAS_LIMIT_POSITION), + web3::types::H256::from_low_u64_be(Self::BLOCK_GAS_LIMIT), + ), + ( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_COINBASE_POSITION), + web3::types::H256::from_str(Self::COIN_BASE).expect("Always valid"), + ), + ( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_DIFFICULTY_POSITION), + web3::types::H256::from_low_u64_be(Self::BLOCK_DIFFICULTY), + ), + ( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_BASE_FEE_POSITION), + web3::types::H256::from_low_u64_be(Self::BASE_FEE), + ), + ]; + + let block_info_bytes = [ + Self::CURRENT_BLOCK_NUMBER.to_be_bytes(), + Self::CURRENT_BLOCK_TIMESTAMP.to_be_bytes(), + ] + .concat(); + + system_context_values.push(( + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_BLOCK_INFO_POSITION), + web3::types::H256::from_slice(block_info_bytes.as_slice()), + )); + + for index in 0..Self::CURRENT_BLOCK_NUMBER { + let padded_index = [[0u8; 16], index.to_be_bytes()].concat(); + let padded_slot = + web3::types::H256::from_low_u64_be(Self::SYSTEM_CONTEXT_BLOCK_HASH_POSITION) + .to_fixed_bytes() + .to_vec(); + let key = web3::signing::keccak256([padded_index, padded_slot].concat().as_slice()); + + let mut hash = web3::types::U256::from_str(Self::ZERO_BLOCK_HASH) + .expect("Invalid zero block hash const"); + hash = hash.add(web3::types::U256::from(index)); + let mut hash_bytes = [0u8; compiler_common::BYTE_LENGTH_FIELD]; + hash.to_big_endian(&mut hash_bytes); + + system_context_values.push(( + web3::types::H256::from(key), + web3::types::H256::from_slice(hash_bytes.as_slice()), + )); + } + + let mut storage = HashMap::new(); + + for (key, value) in system_context_values { + storage.insert( + zkevm_tester::runners::compiler_tests::StorageKey { + address: web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into(), + ), + key: web3::types::U256::from_big_endian(key.as_bytes()), + }, + value, + ); + } + + storage + } +} diff --git a/compiler_tester/src/zkevm/system_contracts.rs b/compiler_tester/src/zkevm/system_contracts.rs new file mode 100644 index 00000000..0b1af82a --- /dev/null +++ b/compiler_tester/src/zkevm/system_contracts.rs @@ -0,0 +1,326 @@ +//! +//! The system contracts. +//! + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; +use std::str::FromStr; +use std::time::Instant; + +use colored::Colorize; +use serde::Deserialize; +use serde::Serialize; + +use crate::compilers::build::Build as zkEVMContractBuild; +use crate::compilers::mode::solidity::Mode as SolidityMode; +use crate::compilers::mode::yul::Mode as YulMode; +use crate::compilers::mode::Mode; +use crate::compilers::solidity::SolidityCompiler; +use crate::compilers::yul::YulCompiler; +use crate::compilers::Compiler; + +/// +/// The system contracts. +/// +#[derive(Serialize, Deserialize)] +pub struct SystemContracts { + /// The deployed system contracts builds. + pub deployed_contracts: Vec<(web3::types::Address, zkEVMContractBuild)>, + /// The default account abstraction contract build. + pub default_aa: zkEVMContractBuild, +} + +impl SystemContracts { + /// The empty contract implementation path. + const PATH_EMPTY_CONTRACT: &'static str = + "system-contracts/contracts/EmptyContract.sol:EmptyContract"; + + /// The `keccak256` system contract implementation path. + const PATH_KECCAK256: &'static str = "system-contracts/contracts/precompiles/Keccak256.yul"; + + /// The `ecrecover` system contract implementation path. + const PATH_ECRECOVER: &'static str = "system-contracts/contracts/precompiles/Ecrecover.yul"; + + /// The `sha256` system contract implementation path. + const PATH_SHA256: &'static str = "system-contracts/contracts/precompiles/SHA256.yul"; + + /// The account code storage system contract implementation path. + const PATH_ACCOUNT_CODE_STORAGE: &'static str = + "system-contracts/contracts/AccountCodeStorage.sol:AccountCodeStorage"; + + /// The contract deployer system contract implementation path. + const PATH_CONTRACT_DEPLOYER: &'static str = + "system-contracts/contracts/ContractDeployer.sol:ContractDeployer"; + + /// The nonce holder system contract implementation path. + const PATH_NONCE_HOLDER: &'static str = + "system-contracts/contracts/NonceHolder.sol:NonceHolder"; + + /// The knows codes storage system contract implementation path. + const PATH_KNOWN_CODES_STORAGE: &'static str = + "system-contracts/contracts/KnownCodesStorage.sol:KnownCodesStorage"; + + /// The immutable simulator system contract implementation path. + const PATH_IMMUTABLE_SIMULATOR: &'static str = + "system-contracts/contracts/ImmutableSimulator.sol:ImmutableSimulator"; + + /// The L1-messenger system contract implementation path. + const PATH_L1_MESSENGER: &'static str = + "system-contracts/contracts/L1Messenger.sol:L1Messenger"; + + /// The `msg.value` simulator system contract implementation path. + const PATH_MSG_VALUE_SIMULATOR: &'static str = + "system-contracts/contracts/MsgValueSimulator.sol:MsgValueSimulator"; + + /// The system context system contract implementation path. + const PATH_SYSTEM_CONTEXT: &'static str = + "system-contracts/contracts/SystemContext.sol:SystemContext"; + + /// The event writer system contract implementation path. + const PATH_EVENT_WRITER: &'static str = "system-contracts/contracts/EventWriter.yul"; + + /// The ETH token system contract implementation path. + const PATH_ETH_TOKEN: &'static str = "system-contracts/contracts/L2EthToken.sol:L2EthToken"; + + /// The default account abstraction contract implementation path. + const PATH_DEFAULT_AA: &'static str = + "system-contracts/contracts/DefaultAccount.sol:DefaultAccount"; + + /// + /// Load or build the system contracts. + /// + pub fn load_or_build( + solc_version: semver::Version, + system_contracts_debug_config: Option, + system_contracts_path: Option, + system_contracts_save_path: Option, + ) -> anyhow::Result { + let system_contracts = if let Some(system_contracts_path) = system_contracts_path { + Self::load(system_contracts_path) + .map_err(|error| anyhow::anyhow!("System contracts loading: {}", error))? + } else { + Self::build(solc_version, system_contracts_debug_config) + .map_err(|error| anyhow::anyhow!("System contracts building: {}", error))? + }; + + if let Some(system_contracts_save_path) = system_contracts_save_path { + system_contracts + .save(system_contracts_save_path) + .map_err(|error| anyhow::anyhow!("System contracts saving: {}", error))?; + } + + Ok(system_contracts) + } + + /// + /// Builds the system contracts. + /// + fn build( + solc_version: semver::Version, + debug_config: Option, + ) -> anyhow::Result { + let build_time_start = Instant::now(); + println!(" {} system contracts", "Building".bright_green().bold()); + + let yul_system_contracts = [ + ( + web3::types::Address::from_low_u64_be(zkevm_opcode_defs::ADDRESS_KECCAK256.into()), + Self::PATH_KECCAK256, + ), + ( + web3::types::Address::from_low_u64_be(zkevm_opcode_defs::ADDRESS_ECRECOVER.into()), + Self::PATH_ECRECOVER, + ), + ( + web3::types::Address::from_low_u64_be(zkevm_opcode_defs::ADDRESS_SHA256.into()), + Self::PATH_SHA256, + ), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_EVENT_WRITER.into(), + ), + Self::PATH_EVENT_WRITER, + ), + ]; + + let solidity_system_contracts = vec![ + (web3::types::Address::zero(), Self::PATH_EMPTY_CONTRACT), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into(), + ), + Self::PATH_ACCOUNT_CODE_STORAGE, + ), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_NONCE_HOLDER.into(), + ), + Self::PATH_NONCE_HOLDER, + ), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_KNOWN_CODES_STORAGE.into(), + ), + Self::PATH_KNOWN_CODES_STORAGE, + ), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_IMMUTABLE_SIMULATOR.into(), + ), + Self::PATH_IMMUTABLE_SIMULATOR, + ), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into(), + ), + Self::PATH_CONTRACT_DEPLOYER, + ), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_L1_MESSENGER.into(), + ), + Self::PATH_L1_MESSENGER, + ), + ( + web3::types::Address::from_low_u64_be(zkevm_opcode_defs::ADDRESS_MSG_VALUE.into()), + Self::PATH_MSG_VALUE_SIMULATOR, + ), + ( + web3::types::Address::from_low_u64_be( + zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into(), + ), + Self::PATH_SYSTEM_CONTEXT, + ), + ( + web3::types::Address::from_low_u64_be(zkevm_opcode_defs::ADDRESS_ETH_TOKEN.into()), + Self::PATH_ETH_TOKEN, + ), + ]; + + let mut yul_file_paths = Vec::with_capacity(yul_system_contracts.len() + 1); + for (_, path) in yul_system_contracts.iter() { + let file_path = path.split(':').next().expect("Always valid"); + yul_file_paths.push(file_path.to_owned()); + } + let yul_mode = YulMode::new(compiler_llvm_context::OptimizerSettings::cycles()).into(); + let mut builds = + Self::compile::(&yul_mode, yul_file_paths, debug_config.clone())?; + + let mut solidity_file_paths = Vec::with_capacity(solidity_system_contracts.len() + 1); + for (_, path) in solidity_system_contracts.iter() { + let file_path = path.split(':').next().expect("Always valid"); + solidity_file_paths.push(file_path.to_owned()); + } + for path in glob::glob("system-contracts/**/*.sol")?.filter_map(Result::ok) { + let path = path.to_string_lossy().to_string(); + if !solidity_file_paths.contains(&path) { + solidity_file_paths.push(path); + } + } + let solidity_mode = SolidityMode::new( + solc_version, + compiler_solidity::SolcPipeline::Yul, + true, + compiler_llvm_context::OptimizerSettings::cycles(), + ) + .into(); + builds.extend(Self::compile::( + &solidity_mode, + solidity_file_paths, + debug_config, + )?); + + let mut system_contracts = + Vec::with_capacity(solidity_system_contracts.len() + yul_system_contracts.len()); + system_contracts.extend(solidity_system_contracts); + system_contracts.extend(yul_system_contracts); + + let mut deployed_contracts = Vec::with_capacity(system_contracts.len()); + for (address, path) in system_contracts.into_iter() { + let build = builds.remove(path).unwrap_or_else(|| { + panic!("System contract source file `{path}` not found in the builds") + }); + deployed_contracts.push((address, build)); + } + + let default_aa = builds.remove(Self::PATH_DEFAULT_AA).ok_or_else(|| { + anyhow::anyhow!("Default account code not found in the compiler build artifacts") + })?; + + println!( + " {} building system contracts in {}.{:03}s", + "Finished".bright_green().bold(), + build_time_start.elapsed().as_secs(), + build_time_start.elapsed().subsec_millis(), + ); + + Ok(Self { + deployed_contracts, + default_aa, + }) + } + + /// + /// Load the system contracts build from the given file. + /// + fn load(system_contracts_path: PathBuf) -> anyhow::Result { + let system_contracts_file = File::open(system_contracts_path.as_path())?; + let system_contracts: SystemContracts = bincode::deserialize_from(system_contracts_file) + .map_err(|error| anyhow::anyhow!("System contract deserialization: {}", error))?; + println!( + "System contracts build successfully loaded from `{}`", + system_contracts_path.to_string_lossy() + ); + Ok(system_contracts) + } + + /// + /// Save the system contracts build to the given file. + /// + fn save(&self, system_contracts_save_path: PathBuf) -> anyhow::Result<()> { + let system_contracts_file = File::create(system_contracts_save_path.as_path())?; + bincode::serialize_into(system_contracts_file, self) + .map_err(|error| anyhow::anyhow!("System contracts serialization: {}", error,))?; + println!( + "System contracts build successfully saved to `{}`", + system_contracts_save_path.to_string_lossy(), + ); + Ok(()) + } + + /// + /// Compiles the system contracts using the specified compiler. + /// + fn compile( + mode: &Mode, + paths: Vec, + debug_config: Option, + ) -> anyhow::Result> + where + C: Compiler, + { + let mut sources = Vec::new(); + for path in paths.into_iter() { + let file_path = if C::has_many_contracts() { + path.split(':').next().expect("Always valid").to_string() + } else { + path + }; + let source = std::fs::read_to_string( + PathBuf::from_str(file_path.as_str()) + .expect("Always valid") + .as_path(), + ) + .map_err(|error| { + anyhow::anyhow!("System contract file `{}` reading: {}", file_path, error) + })?; + sources.push((file_path.to_string(), source)); + } + let compiler = C::new(sources, BTreeMap::new(), debug_config, true); + compiler + .compile(mode, true) + .map_err(|error| anyhow::anyhow!("Failed to compile system contracts: {}", error)) + } +} diff --git a/configs/solc-bin-default.json b/configs/solc-bin-default.json new file mode 100644 index 00000000..ace6ac7e --- /dev/null +++ b/configs/solc-bin-default.json @@ -0,0 +1,458 @@ +{ + "binaries": { + "0.4.10": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.10" + }, + "0.4.11": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.11" + }, + "0.4.12": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.12" + }, + "0.4.13": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.13" + }, + "0.4.14": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.14" + }, + "0.4.15": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.15" + }, + "0.4.16": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.16" + }, + "0.4.17": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.17" + }, + "0.4.18": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.18" + }, + "0.4.19": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.19" + }, + "0.4.20": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.20" + }, + "0.4.21": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.21" + }, + "0.4.22": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.22" + }, + "0.4.23": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.23" + }, + "0.4.24": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.24" + }, + "0.4.25": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.25" + }, + "0.4.26": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.26" + }, + "0.5.0": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.0" + }, + "0.5.1": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.1" + }, + "0.5.2": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.2" + }, + "0.5.3": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.3" + }, + "0.5.4": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.4" + }, + "0.5.5": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.5" + }, + "0.5.6": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.6" + }, + "0.5.7": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.7" + }, + "0.5.8": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.8" + }, + "0.5.9": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.9" + }, + "0.5.10": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.10" + }, + "0.5.11": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.11" + }, + "0.5.12": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.12" + }, + "0.5.13": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.13" + }, + "0.5.14": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.14" + }, + "0.5.15": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.15" + }, + "0.5.16": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.16" + }, + "0.5.17": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.17" + }, + "0.6.0": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.0" + }, + "0.6.1": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.1" + }, + "0.6.2": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.2" + }, + "0.6.3": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.3" + }, + "0.6.4": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.4" + }, + "0.6.5": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.5" + }, + "0.6.6": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.6" + }, + "0.6.7": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.7" + }, + "0.6.8": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.8" + }, + "0.6.9": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.9" + }, + "0.6.10": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.10" + }, + "0.6.11": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.11" + }, + "0.6.12": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.12" + }, + "0.7.0": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.0" + }, + "0.7.1": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.1" + }, + "0.7.2": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.2" + }, + "0.7.3": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.3" + }, + "0.7.4": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.4" + }, + "0.7.5": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.5" + }, + "0.7.6": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.6" + }, + "0.8.0": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.0" + }, + "0.8.1": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.1" + }, + "0.8.2": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.2" + }, + "0.8.3": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.3" + }, + "0.8.4": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.4" + }, + "0.8.5": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.5" + }, + "0.8.6": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.6" + }, + "0.8.7": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.7" + }, + "0.8.8": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.8" + }, + "0.8.9": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.9" + }, + "0.8.10": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.10" + }, + "0.8.11": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.11" + }, + "0.8.12": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.12" + }, + "0.8.13": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.13" + }, + "0.8.14": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.14" + }, + "0.8.15": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.15" + }, + "0.8.16": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.16" + }, + "0.8.17": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.17" + }, + "0.8.18": { + "is_enabled": false, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.18" + }, + "0.8.19": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.19" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-system-contracts.json b/configs/solc-bin-system-contracts.json new file mode 100644 index 00000000..7483e448 --- /dev/null +++ b/configs/solc-bin-system-contracts.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.19": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-system-contracts" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.10.json b/configs/solc-bin-zkevm-candidate-0.4.10.json new file mode 100644 index 00000000..5175023f --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.10": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.10", + "destination": "./solc-bin/solc-0.4.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.11.json b/configs/solc-bin-zkevm-candidate-0.4.11.json new file mode 100644 index 00000000..e97035d9 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.11": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.11", + "destination": "./solc-bin/solc-0.4.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.12.json b/configs/solc-bin-zkevm-candidate-0.4.12.json new file mode 100644 index 00000000..95435c26 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.12": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.12", + "destination": "./solc-bin/solc-0.4.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.13.json b/configs/solc-bin-zkevm-candidate-0.4.13.json new file mode 100644 index 00000000..5c64e825 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.13.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.13": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.13", + "destination": "./solc-bin/solc-0.4.13" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.14.json b/configs/solc-bin-zkevm-candidate-0.4.14.json new file mode 100644 index 00000000..9bf145cf --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.14.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.14": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.14", + "destination": "./solc-bin/solc-0.4.14" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.15.json b/configs/solc-bin-zkevm-candidate-0.4.15.json new file mode 100644 index 00000000..cbcc0dbb --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.15.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.15": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.15", + "destination": "./solc-bin/solc-0.4.15" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.16.json b/configs/solc-bin-zkevm-candidate-0.4.16.json new file mode 100644 index 00000000..0692de92 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.16.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.16": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.16", + "destination": "./solc-bin/solc-0.4.16" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.17.json b/configs/solc-bin-zkevm-candidate-0.4.17.json new file mode 100644 index 00000000..573c8609 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.17.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.17": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.17", + "destination": "./solc-bin/solc-0.4.17" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.18.json b/configs/solc-bin-zkevm-candidate-0.4.18.json new file mode 100644 index 00000000..5aa62f88 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.18.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.18": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.18", + "destination": "./solc-bin/solc-0.4.18" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.19.json b/configs/solc-bin-zkevm-candidate-0.4.19.json new file mode 100644 index 00000000..57e28b1d --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.19.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.19": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.19", + "destination": "./solc-bin/solc-0.4.19" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.20.json b/configs/solc-bin-zkevm-candidate-0.4.20.json new file mode 100644 index 00000000..a26df1e3 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.20.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.20": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.20", + "destination": "./solc-bin/solc-0.4.20" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.21.json b/configs/solc-bin-zkevm-candidate-0.4.21.json new file mode 100644 index 00000000..8ee7e363 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.21.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.21": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.21", + "destination": "./solc-bin/solc-0.4.21" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.22.json b/configs/solc-bin-zkevm-candidate-0.4.22.json new file mode 100644 index 00000000..dd37e793 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.22.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.22": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.22", + "destination": "./solc-bin/solc-0.4.22" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.23.json b/configs/solc-bin-zkevm-candidate-0.4.23.json new file mode 100644 index 00000000..2f7efc55 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.23.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.23": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.23", + "destination": "./solc-bin/solc-0.4.23" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.24.json b/configs/solc-bin-zkevm-candidate-0.4.24.json new file mode 100644 index 00000000..f8fac6b9 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.24.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.24": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.24", + "destination": "./solc-bin/solc-0.4.24" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.25.json b/configs/solc-bin-zkevm-candidate-0.4.25.json new file mode 100644 index 00000000..408f5f30 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.25.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.25": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.25", + "destination": "./solc-bin/solc-0.4.25" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.4.26.json b/configs/solc-bin-zkevm-candidate-0.4.26.json new file mode 100644 index 00000000..bc6c4ae3 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.4.26.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.26": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.4.26", + "destination": "./solc-bin/solc-0.4.26" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.0.json b/configs/solc-bin-zkevm-candidate-0.5.0.json new file mode 100644 index 00000000..ba697267 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.0": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.0", + "destination": "./solc-bin/solc-0.5.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.1.json b/configs/solc-bin-zkevm-candidate-0.5.1.json new file mode 100644 index 00000000..a667b63b --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.1": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.1", + "destination": "./solc-bin/solc-0.5.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.10.json b/configs/solc-bin-zkevm-candidate-0.5.10.json new file mode 100644 index 00000000..8c0f3a71 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.10": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.10", + "destination": "./solc-bin/solc-0.5.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.11.json b/configs/solc-bin-zkevm-candidate-0.5.11.json new file mode 100644 index 00000000..e384237f --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.11": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.11", + "destination": "./solc-bin/solc-0.5.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.12.json b/configs/solc-bin-zkevm-candidate-0.5.12.json new file mode 100644 index 00000000..154b2c10 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.12": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.12", + "destination": "./solc-bin/solc-0.5.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.13.json b/configs/solc-bin-zkevm-candidate-0.5.13.json new file mode 100644 index 00000000..6fe40fdc --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.13.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.13": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.13", + "destination": "./solc-bin/solc-0.5.13" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.14.json b/configs/solc-bin-zkevm-candidate-0.5.14.json new file mode 100644 index 00000000..6f4d48e2 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.14.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.14": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.14", + "destination": "./solc-bin/solc-0.5.14" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.15.json b/configs/solc-bin-zkevm-candidate-0.5.15.json new file mode 100644 index 00000000..f44aa03d --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.15.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.15": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.15", + "destination": "./solc-bin/solc-0.5.15" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.16.json b/configs/solc-bin-zkevm-candidate-0.5.16.json new file mode 100644 index 00000000..c187289d --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.16.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.16": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.16", + "destination": "./solc-bin/solc-0.5.16" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.17.json b/configs/solc-bin-zkevm-candidate-0.5.17.json new file mode 100644 index 00000000..3d658c97 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.17.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.17": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.17", + "destination": "./solc-bin/solc-0.5.17" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.2.json b/configs/solc-bin-zkevm-candidate-0.5.2.json new file mode 100644 index 00000000..9458f4db --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.2": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.2", + "destination": "./solc-bin/solc-0.5.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.3.json b/configs/solc-bin-zkevm-candidate-0.5.3.json new file mode 100644 index 00000000..9876e267 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.3": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.3", + "destination": "./solc-bin/solc-0.5.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.4.json b/configs/solc-bin-zkevm-candidate-0.5.4.json new file mode 100644 index 00000000..8916f006 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.4": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.4", + "destination": "./solc-bin/solc-0.5.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.5.json b/configs/solc-bin-zkevm-candidate-0.5.5.json new file mode 100644 index 00000000..8092900f --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.5": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.5", + "destination": "./solc-bin/solc-0.5.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.6.json b/configs/solc-bin-zkevm-candidate-0.5.6.json new file mode 100644 index 00000000..95b16b09 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.6": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.6", + "destination": "./solc-bin/solc-0.5.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.7.json b/configs/solc-bin-zkevm-candidate-0.5.7.json new file mode 100644 index 00000000..7f1762e3 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.7.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.7": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.7", + "destination": "./solc-bin/solc-0.5.7" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.8.json b/configs/solc-bin-zkevm-candidate-0.5.8.json new file mode 100644 index 00000000..5abbdae0 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.8.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.8": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.8", + "destination": "./solc-bin/solc-0.5.8" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.5.9.json b/configs/solc-bin-zkevm-candidate-0.5.9.json new file mode 100644 index 00000000..fa6d968d --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.5.9.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.9": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.5.9", + "destination": "./solc-bin/solc-0.5.9" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.0.json b/configs/solc-bin-zkevm-candidate-0.6.0.json new file mode 100644 index 00000000..99b73e4c --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.0": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.0", + "destination": "./solc-bin/solc-0.6.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.1.json b/configs/solc-bin-zkevm-candidate-0.6.1.json new file mode 100644 index 00000000..c4a13f07 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.1": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.1", + "destination": "./solc-bin/solc-0.6.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.10.json b/configs/solc-bin-zkevm-candidate-0.6.10.json new file mode 100644 index 00000000..51af91b0 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.10": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.10", + "destination": "./solc-bin/solc-0.6.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.11.json b/configs/solc-bin-zkevm-candidate-0.6.11.json new file mode 100644 index 00000000..528e5a69 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.11": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.11", + "destination": "./solc-bin/solc-0.6.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.12.json b/configs/solc-bin-zkevm-candidate-0.6.12.json new file mode 100644 index 00000000..fd0da807 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.12": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.12", + "destination": "./solc-bin/solc-0.6.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.2.json b/configs/solc-bin-zkevm-candidate-0.6.2.json new file mode 100644 index 00000000..cf540496 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.2": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.2", + "destination": "./solc-bin/solc-0.6.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.3.json b/configs/solc-bin-zkevm-candidate-0.6.3.json new file mode 100644 index 00000000..e868e0d9 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.3": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.3", + "destination": "./solc-bin/solc-0.6.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.4.json b/configs/solc-bin-zkevm-candidate-0.6.4.json new file mode 100644 index 00000000..6a07a333 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.4": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.4", + "destination": "./solc-bin/solc-0.6.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.5.json b/configs/solc-bin-zkevm-candidate-0.6.5.json new file mode 100644 index 00000000..76d716de --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.5": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.5", + "destination": "./solc-bin/solc-0.6.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.6.json b/configs/solc-bin-zkevm-candidate-0.6.6.json new file mode 100644 index 00000000..5c67e9da --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.6": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.6", + "destination": "./solc-bin/solc-0.6.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.7.json b/configs/solc-bin-zkevm-candidate-0.6.7.json new file mode 100644 index 00000000..35a33c4b --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.7.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.7": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.7", + "destination": "./solc-bin/solc-0.6.7" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.8.json b/configs/solc-bin-zkevm-candidate-0.6.8.json new file mode 100644 index 00000000..311c98d0 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.8.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.8": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.8", + "destination": "./solc-bin/solc-0.6.8" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.6.9.json b/configs/solc-bin-zkevm-candidate-0.6.9.json new file mode 100644 index 00000000..bb531b87 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.6.9.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.9": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.6.9", + "destination": "./solc-bin/solc-0.6.9" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.7.0.json b/configs/solc-bin-zkevm-candidate-0.7.0.json new file mode 100644 index 00000000..e60c79ea --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.7.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.0": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.7.0", + "destination": "./solc-bin/solc-0.7.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.7.1.json b/configs/solc-bin-zkevm-candidate-0.7.1.json new file mode 100644 index 00000000..05f22f3e --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.7.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.1": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.7.1", + "destination": "./solc-bin/solc-0.7.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.7.2.json b/configs/solc-bin-zkevm-candidate-0.7.2.json new file mode 100644 index 00000000..e1a75c51 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.7.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.2": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.7.2", + "destination": "./solc-bin/solc-0.7.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.7.3.json b/configs/solc-bin-zkevm-candidate-0.7.3.json new file mode 100644 index 00000000..137f3a72 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.7.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.3": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.7.3", + "destination": "./solc-bin/solc-0.7.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.7.4.json b/configs/solc-bin-zkevm-candidate-0.7.4.json new file mode 100644 index 00000000..73787bf1 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.7.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.4": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.7.4", + "destination": "./solc-bin/solc-0.7.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.7.5.json b/configs/solc-bin-zkevm-candidate-0.7.5.json new file mode 100644 index 00000000..bfaf016b --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.7.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.5": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.7.5", + "destination": "./solc-bin/solc-0.7.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.7.6.json b/configs/solc-bin-zkevm-candidate-0.7.6.json new file mode 100644 index 00000000..e417d4cf --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.7.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.6": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.7.6", + "destination": "./solc-bin/solc-0.7.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.0.json b/configs/solc-bin-zkevm-candidate-0.8.0.json new file mode 100644 index 00000000..aadfea5b --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.0": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.0", + "destination": "./solc-bin/solc-0.8.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.1.json b/configs/solc-bin-zkevm-candidate-0.8.1.json new file mode 100644 index 00000000..3d8ee649 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.1": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.1", + "destination": "./solc-bin/solc-0.8.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.10.json b/configs/solc-bin-zkevm-candidate-0.8.10.json new file mode 100644 index 00000000..0922c024 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.10": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.10", + "destination": "./solc-bin/solc-0.8.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.11.json b/configs/solc-bin-zkevm-candidate-0.8.11.json new file mode 100644 index 00000000..b6a571ee --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.11": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.11", + "destination": "./solc-bin/solc-0.8.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.12.json b/configs/solc-bin-zkevm-candidate-0.8.12.json new file mode 100644 index 00000000..ac3e2f23 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.12": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.12", + "destination": "./solc-bin/solc-0.8.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.13.json b/configs/solc-bin-zkevm-candidate-0.8.13.json new file mode 100644 index 00000000..8749258c --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.13.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.13": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.13", + "destination": "./solc-bin/solc-0.8.13" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.14.json b/configs/solc-bin-zkevm-candidate-0.8.14.json new file mode 100644 index 00000000..290fd813 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.14.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.14": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.14", + "destination": "./solc-bin/solc-0.8.14" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.15.json b/configs/solc-bin-zkevm-candidate-0.8.15.json new file mode 100644 index 00000000..8c384de9 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.15.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.15": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.15", + "destination": "./solc-bin/solc-0.8.15" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.16.json b/configs/solc-bin-zkevm-candidate-0.8.16.json new file mode 100644 index 00000000..14609055 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.16.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.16": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.16", + "destination": "./solc-bin/solc-0.8.16" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.17.json b/configs/solc-bin-zkevm-candidate-0.8.17.json new file mode 100644 index 00000000..5b23f988 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.17.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.17": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.17", + "destination": "./solc-bin/solc-0.8.17" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.18.json b/configs/solc-bin-zkevm-candidate-0.8.18.json new file mode 100644 index 00000000..ea3e8ea2 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.18.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.18": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.18", + "destination": "./solc-bin/solc-0.8.18" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.2.json b/configs/solc-bin-zkevm-candidate-0.8.2.json new file mode 100644 index 00000000..55dfb777 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.2": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.2", + "destination": "./solc-bin/solc-0.8.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.3.json b/configs/solc-bin-zkevm-candidate-0.8.3.json new file mode 100644 index 00000000..6b05e4d9 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.3": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.3", + "destination": "./solc-bin/solc-0.8.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.4.json b/configs/solc-bin-zkevm-candidate-0.8.4.json new file mode 100644 index 00000000..9b347c85 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.4": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.4", + "destination": "./solc-bin/solc-0.8.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.5.json b/configs/solc-bin-zkevm-candidate-0.8.5.json new file mode 100644 index 00000000..b3dbee0c --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.5": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.5", + "destination": "./solc-bin/solc-0.8.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.6.json b/configs/solc-bin-zkevm-candidate-0.8.6.json new file mode 100644 index 00000000..28797951 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.6": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.6", + "destination": "./solc-bin/solc-0.8.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.7.json b/configs/solc-bin-zkevm-candidate-0.8.7.json new file mode 100644 index 00000000..c45d6840 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.7.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.7": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.7", + "destination": "./solc-bin/solc-0.8.7" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.8.json b/configs/solc-bin-zkevm-candidate-0.8.8.json new file mode 100644 index 00000000..0d3f71c3 --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.8.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.8": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.8", + "destination": "./solc-bin/solc-0.8.8" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-candidate-0.8.9.json b/configs/solc-bin-zkevm-candidate-0.8.9.json new file mode 100644 index 00000000..bcde516c --- /dev/null +++ b/configs/solc-bin-zkevm-candidate-0.8.9.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.9": { + "is_enabled": true, + "protocol": "file", + "source": "./solc-bin/solc-0.8.9", + "destination": "./solc-bin/solc-0.8.9" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.10.json b/configs/solc-bin-zkevm-reference-0.4.10.json new file mode 100644 index 00000000..7e64104a --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.10": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.11.json b/configs/solc-bin-zkevm-reference-0.4.11.json new file mode 100644 index 00000000..751be902 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.11": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.12.json b/configs/solc-bin-zkevm-reference-0.4.12.json new file mode 100644 index 00000000..a4cd7167 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.12": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.13.json b/configs/solc-bin-zkevm-reference-0.4.13.json new file mode 100644 index 00000000..d5b34635 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.13.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.13": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.13" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.14.json b/configs/solc-bin-zkevm-reference-0.4.14.json new file mode 100644 index 00000000..9aa57a0e --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.14.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.14": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.14" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.15.json b/configs/solc-bin-zkevm-reference-0.4.15.json new file mode 100644 index 00000000..af19c21e --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.15.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.15": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.15" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.16.json b/configs/solc-bin-zkevm-reference-0.4.16.json new file mode 100644 index 00000000..9e826e94 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.16.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.16": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.16" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.17.json b/configs/solc-bin-zkevm-reference-0.4.17.json new file mode 100644 index 00000000..f37d8fc6 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.17.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.17": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.17" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.18.json b/configs/solc-bin-zkevm-reference-0.4.18.json new file mode 100644 index 00000000..604086e2 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.18.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.18": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.18" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.19.json b/configs/solc-bin-zkevm-reference-0.4.19.json new file mode 100644 index 00000000..75e09550 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.19.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.19": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.19" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.20.json b/configs/solc-bin-zkevm-reference-0.4.20.json new file mode 100644 index 00000000..0e05baac --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.20.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.20": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.20" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.21.json b/configs/solc-bin-zkevm-reference-0.4.21.json new file mode 100644 index 00000000..adb649be --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.21.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.21": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.21" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.22.json b/configs/solc-bin-zkevm-reference-0.4.22.json new file mode 100644 index 00000000..0574ef62 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.22.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.22": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.22" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.23.json b/configs/solc-bin-zkevm-reference-0.4.23.json new file mode 100644 index 00000000..c6af4783 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.23.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.23": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.23" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.24.json b/configs/solc-bin-zkevm-reference-0.4.24.json new file mode 100644 index 00000000..8ce5de77 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.24.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.24": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.24" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.25.json b/configs/solc-bin-zkevm-reference-0.4.25.json new file mode 100644 index 00000000..fd1930f0 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.25.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.25": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.25" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.4.26.json b/configs/solc-bin-zkevm-reference-0.4.26.json new file mode 100644 index 00000000..f545050f --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.4.26.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.4.26": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.4.26" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.0.json b/configs/solc-bin-zkevm-reference-0.5.0.json new file mode 100644 index 00000000..1ff93d05 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.0": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.1.json b/configs/solc-bin-zkevm-reference-0.5.1.json new file mode 100644 index 00000000..a328cf23 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.1": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.10.json b/configs/solc-bin-zkevm-reference-0.5.10.json new file mode 100644 index 00000000..a362dfe2 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.10": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.11.json b/configs/solc-bin-zkevm-reference-0.5.11.json new file mode 100644 index 00000000..36789013 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.11": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.12.json b/configs/solc-bin-zkevm-reference-0.5.12.json new file mode 100644 index 00000000..1a6e52f6 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.12": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.13.json b/configs/solc-bin-zkevm-reference-0.5.13.json new file mode 100644 index 00000000..14b3539e --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.13.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.13": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.13" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.14.json b/configs/solc-bin-zkevm-reference-0.5.14.json new file mode 100644 index 00000000..758f5418 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.14.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.14": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.14" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.15.json b/configs/solc-bin-zkevm-reference-0.5.15.json new file mode 100644 index 00000000..fa37f0f8 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.15.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.15": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.15" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.16.json b/configs/solc-bin-zkevm-reference-0.5.16.json new file mode 100644 index 00000000..6abde4c5 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.16.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.16": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.16" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.17.json b/configs/solc-bin-zkevm-reference-0.5.17.json new file mode 100644 index 00000000..81ea45a0 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.17.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.17": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.17" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.2.json b/configs/solc-bin-zkevm-reference-0.5.2.json new file mode 100644 index 00000000..b56927ab --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.2": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.3.json b/configs/solc-bin-zkevm-reference-0.5.3.json new file mode 100644 index 00000000..bd972cba --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.3": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.4.json b/configs/solc-bin-zkevm-reference-0.5.4.json new file mode 100644 index 00000000..2c73c87b --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.4": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.5.json b/configs/solc-bin-zkevm-reference-0.5.5.json new file mode 100644 index 00000000..5e57f52e --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.5": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.6.json b/configs/solc-bin-zkevm-reference-0.5.6.json new file mode 100644 index 00000000..f75b972d --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.6": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.7.json b/configs/solc-bin-zkevm-reference-0.5.7.json new file mode 100644 index 00000000..b216a79d --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.7.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.7": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.7" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.8.json b/configs/solc-bin-zkevm-reference-0.5.8.json new file mode 100644 index 00000000..24bbe8b5 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.8.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.8": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.8" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.5.9.json b/configs/solc-bin-zkevm-reference-0.5.9.json new file mode 100644 index 00000000..9d9ae30c --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.5.9.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.5.9": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.5.9" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.0.json b/configs/solc-bin-zkevm-reference-0.6.0.json new file mode 100644 index 00000000..65ab35a9 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.0": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.1.json b/configs/solc-bin-zkevm-reference-0.6.1.json new file mode 100644 index 00000000..dfd7519e --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.1": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.10.json b/configs/solc-bin-zkevm-reference-0.6.10.json new file mode 100644 index 00000000..f21bab65 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.10": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.11.json b/configs/solc-bin-zkevm-reference-0.6.11.json new file mode 100644 index 00000000..03078db6 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.11": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.12.json b/configs/solc-bin-zkevm-reference-0.6.12.json new file mode 100644 index 00000000..3efc367a --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.12": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.2.json b/configs/solc-bin-zkevm-reference-0.6.2.json new file mode 100644 index 00000000..e41e8e4a --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.2": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.3.json b/configs/solc-bin-zkevm-reference-0.6.3.json new file mode 100644 index 00000000..78aa7fe1 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.3": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.4.json b/configs/solc-bin-zkevm-reference-0.6.4.json new file mode 100644 index 00000000..dfe4d001 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.4": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.5.json b/configs/solc-bin-zkevm-reference-0.6.5.json new file mode 100644 index 00000000..9777c895 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.5": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.6.json b/configs/solc-bin-zkevm-reference-0.6.6.json new file mode 100644 index 00000000..342f2144 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.6": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.7.json b/configs/solc-bin-zkevm-reference-0.6.7.json new file mode 100644 index 00000000..97969330 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.7.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.7": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.7" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.8.json b/configs/solc-bin-zkevm-reference-0.6.8.json new file mode 100644 index 00000000..1630b196 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.8.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.8": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.8" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.6.9.json b/configs/solc-bin-zkevm-reference-0.6.9.json new file mode 100644 index 00000000..5399e86c --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.6.9.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.6.9": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.6.9" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.7.0.json b/configs/solc-bin-zkevm-reference-0.7.0.json new file mode 100644 index 00000000..b8af9182 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.7.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.0": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.7.1.json b/configs/solc-bin-zkevm-reference-0.7.1.json new file mode 100644 index 00000000..b61f1375 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.7.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.1": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.7.2.json b/configs/solc-bin-zkevm-reference-0.7.2.json new file mode 100644 index 00000000..5374f1fb --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.7.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.2": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.7.3.json b/configs/solc-bin-zkevm-reference-0.7.3.json new file mode 100644 index 00000000..b25843e5 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.7.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.3": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.7.4.json b/configs/solc-bin-zkevm-reference-0.7.4.json new file mode 100644 index 00000000..d379edef --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.7.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.4": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.7.5.json b/configs/solc-bin-zkevm-reference-0.7.5.json new file mode 100644 index 00000000..1e50e87c --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.7.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.5": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.7.6.json b/configs/solc-bin-zkevm-reference-0.7.6.json new file mode 100644 index 00000000..086670c2 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.7.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.7.6": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.7.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.0.json b/configs/solc-bin-zkevm-reference-0.8.0.json new file mode 100644 index 00000000..c7df36ab --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.0.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.0": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.0" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.1.json b/configs/solc-bin-zkevm-reference-0.8.1.json new file mode 100644 index 00000000..1e2a1c07 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.1.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.1": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.1" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.10.json b/configs/solc-bin-zkevm-reference-0.8.10.json new file mode 100644 index 00000000..31de493f --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.10.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.10": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.10" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.11.json b/configs/solc-bin-zkevm-reference-0.8.11.json new file mode 100644 index 00000000..2fabc7f7 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.11.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.11": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.11" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.12.json b/configs/solc-bin-zkevm-reference-0.8.12.json new file mode 100644 index 00000000..23c36cfb --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.12.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.12": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.12" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.13.json b/configs/solc-bin-zkevm-reference-0.8.13.json new file mode 100644 index 00000000..01486927 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.13.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.13": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.13" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.14.json b/configs/solc-bin-zkevm-reference-0.8.14.json new file mode 100644 index 00000000..1a73a210 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.14.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.14": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.14" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.15.json b/configs/solc-bin-zkevm-reference-0.8.15.json new file mode 100644 index 00000000..53a34348 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.15.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.15": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.15" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.16.json b/configs/solc-bin-zkevm-reference-0.8.16.json new file mode 100644 index 00000000..45f64ece --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.16.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.16": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.16" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.17.json b/configs/solc-bin-zkevm-reference-0.8.17.json new file mode 100644 index 00000000..58d6b0d0 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.17.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.17": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.17" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.18.json b/configs/solc-bin-zkevm-reference-0.8.18.json new file mode 100644 index 00000000..b719023b --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.18.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.18": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.18" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.2.json b/configs/solc-bin-zkevm-reference-0.8.2.json new file mode 100644 index 00000000..f12fe7c9 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.2.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.2": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.2" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.3.json b/configs/solc-bin-zkevm-reference-0.8.3.json new file mode 100644 index 00000000..90a48950 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.3.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.3": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.3" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.4.json b/configs/solc-bin-zkevm-reference-0.8.4.json new file mode 100644 index 00000000..96d108ea --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.4.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.4": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.4" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.5.json b/configs/solc-bin-zkevm-reference-0.8.5.json new file mode 100644 index 00000000..b03d94bc --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.5.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.5": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.5" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.6.json b/configs/solc-bin-zkevm-reference-0.8.6.json new file mode 100644 index 00000000..c907aa78 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.6.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.6": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.6" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.7.json b/configs/solc-bin-zkevm-reference-0.8.7.json new file mode 100644 index 00000000..de451713 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.7.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.7": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.7" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.8.json b/configs/solc-bin-zkevm-reference-0.8.8.json new file mode 100644 index 00000000..ca96ccc5 --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.8.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.8": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.8" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/solc-bin-zkevm-reference-0.8.9.json b/configs/solc-bin-zkevm-reference-0.8.9.json new file mode 100644 index 00000000..9d50570f --- /dev/null +++ b/configs/solc-bin-zkevm-reference-0.8.9.json @@ -0,0 +1,14 @@ +{ + "binaries": { + "0.8.9": { + "is_enabled": true, + "protocol": "solc-bin-list", + "source": "https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/${PLATFORM}/list.json", + "destination": "./solc-bin/solc-0.8.9" + } + }, + "platforms": { + "linux": "linux-amd64", + "macos": "macosx-amd64" + } +} diff --git a/configs/vyper-bin-default.json b/configs/vyper-bin-default.json new file mode 100644 index 00000000..edf42584 --- /dev/null +++ b/configs/vyper-bin-default.json @@ -0,0 +1,32 @@ +{ + "binaries": { + "0.3.3": { + "is_enabled": true, + "protocol": "https", + "source": "https://github.com/vyperlang/vyper/releases/download/v0.3.3/vyper.0.3.3+commit.48e326f0.${PLATFORM}", + "destination": "./vyper-bin/vyper-0.3.3" + }, + "0.3.4": { + "is_enabled": false, + "protocol": "https", + "source": "https://github.com/vyperlang/vyper/releases/download/v0.3.4/vyper.0.3.4+commit.f31f0ec4.${PLATFORM}", + "destination": "./vyper-bin/vyper-0.3.4" + }, + "0.3.6": { + "is_enabled": false, + "protocol": "https", + "source": "https://github.com/vyperlang/vyper/releases/download/v0.3.6/vyper.0.3.6+commit.4a2124d0.${PLATFORM}", + "destination": "./vyper-bin/vyper-0.3.6" + }, + "0.3.7": { + "is_enabled": false, + "protocol": "https", + "source": "https://github.com/vyperlang/vyper/releases/download/v0.3.7/vyper.0.3.7+commit.6020b8bb.${PLATFORM}", + "destination": "./vyper-bin/vyper-0.3.7" + } + }, + "platforms": { + "linux": "linux", + "macos": "darwin" + } +} diff --git a/coverage.yaml b/coverage.yaml new file mode 100644 index 00000000..bb6ca31c --- /dev/null +++ b/coverage.yaml @@ -0,0 +1,648 @@ +--- +zinc: + simple: + loop: + do_while: "Unsupported in Zinc" + return: + loop: + do_while: "Unsupported in Zinc" + algorithm: + arrays: + standart_functions_high_order: "Function pointers unsupported in Zinc" + storage: + mapping: "Unsupported in Zinc" + reports: + "CPR-215": "-" + "CPR-220": "-" + "CPR-222": "-" + + +solidity: + simple: + array: + slice: "Unsupported in Solidity" + immutable: + aligned: + array: "Immutable variables cannot have a non-value type in Solidity" + structure: "Immutable variables cannot have a non-value type in Solidity" + bytestring: "Immutable variables cannot have a non-value type in Solidity" + string: "Immutable variables cannot have a non-value type in Solidity" + unaligned: + array: "Immutable variables cannot have a non-value type in Solidity" + structure: "Immutable variables cannot have a non-value type in Solidity" + libsolidity: "From the Solidity repository" + loop: + for: + array: "For loop in array unsupported in Solidity" + loop: "Unsupported in Solidity" + overflow: + positive: + pointer_compound: "Pointers unsupported in Solidity" + pointer_primitive: "Pointers unsupported in Solidity" + return: + loop: "Unsupported in Solidity" + structure: + nested_constant: "Constant structs unsupported in Solidity" + storage: + unaligned: + tuple: "Tuples unsupported in Solidity" + aligned: + tuple: "Tuples unsupported in Solidity" + tuple: "Unsupported in Solidity" + + complex: + create2_recursion_fact: "Recursive create unsupported in Solidity" + create_recursion_fact: "Recursive create unsupported in Solidity" + create: + recursive: "Recursive create unsupported in Solidity" + defi: + Curve: "Only for Vyper" + external: "External tests" + + +vyper: + simple: + algorithm: + arrays: + standart_functions_high_order: "Function types unsupported in Vyper" + recursion: "Recursive internal calls unsupported in Vyper" + sort: + merge_recursive: "Recursive internal calls unsupported in Vyper" + quick: "Recursive internal calls unsupported in Vyper" + tree: + treap: "Recursive internal calls unsupported in Vyper" + array: + double_dynamic: "Empty dynamic arrays unsupported in Vyper" + mutating_storage_in_constructor: "-" + slice: "Arrays slices unsupported in Vyper" + destructuring: + tuple_with_gaps: "Tuples unsupported in Vyper" + implementation: + enumeration_common_namespace: "Enums unsupported in Vyper" + enumeration_constructors: "Enums unsupported in Vyper" + enumeration_immutable_method: "Enums unsupported in Vyper" + enumeration_method_chain: "Enums unsupported in Vyper" + enumeration_method_next: "Enums unsupported in Vyper" + enumeration_method_opposite: "Enums unsupported in Vyper" + enumeration_mutable_method: "Enums unsupported in Vyper" + libsolidity: "External tests" + loop: + array: + inclusive_lower_half: "Inclusive for ranges unsupported in Vyper" + inclusive_middle_half: "Inclusive for ranges unsupported in Vyper" + inclusive_upper_half: "Inclusive for ranges unsupported in Vyper" + do_while: "Unsupported in Vyper" + loop: "Unsupported in Vyper" + range_bitlength_inference: + u8_to_u64_variable: "Variable for range unsupported in Vyper" + while: "Unsupported in Vyper" + match: "Unsupported in Vyper" + operator: + arithmetic: + exponentiation_i8: "Exponentiation with 2 variables unsupported in Vyper" + exponentiation_u8: "Exponentiation with 2 variables unsupported in Vyper" + bitwise: + and_u8: "Unsupported in Vyper" + not_u8: "Unsupported in Vyper" + or_u8: "Unsupported in Vyper" + shift_left: + u8_min_to_max: "Unsupported in Vyper" + u8_ordinar: "Unsupported in Vyper" + shift_right: + u8_max_to_min: "Unsupported in Vyper" + u8_ordinar: "Unsupported in Vyper" + xor_u8: "Unsupported in Vyper" + logical: + xor: "Unsupported in Vyper" + comparison: + address: + greater: "Unsupported in Vyper" + greater_equals: "Unsupported in Vyper" + lesser: "Unsupported in Vyper" + lesser_equals: "Unsupported in Vyper" + ternary: "Unsupported in Vyper" + overflow: + positive: + pointer_compound: "Pointers unsupported in Vyper" + pointer_primitive: "Pointers unsupported in Vyper" + pointer: "Unsupported in Vyper" + return: + calldata_array_order: "-" + loop: + do_while: "Unsupported in Vyper" + loop: "Unsupported in Vyper" + while: "Unsupported in Vyper" + match: "Unsupported in Vyper" + solidity_by_example: + simple: + calling_parent_contracts: "Inheritance unsupported in Vyper" + data_locations: "Specifying of data location unsupported in Vyper" + enum: "Unsupported in Vyper" + for_and_while_loop: "Unsupported in Vyper" + function_modifier: "Unsupported in Vyper" + shadowing_inherited_state_variables: "Inheritance unsupported in Vyper" + storage: + aligned: + tuple: "Tuples unsupported in Vyper" + unaligned: + tuple: "Tuples unsupported in Vyper" + system: "System contracts" + try_catch: "Unsupported in Vyper" + tuple: "Unsupported in Vyper" + + complex: + create_in_library: "Libraries unsupported in Vyper" + default_single_file: "Many contracts in one file unsupported in Vyper" + defi: + shitdao: "Only for Solidity" + Mooniswap: "Only for Solidity" + UniswapV2Router01: "Only for Solidity" + import_library_inline: "Libraries unsupported in Vyper" + parser: + svg: "Only for Solidity, may be rewritten in the future" + library_call_tuple: "Libraries unsupported in Vyper" + solidity_by_example: + simple: + constructor_multiple_inheritance_empty: "Inheritance unsupported in Vyper" + constructor_multiple_inheritance_order: "Inheritance unsupported in Vyper" + constructor_multiple_inheritance_with_arguments: "Inheritance unsupported in Vyper" + enum: "Unsupported in Vyper" + inheritance: "Unsupported in Vyper" + library: "Unsupported in Vyper" + structs: "Structs importing unsupported in Vyper" + try_catch: "Unsupported in Vyper" + try_catch: "Unsupported in Vyper" + + external: + abiencodedecode: + abi_decode_calldata: "Decode unsupported in Vyper" + abi_decode_simple: "Decode unsupported in Vyper" + abi_decode_simple_storage: "Decode unsupported in Vyper" + abi_encode_call: "EncodeCall unsupported in Vyper" + abi_encode_call_declaration: "EncodeCall unsupported in Vyper" + abi_encode_call_memory: "EncodeCall unsupported in Vyper" + contract_array: "Decode unsupported in Vyper" + contract_array_v2: "Decode unsupported in Vyper" + offset_overflow_in_array_decoding: "Decode unsupported in Vyper" + offset_overflow_in_array_decoding_2: "Decode unsupported in Vyper" + offset_overflow_in_array_decoding_3: "Decode unsupported in Vyper" + abiEncoderV1: + abi_decode_dynamic_array: "Decode unsupported in Vyper" + abi_decode_static_array: "Decode unsupported in Vyper" + abi_decode_static_array_v2: "Decode unsupported in Vyper" + abi_decode_trivial: "Decode unsupported in Vyper" + abi_decode_v2: "Decode unsupported in Vyper" + abi_decode_v2_calldata: "Decode unsupported in Vyper" + abi_decode_v2_storage: "Decode unsupported in Vyper" + abi_encode_decode_simple: "Decode unsupported in Vyper" + bool_out_of_bounds: "AbiCoderV1 unsupported in Vyper" + cleanup: "AbiCoderV1 unsupported in Vyper" + decode_slice: "AbiCoderV1 unsupported in Vyper" + dynamic_memory_copy: "Assembly or alternatives unsupported in Vyper" + enums: "Unsupported in Vyper" + return_dynamic_types_cross_call_out_of_range_1: "Assembly or alternatives unsupported in Vyper" + return_dynamic_types_cross_call_out_of_range_2: "Assembly or alternatives unsupported in Vyper" + abiEncoderV2: + abi_encode_v2_in_function_inherited_in_v1_contract: "Inheritance unsupported in Vyper" + abi_encode_v2_in_modifier_used_in_v1_contract: "Function modifiers unsupported in Vyper" + calldata_array_function_types: "Function types unsupported in Vyper" + calldata_array_short: "The same test without revertStrings present" + cleanup: + function: "Function types unsupported in Vyper" + enums: "Unsupported in Vyper" + struct: + struct_function: "Function types unsupported in Vyper" + validation_function_type_inside_struct: "Function types unsupported in Vyper" + accessor: + accessor_for_const_state_variable: "Public constant variables unsupported in Vyper" + arithmetics: + block_inside_unchecked: "Unchecked unsupported in Vyper" + checked_add_v1: "AbiCoderV1 unsupported in Vyper" + checked_called_by_unchecked: "Unchecked unsupported in Vyper" + checked_modifier_called_by_unchecked: "Unchecked unsupported in Vyper" + exp_associativity: "Exponentiation with 2 variables unsupported in Vyper" + unchecked_called_by_checked: "Unchecked unsupported in Vyper" + unchecked_div_by_zero: "Unchecked unsupported in Vyper" + array: + byte_array_storage_layout: "Assembly or alternatives unsupported in Vyper" + calldata_slice_access: "Slices unsupported in Vyper" + concat: + bytes_concat_empty_argument_list: "Unsupported in Vyper" + copying: + array_copy_different_packing: "Copy with different base unsupported in Vyper" + array_copy_storage_storage_different_base: "Copy with different base unsupported in Vyper" + array_copy_storage_storage_different_base_nested: "Copy with different base unsupported in Vyper" + array_copy_target_leftover: "Copy with different base unsupported in Vyper" + array_copy_target_leftover2: "Copy with different base unsupported in Vyper" + array_copy_target_simple: "Copy with different base unsupported in Vyper" + array_copy_target_simple_2: "Copy with different base unsupported in Vyper" + array_of_function_external_storage_to_storage_dynamic: "Function types unsupported in Vyper" + array_of_function_external_storage_to_storage_dynamic_different_mutability: "Function types unsupported in Vyper" + calldata_to_storage_different_base: "Copy with different base unsupported in Vyper" + copy_byte_array_to_storage: "Assembly or alternatives unsupported in Vyper" + copy_function_internal_storage_array: "Function types unsupported in Vyper" + copy_internal_function_array_to_storage: "Function types unsupported in Vyper" + dirty_memory_bytes_to_storage_copy: "Assembly or alternatives unsupported in Vyper" + dirty_memory_bytes_to_storage_copy_ir: "Assembly or alternatives unsupported in Vyper" + function_type_array_to_storage: "Function types unsupported in Vyper" + memory_to_storage_different_base: "Copy with different base unsupported in Vyper" + delete: + bytes_delete_element: "Bytes index access unsupported in Vyper" + fixed_bytes_length_access: "Fixed bytes length unsupported in Vyper" + function_array_cross_calls: "Function types unsupported in Vyper" + function_memory_array: "Function types unsupported in Vyper" + indexAccess: + bytes_index_access: "Bytes index access unsupported in Vyper" + bytes_index_access_memory: "Bytes index access unsupported in Vyper" + bytes_memory_index_access: "Bytes index access unsupported in Vyper" + fixed_bytes_index_access: "Bytes index access unsupported in Vyper" + index_access: "Bytes index access unsupported in Vyper" + invalid_encoding_for_storage_byte_array: "Assembly or alternatives unsupported in Vyper" + pop: + byte_array_pop: "Pop for bytes unsupported in Vyper" + byte_array_pop_copy_long: "Pop for bytes unsupported in Vyper" + byte_array_pop_empty_exception: "Pop for bytes unsupported in Vyper" + byte_array_pop_isolated: "Pop for bytes unsupported in Vyper" + byte_array_pop_long_storage_empty: "Pop for bytes unsupported in Vyper" + byte_array_popbyte_array_pop: "Pop for bytes unsupported in Vyper" + byte_array_pop_masking_long: "Pop for bytes unsupported in Vyper" + byte_array_pop_storage_empty: "Pop for bytes unsupported in Vyper" + byte_array_pop_long_storage_empty_garbage_ref: "Pop for bytes unsupported in Vyper" + push: + byte_array_push: "Push for bytes unsupported in Vyper" + byte_array_push_transition: "Push for bytes unsupported in Vyper" + nested_bytes_push: "Push for bytes unsupported in Vyper" + push_no_args_bytes: "Push for bytes unsupported in Vyper" + push_no_args_1d: "Push(append) without args unsupported in Vyper" + push_no_args_2d: "Push(append) without args unsupported in Vyper" + push_no_args_struct: "Push(append) without args unsupported in Vyper" + slices: "Slices unsupported in Vyper" + asmForLoop: "Assembly or alternatives unsupported in Vyper" + builtinFunctions: + function_types_sig: "Unsupported in Vyper" + msg_sig: "Unsupported in Vyper" + msg_sig_after_internal_call_is_same: "Unsupported in Vyper" + ripemd160_empty: "Unsupported in Vyper" + c99_scoping_activation: "Unsupported in Vyper" + calldata: + calldata_internal_function_pointer: "Function pointers unsupported in Vyper" + calldata_internal_library: "Libraries unsupported in Vyper" + cleanup: + bool_conversion_v1: "AbiCoderV1 unsupported in Vyper" + cleanup_address_types_shortening: "Cleanup unsupported in Vyper" + cleanup_address_types_v1: "AbiCoderV1 unsupported in Vyper" + cleanup_bytes_types_shortening_newCodeGen: "Unsupported in Vyper" + cleanup_bytes_types_shortening_OldCodeGen: "Unsupported in Vyper" + cleanup_bytes_types_v1: "AbiCoderV1 unsupported in Vyper" + cleanup_in_compound_assign: "Cleanup unsupported in Vyper" + exp_cleanup: "Cleanup unsupported in Vyper" + exp_cleanup_direct: "Cleanup unsupported in Vyper" + exp_cleanup_nonzero_base: "Cleanup unsupported in Vyper" + exp_cleanup_smaller_base: "Cleanup unsupported in Vyper" + indexed_log_topic_during_explicit_downcast: "Unsupported in Vyper" + indexed_log_topic_during_explicit_downcast_during_emissions: "Unsupported in Vyper" + constants: + asm_address_constant_regression: "Assembly or alternatives unsupported in Vyper" + asm_constant_file_level: "Assembly or alternatives unsupported in Vyper" + constants_at_file_level_referencing: "Constants referencing unsupported in Vyper" + function_unreferenced: "Unsupported in Vyper" + same_constants_different_files: "Constants importing unsupported in Vyper" + constructor: + base_constructor_arguments: "Inheritance unsupported in Vyper" + constructor_function_argument: "Function types unsupported in Vyper" + constructor_function_complex: "Function types unsupported in Vyper" + function_usage_in_constructor_arguments: "Inheritance unsupported in Vyper" + functions_called_by_constructor_through_dispatch: "Function pointers unsupported in Vyper" + inline_member_init_inheritence_without_constructor: "Inheritance unsupported in Vyper" + order_of_evaluation: "Inheritance unsupported in Vyper" + store_function_in_constructor: "Function types unsupported in Vyper" + store_function_in_constructor_packed: "Function types unsupported in Vyper" + store_internal_unused_function_in_constructor: "Function types unsupported in Vyper" + store_internal_unused_library_function_in_constructor: "Function types unsupported in Vyper" + constructor_inheritance_init_order: "Inheritance unsupported in Vyper" + constructor_inheritance_init_order_2: "Inheritance unsupported in Vyper" + constructor_inheritance_init_order_3_legacy: "Inheritance unsupported in Vyper" + constructor_inheritance_init_order_3_viaIR: "Inheritance unsupported in Vyper" + constructor_with_params_diamond_inheritance: "Inheritance unsupported in Vyper" + constructor_with_params_inheritance: "Inheritance unsupported in Vyper" + constructor_with_params_inheritance_2: "Inheritance unsupported in Vyper" + conversions: + function_type_array_to_storage: "Function types unsupported in Vyper" + empty_for_loop: "Unsupported in Vyper" + ecrecover: + ecrecover_abiV2: "Abi coder v2 unsupported in Vyper" + empty_for_loop: "Unsupported in Vyper" + enums: "Enums unsupported in Vyper" + error: "Custom errors unsupported in Vyper" + errors: "Custom errors unsupported in Vyper" + events: + event_access_through_base_name_emit: "Inheritance unsupported in Vyper" + event_anonymous: "Anonymous events unsupported in Vyper" + event_anonymous_with_signature_collision: "Anonymous events unsupported in Vyper" + event_anonymous_with_signature_collision2: "Anonymous events unsupported in Vyper" + event_anonymous_with_topics: "Anonymous events unsupported in Vyper" + event_dynamic_array_memory_v2: "Abi coder v2 unsupported in Vyper" + event_dynamic_array_storage_v2: "Abi coder v2 unsupported in Vyper" + event_indexed_function: "Function types unsupported in Vyper" + event_indexed_function2: "Function types unsupported in Vyper" + event_selector: "Unsupported in Vyper" + event_signature_in_library: "Libraries unsupported in Vyper" + events_with_same_name: "Unsupported in Vyper" + events_with_same_name_inherited_emit: "Unsupported in Vyper" + simple: "Inheritance unsupported in Vyper" + exponentiation: + literal_base: "Exponentiation overflow unsupported in Vyper" + signed_base: "Exponentiation overflow unsupported in Vyper" + small_exp: "Exponentiation with 2 variables unsupported in Vyper" + expressions: + bytes_comparison: "Unsupported in Vyper" + conditional_expression_different_types: "Conditional expression unsupported in Vyper" + conditional_expression_false_literal: "Conditional expression unsupported in Vyper" + conditional_expression_functions: "Conditional expression unsupported in Vyper" + conditional_expression_multiple: "Conditional expression unsupported in Vyper" + conditional_expression_storage_memory_1: "Conditional expression unsupported in Vyper" + conditional_expression_storage_memory_2: "Conditional expression unsupported in Vyper" + conditional_expression_true_literal: "Conditional expression unsupported in Vyper" + conditional_expression_tuples: "Conditional expression unsupported in Vyper" + conditional_expression_with_return_values: "Conditional expression unsupported in Vyper" + inc_dec_operators: "Unsupported in Vyper" + uncalled_address_transfer_send: "Unsupported in Vyper" + externalContracts: "Temporary unsupported in runner" + externalSource: "Temporary unsupported in runner" + fallback: + fallback_argument: "Arguments in fallback functions unsupported in Vyper" + fallback_argument_to_storage: "Arguments in fallback functions unsupported in Vyper" + fallback_or_receive: "Receive function unsupported in Vyper" + fallback_override: "Inheritance unsupported in Vyper" + fallback_override2: "Inheritance unsupported in Vyper" + fallback_override_multi: "Inheritance unsupported in Vyper" + inherited: "Inheritance unsupported in Vyper" + freeFunctions: "Unsupported in Vyper" + functionCall: + bare_call_no_returndatacopy: "Uses ecrecover with invalid input" + bound_function_in_function: "Function types unsupported in Vyper" + bound_function_in_var: "Unsupported in Vyper" + bound_function_to_string: "Unsupported in Vyper" + call_function_returning_function: "Function types unsupported in Vyper" + call_function_returning_nothing_via_pointer: "Function pointers unsupported in Vyper" + call_internal_function_with_multislot_arguments_via_pointer: "Function pointers unsupported in Vyper" + call_options_overload: "Unsupported in Vyper" + calling_uninitialized_function: "Function types unsupported in Vyper" + calling_uninitialized_function_in_detail: "Function types unsupported in Vyper" + calling_uninitialized_function_through_array: "Function types unsupported in Vyper" + conditional_with_arguments: "Conditional expression unsupported in Vyper" + disordered_named_args: "Unsupported in Vyper" + external_call_to_nonexisting_debugstrings: "revertStrings unsupported in Vyper" + external_public_override: "Inheritance unsupported in Vyper" + file_level_call_via_module: "Temporary unsupported in runner" + gas_and_value_basic: "Ignored for Solidity" + gas_and_value_brace_syntax: "Ignored for Solidity" + inheritance: "Inheritance unsupported in Vyper" + mapping_array_internal_argument: "Unsupported in Vyper" + mapping_internal_argument: "Unsupported in Vyper" + mapping_internal_return: "Unsupported in Vyper" + named_args: "Unsupported in Vyper" + named_args_overload: "Unsupported in Vyper" + precompile_extcodesize_check: "Uses unsupported code reflection" + return_size_bigger_than_expected: "Assembly or alternatives unsupported in Vyper" + return_size_shorter_than_expected: "Assembly or alternatives unsupported in Vyper" + return_size_shorter_than_expected_evm_version_after_homestead: "Assembly or alternatives unsupported in Vyper" + send_zero_ether: "Receive function unsupported in Vyper" + functionSelector: "Unsupported in Vyper" + functionTypes: "Function types unsupported in Vyper" + immutable: + getter: "Public immutable variables unsupported in Vyper" + getter_call_in_constructor: "Public immutable variables unsupported in Vyper" + inheritance: "Inheritance unsupported in Vyper" + internal_function_pointer: "Function pointers unsupported in Vyper" + inheritance: "Unsupported in Vyper" + inlineAssembly: "Unsupported in Vyper" + interfaceID: + homer_interfaceId: "interfaceID unsupported in Vyper" + interfaceId_events: "interfaceID unsupported in Vyper" + interfaces: "interfaceID unsupported in Vyper" + lisa_interfaceId: "interfaceID unsupported in Vyper" + interface_inheritance_conversions: "Inheritance unsupported in Vyper" + libraries: "Unsupported in Vyper" + literals: + hex_string_with_underscore: "Unsupported in Vyper" + ternary_operator_with_literal_types_overflow: "Ternary operators unsupported in Vyper" + memoryManagement: + assembly_access: "Assembly unsupported in Vyper" + return_variable: "Assembly or alternatives unsupported in Vyper" + static_memory_array_allocation: "Assembly or alternatives unsupported in Vyper" + struct_allocation: "Assembly or alternatives unsupported in Vyper" + metaTypes: + name_other_contract: "Unsupported in Vyper" + modifiers: "Unsupported in Vyper" + multiSource: "Temporary unsupported in runner" + operators: + shifts: + shift_bytes_cleanup: "Cleanup unsupported in Vyper" + shift_bytes_cleanup_viaYul: "Cleanup unsupported in Vyper" + shift_constant_left_assignment: "Shift left assignment operator unsupported in Vyper" + shift_constant_right_assignment: "Shift right assignment operator unsupported in Vyper" + shift_left_assignment: "Shift left assignment operator unsupported in Vyper" + shift_left_assignment_different_type: "Shift left assignment operator unsupported in Vyper" + shift_left_larger_type: "Exponentiation overflow unsupported in Vyper" + shift_left_uint32: "Exponentiation overflow unsupported in Vyper" + shift_left_uint8: "Exponentiation overflow unsupported in Vyper" + shift_negative_constant_left: "Negative numbers shifts unsupported in Vyper" + shift_negative_constant_right: "Negative numbers shifts unsupported in Vyper" + shift_right_assignment: "Shift right assignment operator unsupported in Vyper" + shift_right_garbled_signed_v1: "Assembly or alternatives unsupported in Vyper" + shift_right_garbled_signed_v2: "Assembly or alternatives unsupported in Vyper" + shift_right_garbled_v1: "Assembly or alternatives unsupported in Vyper" + shift_right_garbled_v2: "Assembly or alternatives unsupported in Vyper" + shift_right_negative_literal: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_assignment: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_int16: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_int32: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_int8: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_signextend_int16_v1: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_signextend_int16_v2: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_signextend_int32_v1: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_signextend_int32_v2: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_signextend_int8_v1: "Negative values shifts unsupported in Vyper" + shift_right_negative_lvalue_signextend_int8_v2: "Negative values shifts unsupported in Vyper" + shift_right_uint32: "uint32 shifts unsupported in Vyper" + shift_right_uint8: "uint8 shifts unsupported in Vyper" + payable: + no_nonpayable_circumvention_by_modifier: "Function modifiers unsupported in Vyper" + receive: "Receive function unsupported in Vyper" + revertStrings: + array_slices: "Unsupported in Vyper" + calldata_too_short_v1: "Abi decode unsupported in Vyper" + empty_v2: "Abi coder v2 unsupported in Vyper" + enum_v1: "Enums unsupported in Vyper" + enum_v2: "Enums unsupported in Vyper" + function_entry_checks_v2: "Abi coder v2 unsupported in Vyper" + invalid_abi_decoding_calldata_v1: "Abi decode unsupported in Vyper" + invalid_abi_decoding_memory_v1: "Assembly or alternatives unsupported in Vyper" + library_non_view_call: "Libraries unsupported in Vyper" + reverts: + error_struct: "Custom errors unsupported in Vyper" + invalid_enum_as_external_arg: "Enums unsupported in Vyper" + invalid_enum_as_external_ret: "Enums unsupported in Vyper" + invalid_enum_compared: "Enums unsupported in Vyper" + invalid_enum_stored: "Enums unsupported in Vyper" + invalid_instruction: "Assembly or alternatives unsupported in Vyper" + revert_return_area: "Assembly or alternatives unsupported in Vyper" + specialFunctions: + abi_encode_with_signature_from_string: "Unsupported in Vyper" + abi_functions_member_access: "Unsupported in Vyper" + state: + block_basefee: "Uses unsupported basefee" + block_chainid: "Uses unsupported chainid" + block_coinbase: "Uses unsupported coinbase" + block_difficulty: "Uses unsupported difficulty" + block_gaslimit: "Uses unsupported ETH/gas/balance" + block_number: "Uses unsupported number" + block_timestamp: "Uses unsupported timestamp" + blockhash_basic: "Uses unsupported blockhash" + gasleft: "Uses unsupported ETH/gas/balance" + msg_sig: "Uses unsupported msg.sig" + msg_value: "Uses unsupported ETH/gas/balance" + tx_gasprice: "Uses unsupported ETH/gas/balance" + tx_origin: "Uses unsupported origin" + uncalled_blockhash: "Uses unsupported blockhash" + state_variables_init_order: "Inheritance unsupported in Vyper" + state_variables_init_order_2: "Inheritance unsupported in Vyper" + state_variables_init_order_3: "Inheritance unsupported in Vyper" + statements: + do_while_loop_continue: "Do while loop unsupported in Vyper" + storage: + packed_functions: "Function types unsupported in Vyper" + packed_storage_overflow: "Unchecked unsupported in Vyper" + packed_storage_structs_enum: "Enums unsupported in Vyper" + strings: + concat: + string_concat_empty_argument_list: "Unsupported in Vyper" + structs: + calldata: + calldata_struct_as_argument_of_lib_function: "Libraries unsupported in Vyper" + calldata_struct_to_memory_tuple_assignment: "Unsupported in Vyper" + function_type_copy: "Function types unsupported in Vyper" + recursive_struct_2: "Assembly or alternatives unsupported in Vyper" + struct_assign_reference_to_struct: "Storage references unsupported in Vyper" + struct_memory_to_storage_function_ptr: "Function pointers unsupported in Vyper" + struct_reference: "Storage references unsupported in Vyper" + struct_referencing: "Unsupported in Vyper" + struct_storage_to_memory_function_ptr: "Function pointers unsupported in Vyper" + using_for_function_on_struct: "Unsupported in Vyper" + tryCatch: "Unsupported in Vyper" + types: + array_mapping_abstract_constructor_param: "Abstract contracts unsupported in Vyper" + convert_fixed_bytes_to_fixed_bytes_same_size: "Unsupported in Vyper" + external_function_to_address: "Function types unsupported in Vyper" + user_defined_types_mapping_storage: "User defined value types unsupported in Vyper" + mapping: + user_defined_types_mapping_storage: "User defined value types unsupported in Vyper" + mapping_abstract_constructor_param: "Abstract contracts unsupported in Vyper" + mapping_contract_key_library: "Libraries unsupported in Vyper" + mapping_enum_key_getter_v1: "Enums unsupported in Vyper" + mapping_enum_key_getter_v2: "Enums unsupported in Vyper" + mapping_enum_key_library_v1: "Enums unsupported in Vyper" + mapping_enum_key_library_v2: "Enums unsupported in Vyper" + mapping_enum_key_v1: "Enums unsupported in Vyper" + mapping_enum_key_v2: "Enums unsupported in Vyper" + nested_tuples: "Tuples unsupported in Vyper" + struct_mapping_abstract_constructor_param: "Abstract contracts unsupported in Vyper" + tuple_assign_multi_slot_grow: "Tuples unsupported in Vyper" + type_conversion_cleanup: "Cleanup unsupported in Vyper" + uninitializedFunctionPointer: "Function pointers unsupported in Vyper" + userDefinedValueType: "Unsupported in Vyper" + using: "Unsupported in Vyper" + variables: + mapping_local_assignment: "Unsupported in Vyper" + mapping_local_compound_assignment: "Unsupported in Vyper" + mapping_local_tuple_assignment: "Unsupported in Vyper" + public_state_overridding: "Inheritance unsupported in Vyper" + public_state_overridding_dynamic_struct: "Inheritance unsupported in Vyper" + public_state_overridding_mapping_to_dynamic_struct: "Inheritance unsupported in Vyper" + storing_invalid_boolean: "Assembly or alternatives unsupported in Vyper" + various: + address_code: "Uses unsupported code reflection" + address_code_complex: "Uses unsupported code reflection" + balance: "Uses unsupported ETH/gas/balance" + code_access_content: "Uses unsupported code reflection" + code_access_create: "Uses unsupported code reflection" + code_access_padding: "Uses unsupported code reflection" + code_access_runtime: "Uses unsupported code reflection" + code_length: "Uses unsupported code reflection" + code_length_contract_member: "Uses unsupported code reflection" + codebalance_assembly: "Uses unsupported code reflection" + codehash: "Uses unsupported code reflection" + codehash_assembly: "Uses unsupported code reflection" + crazy_elementary_typenames_on_stack: "Unsupported in Vyper" + cross_contract_types: "Temporary unsupported in runner" + destructuring_assignment: "Unsupported in Vyper" + gasleft_decrease: "Uses unsupported ETH/gas/balance" + gasleft_shadow_resolution: "gasleft function not present in Vyper" + inline_member_init_inheritence: "Inheritance unsupported in Vyper" + multi_modifiers: "Function modifiers unsupported in Vyper" + multi_variable_declaration: "Unsupported in Vyper" + single_copy_with_multiple_inheritance: "Inheritance unsupported in Vyper" + state_variable_under_contract_name: "Unsupported in Vyper" + staticcall_for_view_and_pure_pre_byzantium: "Ignored for Solidity" + super: "Inheritance unsupported in Vyper" + super_alone: "Unsupported in Vyper" + super_parentheses: "Inheritance unsupported in Vyper" + test_underscore_in_hex: "Unsupported in Vyper" + typed_multi_variable_declaration: "Unsupported in Vyper" + value_complex: "Ignored for Solidity" + value_insane: "Ignored for Solidity" + viaYul: + array_function_pointers: "Function pointers unsupported in Vyper" + array_push_return_reference: "Unsupported in Vyper" + array_storage_index_zeroed_test: "Assembly or alternatives unsupported in Vyper" + array_storage_push_empty: "Push(append) without args unsupported in Vyper" + array_storage_push_empty_length_address: "Push(append) without args unsupported in Vyper" + calldata_array_index_range_access: "Unsupported in Vyper" + cleanup: "Assembly or alternatives unsupported in Vyper" + comparison_functions: "Function types unsupported in Vyper" + conditional: "Conditional expression unsupported in Vyper" + conversion: + function_cast: "Function types unsupported in Vyper" + implicit_cast_assignment: "Implicit conversions unsupported in Vyper" + implicit_cast_function_call: "Implicit conversions unsupported in Vyper" + implicit_cast_local_assignment: "Implicit conversions unsupported in Vyper" + copy_struct_invalid_ir_bug: "Function types unsupported in Vyper" + delete: "Function types unsupported in Vyper" + dirty_calldata_struct: "Cleanup unsupported in Vyper" + dirty_memory_dynamic_array: "Assembly or alternatives unsupported in Vyper" + dirty_memory_int32: "Cleanup unsupported in Vyper" + dirty_memory_static_array: "Assembly or alternatives unsupported in Vyper" + dirty_memory_struct: "Assembly or alternatives unsupported in Vyper" + dirty_memory_uint32: "Cleanup unsupported in Vyper" + empty_return_corrupted_free_memory_pointer: "Assembly or alternatives unsupported in Vyper" + exp: "Exponentiation with 2 variables unsupported in Vyper" + exp_neg: "Exponentiation with 2 variables unsupported in Vyper" + exp_neg_overflow: "Exponentiation with 2 variables unsupported in Vyper" + exp_overflow: "Exponentiation with 2 variables unsupported in Vyper" + exp_various: "Exponentiation with 2 variables unsupported in Vyper" + function_address: "Unsupported in Vyper" + function_pointers: "Function pointers unsupported in Vyper" + function_selector: "Unsupported in Vyper" + local_tuple_assignment: "Unsupported in Vyper" + mapping_enum_key_getter: "Enums unsupported in Vyper" + memory_struct_allow: "Assembly or alternatives unsupported in Vyper" + return_storage_pointers: "Storage pointers unsupported in Vyper" + simple_inline_asm: "Assembly unsupported in Vyper" + storage: + dirty_storage_bytes: "Assembly or alternatives unsupported in Vyper" + dirty_storage_dynamic_array: "Assembly or alternatives unsupported in Vyper" + dirty_storage_static_array: "Assembly or alternatives unsupported in Vyper" + dirty_storage_struct: "Assembly or alternatives unsupported in Vyper" + unary_fixedbytes: "Unsupported in Vyper" + various_inline_asm: "Assembly unsupported in Vyper" + virtual_functions: "Unsupported in Vyper" + virtualFunctions: "Unsupported in Vyper" + + + + + + + diff --git a/coverage_watcher/Cargo.toml b/coverage_watcher/Cargo.toml new file mode 100644 index 00000000..94235270 --- /dev/null +++ b/coverage_watcher/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "coverage-watcher" +version = "1.3.1" +authors = [ + "Alex Zarudnyy ", + "Anton Dyadyuk ", +] +license = "MIT OR Apache-2.0" +edition = "2021" +description = "The integration tests coverage watcher" + +[[bin]] +name = "coverage-watcher" +path = "src/coverage_watcher/main.rs" + +[dependencies] +structopt = { version = "0.3", default-features = false } +anyhow = "1.0" + +serde = { version = "1.0", features = [ "derive" ] } +serde_yaml = "0.9" + +compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", rev = "a6c5b02e4f149f82f1c3821a6f258363308abd2a" } diff --git a/coverage_watcher/src/coverage_watcher/arguments.rs b/coverage_watcher/src/coverage_watcher/arguments.rs new file mode 100644 index 00000000..e2002773 --- /dev/null +++ b/coverage_watcher/src/coverage_watcher/arguments.rs @@ -0,0 +1,27 @@ +//! +//! The coverage watcher arguments. +//! + +use std::path::PathBuf; + +use structopt::StructOpt; + +/// +/// The coverage watcher arguments. +/// +#[derive(Debug, StructOpt)] +#[structopt(name = "coverage-watcher", about = "The tests coverage watcher")] +pub struct Arguments { + /// The missed tests output file path. + #[structopt(short = "o", long = "output")] + pub output: Option, +} + +impl Arguments { + /// + /// A shortcut constructor. + /// + pub fn new() -> Self { + Self::from_args() + } +} diff --git a/coverage_watcher/src/coverage_watcher/main.rs b/coverage_watcher/src/coverage_watcher/main.rs new file mode 100644 index 00000000..2a406b95 --- /dev/null +++ b/coverage_watcher/src/coverage_watcher/main.rs @@ -0,0 +1,130 @@ +//! +//! The coverage watcher binary. +//! + +pub(crate) mod arguments; + +use std::fs::File; +use std::io::Read; +use std::io::Write; +use std::path::PathBuf; + +use self::arguments::Arguments; + +use coverage_watcher::TestsDirectory; +use coverage_watcher::TestsSet; + +/// +/// The application entry point. +/// +fn main() -> anyhow::Result<()> { + let arguments = Arguments::new(); + + let mut data = String::new(); + File::open("coverage.yaml") + .expect("Failed to open ignore file") + .read_to_string(&mut data) + .expect("Failed to read ignore file"); + + let ignore_file: coverage_watcher::IgnoreFileEntity = + serde_yaml::from_str(data.as_str()).expect("Invalid ignore file"); + + let missed = TestsSet::get_missed_for_groups( + vec![ + vec![ + TestsSet { + name: "solidity/simple".to_string(), + directories: vec![TestsDirectory { + path: PathBuf::from("tests/solidity/simple"), + extension: compiler_common::EXTENSION_SOLIDITY.to_string(), + flatten: false, + }], + }, + TestsSet { + name: "vyper/simple".to_string(), + directories: vec![TestsDirectory { + path: PathBuf::from("tests/vyper/simple"), + extension: compiler_common::EXTENSION_VYPER.to_string(), + flatten: false, + }], + }, + ], + vec![ + TestsSet { + name: "solidity/complex".to_string(), + directories: vec![TestsDirectory { + path: PathBuf::from("tests/solidity/complex"), + extension: compiler_common::EXTENSION_JSON.to_string(), + flatten: false, + }], + }, + TestsSet { + name: "vyper/complex".to_string(), + directories: vec![TestsDirectory { + path: PathBuf::from("tests/vyper/complex"), + extension: compiler_common::EXTENSION_JSON.to_string(), + flatten: false, + }], + }, + ], + vec![ + TestsSet { + name: "solidity/external".to_string(), + directories: vec![TestsDirectory { + path: PathBuf::from("solidity/test/libsolidity/semanticTests"), + extension: compiler_common::EXTENSION_SOLIDITY.to_string(), + flatten: false, + }], + }, + TestsSet { + name: "vyper/external".to_string(), + directories: vec![ + TestsDirectory { + path: PathBuf::from("tests/vyper/external"), + extension: compiler_common::EXTENSION_VYPER.to_string(), + flatten: false, + }, + TestsDirectory { + path: PathBuf::from("tests/vyper/complex/external"), + extension: compiler_common::EXTENSION_JSON.to_string(), + flatten: true, + }, + ], + }, + ], + ], + &ignore_file, + )?; + + let mut result = String::new(); + for (name, tests) in missed { + if tests.is_empty() { + continue; + } + result.push_str(name.as_str()); + result.push('\n'); + for test in tests.into_iter() { + result.push_str(test.as_str()); + result.push('\n'); + } + result.push('\n'); + } + + match arguments.output { + Some(path) => { + let mut file = File::create(path.as_path()).expect("Failed to create output file"); + file.write_all(result.as_bytes()) + .expect("Failed to write result"); + } + None => println!( + "{}", + if result.is_empty() { + "No missing tests found".to_owned() + } else { + result + } + ), + } + + Ok(()) +} diff --git a/coverage_watcher/src/directory.rs b/coverage_watcher/src/directory.rs new file mode 100644 index 00000000..426a0d80 --- /dev/null +++ b/coverage_watcher/src/directory.rs @@ -0,0 +1,17 @@ +//! +//! The tests directory entity. +//! + +use std::path::PathBuf; + +/// +/// The tests directory entity. +/// +pub struct TestsDirectory { + /// The tests directory path. + pub path: PathBuf, + /// The tests extension. + pub extension: String, + /// The flag if flatten enabled. + pub flatten: bool, +} diff --git a/coverage_watcher/src/ignore_file.rs b/coverage_watcher/src/ignore_file.rs new file mode 100644 index 00000000..3761a92b --- /dev/null +++ b/coverage_watcher/src/ignore_file.rs @@ -0,0 +1,35 @@ +//! +//! The ignore file entity. +//! + +use std::collections::HashMap; + +use serde::Deserialize; + +/// +/// The ignore file entity. +/// +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum Entity { + /// The directory. + Directory(HashMap), + /// The ignored entity. + Ignored(String), +} + +impl Entity { + /// + /// Get the subdirectory. + /// + pub fn get(&self, path: &str) -> Option<&Self> { + let mut directory = Some(self); + for part in path.split('/') { + directory = match directory { + Some(Entity::Directory(entries)) => entries.get(part), + _ => None, + } + } + directory + } +} diff --git a/coverage_watcher/src/index.rs b/coverage_watcher/src/index.rs new file mode 100644 index 00000000..ddbc4f9e --- /dev/null +++ b/coverage_watcher/src/index.rs @@ -0,0 +1,116 @@ +//! +//! The test directory file system entity. +//! + +use std::collections::HashMap; +use std::fs; +use std::path::Path; + +use crate::ignore_file::Entity as IgnoreFileEntity; + +/// +/// The test directory file system entity. +/// +#[derive(Debug)] +pub struct FSEntity { + /// The directory entries. Is `None` for files. + entries: Option>, +} + +impl FSEntity { + /// + /// Indexes the specified directory. + /// + pub fn index(path: &Path, extension: &str) -> anyhow::Result { + let mut entries = HashMap::new(); + + for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + let entry_type = entry.file_type()?; + + if entry_type.is_dir() { + entries.insert( + path.file_stem() + .ok_or_else(|| anyhow::anyhow!("Failed to get filename"))? + .to_string_lossy() + .to_string(), + Self::index(&path, extension)?, + ); + continue; + } + + if !entry_type.is_file() { + anyhow::bail!("Invalid entry type"); + } + + if let Some(file_extension) = path.extension() { + if file_extension != extension { + continue; + } + } else { + continue; + } + + entries.insert( + path.file_stem() + .ok_or_else(|| anyhow::anyhow!("Failed to get filename"))? + .to_string_lossy() + .to_string(), + Self { entries: None }, + ); + } + Ok(Self { + entries: Some(entries), + }) + } + + /// + /// Returns the test names. + /// + pub fn as_string_vec(&self, ignored: Option<&IgnoreFileEntity>, flatten: bool) -> Vec { + let mut accumulator = Vec::with_capacity(16384); + self.as_string_vec_recursive("", ignored, &mut accumulator, flatten); + accumulator.sort_by_key(|test| test.to_owned()); + accumulator + } + + /// + /// Inner names accumulator function. + /// + fn as_string_vec_recursive( + &self, + current: &str, + ignored: Option<&IgnoreFileEntity>, + accumulator: &mut Vec, + flatten: bool, + ) { + let entries = match &self.entries { + Some(entries) => entries, + None => { + accumulator.push(current.to_owned()); + return; + } + }; + + for (name, entity) in entries.iter() { + let ignored = match ignored { + Some(IgnoreFileEntity::Directory(entries)) => entries.get(name), + _ => None, + }; + if let Some(IgnoreFileEntity::Ignored(_)) = ignored { + continue; + } + if flatten && entity.entries.is_none() { + accumulator.push(current.to_owned()); + continue; + } + let mut current = current.to_owned(); + if !current.is_empty() { + current.push('/'); + } + current.push_str(name); + entity.as_string_vec_recursive(¤t, ignored, accumulator, flatten); + } + } +} diff --git a/coverage_watcher/src/lib.rs b/coverage_watcher/src/lib.rs new file mode 100644 index 00000000..c78852d8 --- /dev/null +++ b/coverage_watcher/src/lib.rs @@ -0,0 +1,92 @@ +//! +//! The coverage watcher library. +//! + +pub(crate) mod directory; +pub(crate) mod ignore_file; +pub(crate) mod index; + +use std::collections::HashSet; + +use crate::index::FSEntity; + +pub use self::directory::TestsDirectory; +pub use self::ignore_file::Entity as IgnoreFileEntity; + +/// +/// The tests directory. +/// +pub struct TestsSet { + /// The name. + pub name: String, + /// The first value is a path, the second is an extension. + pub directories: Vec, +} + +impl TestsSet { + /// + /// Get missed tests for every group. + /// + pub fn get_missed_for_groups( + groups: Vec>, + ignore_file: &IgnoreFileEntity, + ) -> anyhow::Result)>> { + let mut result = Vec::new(); + for group in groups { + result.extend(Self::get_missed(group, ignore_file)?); + } + Ok(result) + } + + /// + /// Get missed tests for every element of array. + /// + fn get_missed( + tests_sets: Vec, + ignore_file: &IgnoreFileEntity, + ) -> anyhow::Result)>> { + let mut indexes = Vec::with_capacity(tests_sets.len()); + for tests_set in tests_sets.iter() { + let mut tests_set_indexes = Vec::with_capacity(tests_set.directories.len()); + for directory in tests_set.directories.iter() { + tests_set_indexes.push(FSEntity::index( + directory.path.as_path(), + directory.extension.as_str(), + )?); + } + indexes.push(tests_set_indexes); + } + + let mut result = Vec::new(); + + for (index, tests_set) in tests_sets.iter().enumerate() { + let mut set = HashSet::new(); + for (directory_index, directory) in tests_set.directories.iter().enumerate() { + set.extend(indexes[index][directory_index].as_string_vec(None, directory.flatten)); + } + + let ignore = ignore_file.get(tests_set.name.as_str()); + + let mut missed = Vec::new(); + for (index_other, tests_set_other) in tests_sets.iter().enumerate() { + if index_other == index { + continue; + } + for (directory_index, directory) in tests_set_other.directories.iter().enumerate() { + for test in indexes[index_other][directory_index] + .as_string_vec(ignore, directory.flatten) + { + if !set.contains(test.as_str()) { + missed.push(test); + } + } + } + } + missed.sort(); + missed.dedup(); + + result.push((tests_set.name.clone(), missed)); + } + Ok(result) + } +} diff --git a/eraLogo.svg b/eraLogo.svg new file mode 100644 index 00000000..6ec790c0 --- /dev/null +++ b/eraLogo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/solidity b/solidity new file mode 160000 index 00000000..812e2664 --- /dev/null +++ b/solidity @@ -0,0 +1 @@ +Subproject commit 812e26640ccba7fafc95f71509c6e21c8434efec diff --git a/solidity_adapter/Cargo.toml b/solidity_adapter/Cargo.toml new file mode 100644 index 00000000..f42766e0 --- /dev/null +++ b/solidity_adapter/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "solidity-adapter" +version = "1.3.1" +authors = [ + "Alex Zarudnyy ", + "Anton Dyadyuk ", +] +license = "MIT OR Apache-2.0" +edition = "2021" +description = "The Ethereum Solidity tests adapter" + +[[bin]] +name = "tests-updater" +path = "src/tests_updater/main.rs" + +[lib] +doctest = false + +[dependencies] +structopt = { version = "0.3", default-features = false } +anyhow = "1.0" + +serde = { version = "1.0", features = [ "derive" ] } +serde_yaml = "0.9" +semver = { version = "1.0", features = [ "serde" ] } +regex = "1.7" +md5 = "0.7" + +web3 = { version= "0.18.0", default-features = false, features = ["http-rustls-tls", "test", "signing"] } + +compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", rev = "a6c5b02e4f149f82f1c3821a6f258363308abd2a" } diff --git a/solidity_adapter/src/index/conflicts.rs b/solidity_adapter/src/index/conflicts.rs new file mode 100644 index 00000000..cd59f8b2 --- /dev/null +++ b/solidity_adapter/src/index/conflicts.rs @@ -0,0 +1,92 @@ +//! +//! The test file update conflicts flags. +//! + +use std::fmt::Display; +use std::fmt::Formatter; +use std::fs::File; +use std::io::Read; +use std::path::Path; + +use super::test_file::TestFile; + +/// +/// The test file update conflicts flags. +/// +pub struct Conflicts { + /// The test source data. + data: bool, + /// The test enabled flag. + enabled: bool, + /// The test group. + group: bool, + /// The test comment. + comment: bool, + /// The test modes filter. + modes: bool, + /// The test version filter. + version: bool, +} + +impl Conflicts { + /// + /// Try to get conflicts flags from the old test entity changes. + /// + pub fn try_from_test_entity_changes(test: &TestFile, path: &Path) -> anyhow::Result { + let hash = test + .hash + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Test file hash is None"))?; + + let mut file = File::open(path)?; + let mut data = String::new(); + file.read_to_string(&mut data) + .map_err(|error| anyhow::anyhow!("Failed to read test file: {}", error))?; + let actual_hash = TestFile::md5(data.as_str()); + + Ok(Self { + data: !hash.eq(&actual_hash), + enabled: !test.enabled, + group: test.group.is_some(), + comment: test.comment.is_some(), + modes: test.modes.is_some(), + version: test.version.is_some(), + }) + } +} + +impl Display for Conflicts { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if !(self.data || self.enabled || self.group || self.comment || self.modes || self.version) + { + return write!(f, "no conflicts"); + } + + let mut conflicts = Vec::new(); + if self.data { + conflicts.push("test source changes"); + } + if self.enabled { + conflicts.push("enabled flag"); + } + if self.group { + conflicts.push("group"); + } + if self.comment { + conflicts.push("comment"); + } + if self.modes { + conflicts.push("modes filter"); + } + if self.version { + conflicts.push("version filter"); + } + let conflicts_str = conflicts.join(", "); + write!(f, "{} ", conflicts_str)?; + if conflicts.len() == 1 { + write!(f, "was overwritten by update") + } else { + write!(f, "were overwritten by update") + } + } +} diff --git a/solidity_adapter/src/index/directory.rs b/solidity_adapter/src/index/directory.rs new file mode 100644 index 00000000..51e0a2d3 --- /dev/null +++ b/solidity_adapter/src/index/directory.rs @@ -0,0 +1,37 @@ +//! +//! The Solidity tests directory file system entity. +//! + +use std::collections::BTreeMap; + +use serde::Deserialize; +use serde::Serialize; + +use super::FSEntity; + +/// +/// The Solidity tests directory file system entity. +/// +#[derive(Debug, Serialize, Deserialize)] +pub struct Directory { + /// Whether the tests directory is enabled. + pub enabled: bool, + /// The tests directory comment. + #[serde(skip_serializing_if = "Option::is_none")] + pub comment: Option, + /// The directory entries. Is `None` for files. + pub entries: BTreeMap, +} + +impl Directory { + /// + /// A shortcut constructor. + /// + pub fn new(entries: BTreeMap) -> Self { + Self { + enabled: true, + entries, + comment: None, + } + } +} diff --git a/solidity_adapter/src/index/enabled.rs b/solidity_adapter/src/index/enabled.rs new file mode 100644 index 00000000..737b6881 --- /dev/null +++ b/solidity_adapter/src/index/enabled.rs @@ -0,0 +1,39 @@ +//! +//! The enabled test entity description. +//! + +use std::path::PathBuf; + +/// +/// The enabled test entity description. +/// +#[derive(Debug, Clone)] +pub struct EnabledTest { + /// The test path. + pub path: PathBuf, + /// The optimization modes which all the cases are enabled for. + pub modes: Option>, + /// The compiler version the test must be run with. + pub version: Option, + /// The test group. + pub group: Option, +} + +impl EnabledTest { + /// + /// A shortcut constructor. + /// + pub fn new( + path: PathBuf, + modes: Option>, + version: Option, + group: Option, + ) -> Self { + Self { + path, + modes, + version, + group, + } + } +} diff --git a/solidity_adapter/src/index/mod.rs b/solidity_adapter/src/index/mod.rs new file mode 100644 index 00000000..cdc9bb07 --- /dev/null +++ b/solidity_adapter/src/index/mod.rs @@ -0,0 +1,338 @@ +//! +//! The Solidity tests file system entity. +//! + +pub mod conflicts; +mod directory; +pub mod enabled; +mod test_file; + +use std::collections::BTreeMap; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; + +use serde::Deserialize; +use serde::Serialize; + +use self::conflicts::Conflicts; +use self::directory::Directory; +use self::enabled::EnabledTest; +use self::test_file::TestFile; + +/// +/// The Solidity tests file system entity. +/// +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum FSEntity { + /// The directory. + Directory(Directory), + /// The test file. + File(TestFile), +} + +impl FSEntity { + /// + /// Indexes the specified directory. + /// + pub fn index(path: &Path) -> anyhow::Result { + let mut entries = BTreeMap::new(); + + for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + let entry_type = entry.file_type()?; + + if entry_type.is_dir() { + entries.insert( + path.file_name() + .ok_or_else(|| anyhow::anyhow!("Failed to get filename"))? + .to_string_lossy() + .to_string(), + Self::index(&path)?, + ); + continue; + } + + if !entry_type.is_file() { + anyhow::bail!("Invalid entry type"); + } + + entries.insert( + path.file_name() + .ok_or_else(|| anyhow::anyhow!("Failed to get filename"))? + .to_string_lossy() + .to_string(), + Self::File(TestFile::try_from(path.as_path())?), + ); + } + + Ok(Self::Directory(Directory::new(entries))) + } + + /// + /// Updates the new index, tests and returns changes. + /// + #[allow(clippy::type_complexity)] + pub fn update( + &self, + new: &mut FSEntity, + initial: &Path, + ) -> anyhow::Result<(Vec, Vec, Vec<(PathBuf, Conflicts)>)> { + let mut created = Vec::new(); + let mut deleted = Vec::new(); + let mut updated = Vec::new(); + self.update_recursive(new, initial, &mut created, &mut deleted, &mut updated)?; + Ok((created, deleted, updated)) + } + + /// + /// Returns the enabled tests list with the `initial` directory prefix. + /// + pub fn into_enabled_list(self, initial: &Path) -> Vec { + let mut accumulator = Vec::with_capacity(16384); + self.into_enabled_list_recursive(initial, &mut accumulator); + accumulator.sort_by_key(|test| test.path.to_owned()); + accumulator + } + + /// + /// Returns the enabled test by the path with the `initial` directory prefix (None if not found or test disabled). + /// + pub fn into_enabled_test(self, initial: &Path, path: &Path) -> Option { + let mut current_entity = self; + for path_part in path.iter() { + match current_entity { + FSEntity::Directory(mut directory) => { + current_entity = match directory + .entries + .remove(path_part.to_string_lossy().as_ref()) + { + Some(entity) => entity, + None => return None, + } + } + FSEntity::File(_) => return None, + } + } + match current_entity { + FSEntity::Directory(_) => None, + FSEntity::File(file) => { + if !file.enabled { + return None; + } + let mut file_path = initial.to_path_buf(); + file_path.push(path); + Some(EnabledTest::new( + file_path, + file.modes, + file.version, + file.group, + )) + } + } + } + + /// + /// Updates new index, tests and lists changes. + /// + fn update_recursive( + &self, + new: &mut FSEntity, + current: &Path, + created: &mut Vec, + deleted: &mut Vec, + updated: &mut Vec<(PathBuf, Conflicts)>, + ) -> anyhow::Result<()> { + let (old_entities, new_entities) = match (self, new) { + (Self::File(old_file), Self::File(new_file)) => { + let new_hash = TestFile::md5( + new_file + .data + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Test data is None: {:?}", current))? + .as_str(), + ); + if old_file + .hash + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Test file hash is None: {:?}", current))? + .eq(&new_hash) + { + new_file.enabled = old_file.enabled; + new_file.group = old_file.group.clone(); + new_file.comment = old_file.comment.clone(); + new_file.modes = old_file.modes.clone(); + new_file.version = old_file.version.clone(); + } else { + let conflicts = Conflicts::try_from_test_entity_changes(old_file, current) + .map_err(|err| { + anyhow::anyhow!( + "Failed to get conflicts flags for test file {:?}: {}", + current, + err + ) + })?; + let mut file_to_write = fs::OpenOptions::new() + .write(true) + .truncate(true) + .open(current)?; + file_to_write.write_all( + new_file + .data + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Test data is None: {:?}", current))? + .as_bytes(), + )?; + updated.push((current.to_owned(), conflicts)); + } + return Ok(()); + } + ( + Self::Directory(Directory { + enabled: old_enabled, + entries: old_entities, + comment: old_comment, + }), + Self::Directory(Directory { + enabled: new_enabled, + entries: new_entities, + comment: new_comment, + }), + ) => { + *new_enabled = *old_enabled; + *new_comment = old_comment.clone(); + + (old_entities, new_entities) + } + (_, new) => { + self.list_recursive(current, deleted); + new.list_recursive(current, created); + self.delete(current)?; + new.create_recursive(current)?; + return Ok(()); + } + }; + + for (name, entity) in old_entities.iter() { + let mut current = current.to_owned(); + current.push(name); + if let Some(new_entity) = new_entities.get_mut(name) { + entity.update_recursive(new_entity, ¤t, created, deleted, updated)?; + } else { + entity.list_recursive(¤t, deleted); + entity.delete(¤t)?; + } + } + for (name, entity) in new_entities.iter() { + if !old_entities.contains_key(name) { + let mut current = current.to_owned(); + current.push(name); + entity.list_recursive(¤t, created); + entity.create_recursive(¤t)?; + } + } + + Ok(()) + } + + /// + /// Inner enabled accumulator function. + /// + fn into_enabled_list_recursive(self, current: &Path, accumulator: &mut Vec) { + let entries = match self { + Self::File(file) => { + if !file.enabled { + return; + } + accumulator.push(EnabledTest::new( + current.to_owned(), + file.modes, + file.version, + file.group, + )); + return; + } + Self::Directory(directory) => { + if !directory.enabled { + return; + } + directory.entries + } + }; + + for (name, entity) in entries.into_iter() { + let mut current = current.to_owned(); + current.push(name); + entity.into_enabled_list_recursive(¤t, accumulator); + } + } + + /// + /// Creates files and folders from self. + /// + fn create_recursive(&self, current: &Path) -> anyhow::Result<()> { + let entries = match self { + Self::Directory(directory) => &directory.entries, + Self::File(test) => { + let mut file = File::create(current) + .map_err(|err| anyhow::anyhow!("Failed to create file: {}", err))?; + file.write_all( + test.data + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Test data is None: {:?}", current))? + .as_bytes(), + )?; + return Ok(()); + } + }; + + fs::create_dir(current) + .map_err(|err| anyhow::anyhow!("Failed to create directory: {}", err))?; + + for (name, entity) in entries.iter() { + let mut current = current.to_owned(); + current.push(name); + entity.create_recursive(¤t)?; + } + + Ok(()) + } + + /// + /// Deletes files and folders from self. + /// + fn delete(&self, current: &Path) -> anyhow::Result<()> { + if let Self::Directory(_) = self { + fs::remove_dir_all(current) + .map_err(|err| anyhow::anyhow!("Failed to delete directory: {}", err))?; + } else { + fs::remove_file(current) + .map_err(|err| anyhow::anyhow!("Failed to delete file: {}", err))?; + } + + Ok(()) + } + + /// + /// Inner accumulator function. + /// + fn list_recursive(&self, current: &Path, accumulator: &mut Vec) { + let entries = match self { + Self::Directory(directory) => &directory.entries, + Self::File(_) => { + accumulator.push(current.to_owned()); + return; + } + }; + + for (name, entity) in entries.iter() { + let mut current = current.to_owned(); + current.push(name); + entity.list_recursive(¤t, accumulator); + } + } +} diff --git a/solidity_adapter/src/index/test_file.rs b/solidity_adapter/src/index/test_file.rs new file mode 100644 index 00000000..ddff5999 --- /dev/null +++ b/solidity_adapter/src/index/test_file.rs @@ -0,0 +1,70 @@ +//! +//! The Solidity test file. +//! + +use std::fs; +use std::io::Read; + +use serde::Deserialize; +use serde::Serialize; +use std::path::Path; + +/// +/// The Solidity test file. +/// +#[derive(Debug, Serialize, Deserialize)] +pub struct TestFile { + /// The test data used for updating. + #[serde(skip_serializing)] + pub data: Option, + /// The original test file hash. Only for Solidity. + #[serde(skip_serializing_if = "Option::is_none")] + pub hash: Option, + /// Whether the test is enabled. + pub enabled: bool, + /// The test group. + #[serde(skip_serializing_if = "Option::is_none")] + pub group: Option, + /// The comment. + #[serde(skip_serializing_if = "Option::is_none")] + pub comment: Option, + /// The optimization modes which all the cases are enabled for. + #[serde(skip_serializing_if = "Option::is_none")] + pub modes: Option>, + /// The compiler version the test must be run with. + #[serde(skip_serializing_if = "Option::is_none")] + pub version: Option, +} + +impl TryFrom<&Path> for TestFile { + type Error = anyhow::Error; + + fn try_from(value: &Path) -> Result { + let mut file = fs::File::open(value)?; + + let mut data = String::new(); + file.read_to_string(&mut data) + .map_err(|error| anyhow::anyhow!("Failed to read test file: {}", error))?; + + let hash = Self::md5(data.as_str()); + + Ok(Self { + data: Some(data), + hash: Some(hash), + enabled: true, + group: None, + comment: None, + modes: None, + version: None, + }) + } +} + +impl TestFile { + /// + /// Returns MD5 hash as hex string. + /// + pub fn md5(data: &str) -> String { + format!("0x{:x}", md5::compute(data.as_bytes())) + } +} diff --git a/solidity_adapter/src/lib.rs b/solidity_adapter/src/lib.rs new file mode 100644 index 00000000..921412a3 --- /dev/null +++ b/solidity_adapter/src/lib.rs @@ -0,0 +1,51 @@ +//! +//! The Solidity adapter library. +//! + +pub(crate) mod index; +mod test; + +use std::ops::Add; +use std::str::FromStr; + +pub use self::index::enabled::EnabledTest; +pub use self::index::FSEntity; +pub use self::test::function_call::event::Event; +pub use self::test::function_call::gas_option::GasOption; +pub use self::test::function_call::FunctionCall; +pub use self::test::params::abi_encoder_v1_only::ABIEncoderV1Only; +pub use self::test::params::compile_to_ewasm::CompileToEwasm; +pub use self::test::params::compile_via_yul::CompileViaYul; +pub use self::test::params::evm_version::EVMVersion; +pub use self::test::params::evm_version::EVM; +pub use self::test::params::revert_strings::RevertStrings; +pub use self::test::params::Params; +pub use self::test::Test; + +/// The default contract address. +pub const DEFAULT_CONTRACT_ADDRESS: &str = "c06afe3a8444fc0004668591e8306bfb9968e79e"; + +/// The index of the account used as the default caller. +pub const DEFAULT_ACCOUNT_INDEX: usize = 0; + +/// +/// First pre-generated account address. +/// +const ZERO_ADDRESS: &str = "1212121212121212121212121212120000000012"; + +/// The caller address multiplier. +const ADDRESS_INDEX_MULTIPLIER: usize = 4096; // 16^3 + +/// +/// Returns address of pre-generated account by index. +/// +pub fn account_address(index: usize) -> web3::types::Address { + let address = web3::types::U256::from_str(ZERO_ADDRESS).expect("Default address"); + let address = address.add(index * ADDRESS_INDEX_MULTIPLIER); + + let mut bytes = [0u8; compiler_common::BYTE_LENGTH_FIELD]; + address.to_big_endian(&mut bytes); + web3::types::Address::from_slice( + &bytes[compiler_common::BYTE_LENGTH_FIELD - compiler_common::BYTE_LENGTH_ETH_ADDRESS..], + ) +} diff --git a/solidity_adapter/src/test/function_call/event.rs b/solidity_adapter/src/test/function_call/event.rs new file mode 100644 index 00000000..6ea95221 --- /dev/null +++ b/solidity_adapter/src/test/function_call/event.rs @@ -0,0 +1,60 @@ +//! +//! The event. +//! + +use std::str::FromStr; + +use crate::test::function_call::parser::Event as SyntaxEvent; +use crate::test::function_call::parser::EventVariant; + +/// +/// The event. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Event { + /// The event address. + pub address: Option, + /// The event topics. + pub topics: Vec, + /// The expected values. + pub expected: Vec, +} + +impl TryFrom for Event { + type Error = anyhow::Error; + + fn try_from(event: SyntaxEvent) -> Result { + let address = event.address.as_ref().map(|address| { + web3::types::Address::from_str(address).expect(super::VALIDATED_BY_THE_PARSER) + }); + let mut expected = Vec::new(); + let mut topics = Vec::new(); + if let Some(literals) = event.expected { + for literal in literals { + if literal.indexed { + topics.extend(literal.inner.as_bytes_be()?); + } else { + expected.extend(literal.inner.as_bytes_be()?); + } + } + } + let mut topics = super::bytes_as_u256(topics.as_slice()); + if let EventVariant::Signature { identifier, types } = event.variant { + topics.insert( + 0, + web3::types::U256::from_big_endian( + web3::signing::keccak256( + super::signature(Some(identifier), Some(types)).as_bytes(), + ) + .as_slice(), + ), + ) + } + let expected = super::bytes_as_u256(expected.as_slice()); + Ok(Self { + address, + topics, + expected, + }) + } +} diff --git a/solidity_adapter/src/test/function_call/gas_option.rs b/solidity_adapter/src/test/function_call/gas_option.rs new file mode 100644 index 00000000..703c3773 --- /dev/null +++ b/solidity_adapter/src/test/function_call/gas_option.rs @@ -0,0 +1,34 @@ +//! +//! The gas option. +//! + +use crate::test::function_call::parser::Gas; +use crate::test::function_call::parser::GasVariant; + +/// +/// The gas option. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum GasOption { + /// `irOptimized` in the metadata. + IrOptimized(web3::types::U256), + /// `legacy` in the metadata. + Legacy(web3::types::U256), + /// `legacyOptimized` in the metadata. + LegacyOptimized(web3::types::U256), + /// `ir` in the metadata. + Ir(web3::types::U256), +} + +impl From for GasOption { + fn from(gas: Gas) -> Self { + let value = web3::types::U256::from_dec_str(gas.value.as_str()) + .expect(super::VALIDATED_BY_THE_PARSER); + match gas.variant { + GasVariant::IrOptimized => Self::IrOptimized(value), + GasVariant::Legacy => Self::Legacy(value), + GasVariant::LegacyOptimized => Self::LegacyOptimized(value), + GasVariant::Ir => Self::Ir(value), + } + } +} diff --git a/solidity_adapter/src/test/function_call/mod.rs b/solidity_adapter/src/test/function_call/mod.rs new file mode 100644 index 00000000..278855b9 --- /dev/null +++ b/solidity_adapter/src/test/function_call/mod.rs @@ -0,0 +1,358 @@ +//! +//! The function call. +//! + +pub mod event; +pub mod gas_option; +pub mod parser; + +use crate::test::function_call::parser::Call; +use crate::test::function_call::parser::CallVariant; +use crate::test::function_call::parser::Identifier; +use crate::test::function_call::parser::Type; +use crate::test::function_call::parser::Unit; + +use self::event::Event; +use self::gas_option::GasOption; + +/// +/// The function call. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum FunctionCall { + /// The library. + Library { + /// The library name. + name: String, + /// The source file name. + source: Option, + }, + /// The custom function call. + Call { + /// The function signature. + method: String, + /// The calldata. + calldata: Vec, + /// The value in wei. + value: Option, + /// The expected output. + expected: Vec, + /// The flag if failure expected. + failure: bool, + /// The expected events. + events: Vec, + /// The expected gas options. + gas_options: Vec, + }, + /// The constructor call. + Constructor { + /// The calldata. + calldata: Vec, + /// The value in wei. + value: Option, + /// The expected events. + events: Vec, + /// The expected gas options. + gas_options: Vec, + }, + /// The `isoltest_builtin_test` standard function. + IsoltestBuiltinTest { + /// The expected return value. + expected: web3::types::U256, + }, + /// The `isoltest_side_effects_test` standard function. + IsoltestSideEffectsTest { + /// The input. + input: Vec, + /// The expected output. + expected: Vec, + }, + /// The `balance` standard function. + Balance { + /// The input. + input: Option, + /// The expected output. + expected: web3::types::U256, + /// The expected events. + events: Vec, + /// The expected gas options. + gas_options: Vec, + }, + /// The `storageEmpty` standard function. + StorageEmpty { + /// The expected output. + expected: bool, + }, + /// The `account` standard function. + Account { + /// The input. + input: usize, + /// The expected output. + expected: web3::types::Address, + }, +} + +impl TryFrom for FunctionCall { + type Error = anyhow::Error; + + fn try_from(value: Call) -> anyhow::Result { + match value.variant { + CallVariant::Library { identifier, source } => Ok(Self::Library { + name: identifier.name, + source, + }), + CallVariant::Call { + identifier, + types, + value, + input, + expected, + failure, + events, + gas, + } => { + let signature = signature(identifier.clone(), types); + + let value = match value { + Some(value) => { + let mut amount = web3::types::U256::from_dec_str(value.amount.as_str()) + .expect(VALIDATED_BY_THE_PARSER); + if value.unit == Unit::Ether { + amount = amount + .checked_mul(web3::types::U256::from(u64::pow(10, 18))) + .ok_or_else(|| anyhow::anyhow!("Overflow: amount too much"))?; + } + Some(amount) + } + None => None, + }; + + let input = match input { + Some(input) => input + .into_iter() + .map(|literal| literal.as_bytes_be()) + .collect::>>>()? + .into_iter() + .flatten() + .collect::>(), + None => Vec::new(), + }; + + let expected = match expected { + Some(expected) => bytes_as_u256( + expected + .into_iter() + .map(|literal| literal.as_bytes_be()) + .collect::>>>()? + .into_iter() + .flatten() + .collect::>() + .as_slice(), + ), + None => Vec::new(), + }; + + let events = events + .into_iter() + .map(|event| event.try_into()) + .collect::>>()?; + + let gas_options: Vec = gas + .into_iter() + .map(|gas_option| gas_option.into()) + .collect(); + + match signature.as_str() { + "constructor()" => { + if !expected.is_empty() { + anyhow::bail!("Constructor should not expect values"); + } + Ok(Self::Constructor { + calldata: input, + value, + events, + gas_options, + }) + } + "isoltest_builtin_test" => { + if expected.len() != 1 { + anyhow::bail!("isoltest_builtin_test should expect one element"); + } + if !input.is_empty() { + anyhow::bail!("isoltest_builtin_test don't expect params"); + } + if !events.is_empty() { + anyhow::bail!("standard functions don't emit events"); + } + if !gas_options.is_empty() { + anyhow::bail!("standard functions can not have gas options"); + } + Ok(Self::IsoltestBuiltinTest { + expected: expected.into_iter().next().expect("Always valid"), + }) + } + "isoltest_side_effects_test" => { + if !events.is_empty() { + anyhow::bail!("standard functions don't emit events"); + } + if !gas_options.is_empty() { + anyhow::bail!("standard functions can not have gas options"); + } + Ok(Self::IsoltestSideEffectsTest { input, expected }) + } + "balance" => { + if input.len() > compiler_common::BYTE_LENGTH_FIELD { + anyhow::bail!("balance function expect one or zero element"); + } + if expected.len() != 1 { + anyhow::bail!("balance function returns 1 element"); + } + Ok(Self::Balance { + input: if input.is_empty() { + None + } else { + if !input + .iter() + .take( + compiler_common::BYTE_LENGTH_FIELD + - compiler_common::BYTE_LENGTH_ETH_ADDRESS, + ) + .all(|byte| byte.eq(&0)) + { + anyhow::bail!( + "expected cleaned up address as input for balance function" + ); + } + Some(web3::types::Address::from_slice( + &input[compiler_common::BYTE_LENGTH_FIELD + - compiler_common::BYTE_LENGTH_ETH_ADDRESS..], + )) + }, + expected: expected.into_iter().next().expect("Always valid"), + events, + gas_options, + }) + } + "storageEmpty" => { + if !input.is_empty() { + anyhow::bail!("storageEmpty function don't expect input"); + } + if expected.len() != 1 { + anyhow::bail!("storageEmpty function returns one element"); + } + if !events.is_empty() { + anyhow::bail!("standard functions don't emit events"); + } + if !gas_options.is_empty() { + anyhow::bail!("standard functions can not have gas options"); + } + Ok(Self::StorageEmpty { + expected: !expected.into_iter().next().expect("Always valid").is_zero(), + }) + } + "account" => { + if input.len() != compiler_common::BYTE_LENGTH_FIELD { + anyhow::bail!("account function expect one element"); + } + if expected.len() != 1 { + anyhow::bail!("account function returns 1 element"); + } + if !events.is_empty() { + anyhow::bail!("standard functions don't emit events"); + } + if !gas_options.is_empty() { + anyhow::bail!("standard functions can not have gas options"); + } + let input = web3::types::U256::from_big_endian(input.as_slice()); + let expected = expected.into_iter().next().expect("Always valid"); + let mut expected_bytes = [0u8; compiler_common::BYTE_LENGTH_FIELD]; + expected.to_big_endian(&mut expected_bytes); + Ok(Self::Account { + input: input.as_usize(), + expected: web3::types::Address::from_slice( + &expected_bytes[expected_bytes.len() + - compiler_common::BYTE_LENGTH_ETH_ADDRESS..], + ), + }) + } + signature_str => { + let calldata = if signature == "()" { + input + } else { + let mut bytes = + web3::signing::keccak256(signature_str.as_bytes())[0..4].to_vec(); + bytes.extend(input); + bytes + }; + Ok(Self::Call { + method: identifier + .map(|identifier| identifier.name) + .unwrap_or_default(), + calldata, + value, + expected, + failure, + events, + gas_options, + }) + } + } + } + } + } +} + +impl FunctionCall { + /// + /// Parses function calls. + /// + pub fn parse_calls(value: &str) -> anyhow::Result> { + self::parser::Parser::default() + .parse(value) + .map_err(|error| anyhow::anyhow!("Failed to parse function calls: {:?}", error))? + .into_iter() + .map(|call| call.try_into()) + .collect::>>() + } +} + +/// The unreachable branch panic, which is prevented by the parser. +static VALIDATED_BY_THE_PARSER: &str = "Unreachable as long as the parser works correctly"; + +/// +/// Returns signature from identifier and types. +/// +fn signature(identifier: Option, types: Option>) -> String { + let mut signature = identifier + .map(|identifier| identifier.name) + .unwrap_or_default(); + if let Some(types) = types { + signature.push_str( + format!( + "({})", + types + .into_iter() + .map(|r#type| r#type.to_string()) + .collect::>() + .join(",") + ) + .as_str(), + ); + } + signature +} + +/// +/// Converts bytes to vector of U256. +/// +fn bytes_as_u256(bytes: &[u8]) -> Vec { + let mut result = Vec::new(); + for value in bytes.chunks(compiler_common::BYTE_LENGTH_FIELD) { + let mut value = value.to_owned(); + while value.len() < compiler_common::BYTE_LENGTH_FIELD { + value.push(0); + } + result.push(web3::types::U256::from_big_endian(value.as_slice())); + } + result +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/error.rs b/solidity_adapter/src/test/function_call/parser/lexical/error.rs new file mode 100644 index 00000000..db260b53 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/error.rs @@ -0,0 +1,168 @@ +//! +//! The lexical parser error. +//! + +use super::token::lexeme::literal::hex::Hex; +use super::token::lexeme::literal::integer::Integer; +use super::token::location::Location; + +/// +/// The lexical parser error. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Error { + /// The string has not been terminated. + UnterminatedDoubleQuoteString { + /// The location where the unterminated string starts. + start: Location, + /// The location where the unterminated string ends. + end: Location, + }, + + /// A non-decimal or `x` character is found after a single `0` in an integer literal. + ExpectedOneOfDecimalOrXInteger { + /// The location of the invalid character. + location: Location, + /// The allowed characters. + expected: String, + /// The invalid character. + found: char, + }, + /// A non-decimal character is found in a decimal literal. + ExpectedOneOfDecimalInteger { + /// The location of the invalid character. + location: Location, + /// The allowed characters. + expected: String, + /// The invalid character. + found: char, + }, + /// A non-hexadecimal character is found in a hexadecimal literal. + ExpectedOneOfHexadecimalInteger { + /// The location of the invalid character. + location: Location, + /// The allowed characters. + expected: String, + /// The invalid character. + found: char, + }, + + /// The hex has not been terminated. + UnterminatedDoubleQuoteHex { + /// The location where the unterminated hex starts. + start: Location, + /// The location where the unterminated hex ends. + end: Location, + }, + /// A non-hexadecimal character is found in a hex literal. + ExpectedOneOfHexadecimalHex { + /// The location of the invalid character. + location: Location, + /// The allowed characters. + expected: String, + /// The invalid character. + found: char, + }, + + /// An unexpected character forbidden in the current state. + InvalidCharacter { + /// The location of the invalid character. + location: Location, + /// The invalid character. + found: char, + }, + /// Unable to finish a lexeme. + UnexpectedEnd { + /// The location of the end of the unfinished lexeme. + location: Location, + }, +} + +impl Error { + /// + /// A shortcut constructor. + /// + pub fn unterminated_double_quote_string(start: Location, end: Location) -> Self { + Self::UnterminatedDoubleQuoteString { start, end } + } + + /// + /// A shortcut constructor. + /// + pub fn expected_one_of_decimal_or_x_integer(location: Location, found: char) -> Self { + let mut expected = Integer::CHARACTERS_DECIMAL.to_vec(); + expected.push(Integer::CHARACTER_INITIAL_HEXADECIMAL); + Self::ExpectedOneOfDecimalOrXInteger { + location, + expected: Self::join_expected(expected), + found, + } + } + + /// + /// A shortcut constructor. + /// + pub fn expected_one_of_decimal_integer(location: Location, found: char) -> Self { + Self::ExpectedOneOfDecimalInteger { + location, + expected: Self::join_expected(Integer::CHARACTERS_DECIMAL.to_vec()), + found, + } + } + + /// + /// A shortcut constructor. + /// + pub fn expected_one_of_hexadecimal_integer(location: Location, found: char) -> Self { + Self::ExpectedOneOfHexadecimalInteger { + location, + expected: Self::join_expected(Integer::CHARACTERS_HEXADECIMAL.to_vec()), + found, + } + } + + /// + /// A shortcut constructor. + /// + pub fn unterminated_double_quote_hex(start: Location, end: Location) -> Self { + Self::UnterminatedDoubleQuoteHex { start, end } + } + + /// + /// A shortcut constructor. + /// + pub fn expected_one_of_hexadecimal_hex(location: Location, found: char) -> Self { + Self::ExpectedOneOfHexadecimalHex { + location, + expected: Self::join_expected(Hex::CHARACTERS.to_vec()), + found, + } + } + + /// + /// A shortcut constructor. + /// + pub fn invalid_character(location: Location, found: char) -> Self { + Self::InvalidCharacter { location, found } + } + + /// + /// A shortcut constructor. + /// + pub fn unexpected_end(location: Location) -> Self { + Self::UnexpectedEnd { location } + } + + /// + /// Converts a group of characters into a comma-separated list. + /// + /// E.g. ['a', 'b', 'c'] turns into `a`, `b`, `c`. + /// + fn join_expected(chars: Vec) -> String { + chars + .into_iter() + .map(|symbol| format!("`{symbol}`")) + .collect::>() + .join(", ") + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/mod.rs new file mode 100644 index 00000000..6a2aca22 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/mod.rs @@ -0,0 +1,24 @@ +//! +//! The Solidity tests metadata lexical parser. +//! + +#[cfg(test)] +mod tests; + +mod error; +mod stream; +mod token; + +pub use self::error::Error; +pub use self::stream::TokenStream; +pub use self::token::lexeme::identifier::Identifier; +pub use self::token::lexeme::keyword::Keyword; +pub use self::token::lexeme::literal::boolean::Boolean as BooleanLiteral; +pub use self::token::lexeme::literal::hex::Hex as HexLiteral; +pub use self::token::lexeme::literal::integer::Integer as IntegerLiteral; +pub use self::token::lexeme::literal::string::String as StringLiteral; +pub use self::token::lexeme::literal::Literal; +pub use self::token::lexeme::symbol::Symbol; +pub use self::token::lexeme::Lexeme; +pub use self::token::location::Location; +pub use self::token::Token; diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/error.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/error.rs new file mode 100644 index 00000000..cd368cf2 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/error.rs @@ -0,0 +1,12 @@ +//! +//! The lexical comment parser error. +//! + +/// +/// The lexical comment parser error. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// The lexeme is not a comment, which means that another parser must be run. + NotAComment, +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/mod.rs new file mode 100644 index 00000000..6133cc57 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/mod.rs @@ -0,0 +1,95 @@ +//! +//! The lexical comment parser. +//! + +#[cfg(test)] +mod tests; + +pub mod error; +pub mod output; + +use self::error::Error; +use self::output::Output; +use crate::test::function_call::parser::lexical::token::lexeme::comment::Comment; + +/// +/// The parser state. +/// +pub enum State { + /// The initial state. + Start, + /// The `#` has been parsed so far. + NumberCharacter, + /// The `#{comment}#` has been parsed so far. + SecondNumberCharacter, +} + +/// +/// Parses a comment. +/// +pub fn parse(input: &str) -> Result { + let mut state = State::Start; + let mut length_chars = 0; + let mut length_bytes = 0; + let mut lines = 0; + let mut column = 1; + + loop { + let character = input.chars().nth(length_chars); + match state { + State::Start => match character { + Some('#') => { + length_chars += 1; + length_bytes += 1; + column += 1; + state = State::NumberCharacter; + } + _ => return Err(Error::NotAComment), + }, + State::NumberCharacter => match character { + Some('#') => { + length_chars += 1; + length_bytes += 1; + column += 1; + state = State::SecondNumberCharacter; + } + Some('\n') => { + length_chars += 1; + length_bytes += 1; + column = 1; + lines += 1; + } + Some(char) => { + length_chars += 1; + length_bytes += char.len_utf8(); + column += 1; + } + None => return Err(Error::NotAComment), + }, + State::SecondNumberCharacter => match character { + Some(char) => { + if !char.is_ascii_whitespace() { + return Err(Error::NotAComment); + } + length_chars += 1; + length_bytes += 1; + if char == '\n' { + lines += 1; + column = 1; + } else { + column += 1; + } + let comment = Comment::new(input[1..length_bytes - 2].to_owned()); + return Ok(Output::new( + length_bytes, + length_chars, + lines, + column, + comment, + )); + } + _ => return Err(Error::NotAComment), + }, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/output.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/output.rs new file mode 100644 index 00000000..4070a854 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/output.rs @@ -0,0 +1,43 @@ +//! +//! The lexical comment parser output. +//! + +use crate::test::function_call::parser::lexical::token::lexeme::comment::Comment; + +/// +/// The lexical comment parser output. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Output { + /// The number of bytes in the comment. + pub length_bytes: usize, + /// The number of characters in the comment. + pub length_chars: usize, + /// The numbers of lines in the comment. + pub lines: usize, + /// The column where the comment ends. + pub column: usize, + /// The comment data. + pub comment: Comment, +} + +impl Output { + /// + /// A shortcut constructor. + /// + pub fn new( + length_bytes: usize, + length_chars: usize, + lines: usize, + column: usize, + comment: Comment, + ) -> Self { + Self { + length_bytes, + length_chars, + lines, + column, + comment, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/tests.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/tests.rs new file mode 100644 index 00000000..b94ed95d --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/comment/tests.rs @@ -0,0 +1,55 @@ +//! +//! The lexical comment parser tests. +//! + +use super::parse; +use super::Error; +use super::Output; +use crate::test::function_call::parser::lexical::token::lexeme::comment::Comment; + +#[test] +fn ok() { + let input = r#"# mega ultra comment text # "#; + let expected = Ok(Output::new( + input.len(), + input.len(), + input.lines().count() - 1, + input.len() + 1, + Comment::new(" mega ultra comment text ".to_owned()), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_multi_line() { + let input = r#"# This +is the mega ultra test application! +# +"#; + let expected = Ok(Output::new( + input.len(), + input.len(), + 3, + 1, + Comment::new(" This\nis the mega ultra test application!\n".to_owned()), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_not_a_comment_unterminated_double_quote() { + let input = r#"not a comment text"#; + let expected = Err(Error::NotAComment); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_not_a_comment_no_whitespace_in_the_end() { + let input = r#"#not a comment text#"#; + let expected = Err(Error::NotAComment); + let result = parse(input); + assert_eq!(result, expected); +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/error.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/error.rs new file mode 100644 index 00000000..d1449043 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/error.rs @@ -0,0 +1,24 @@ +//! +//! The lexical hex literal parser error. +//! + +/// +/// The lexical hex literal parser error. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// The lexeme is not a hex, which means that another parser must be run. + NotAHex, + /// The hex has not been terminated, which ends up with an entire file treated as an unterminated hex. + UnterminatedDoubleQuote { + /// The position where the unterminated hex ends. + offset: usize, + }, + /// A non-hexadecimal character is found in a hex literal. + ExpectedOneOfHexadecimal { + /// The invalid character. + found: char, + /// The position of the invalid character. + offset: usize, + }, +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/mod.rs new file mode 100644 index 00000000..b45975ee --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/mod.rs @@ -0,0 +1,94 @@ +//! +//! The lexical hex literal parser. +//! + +#[cfg(test)] +mod tests; + +pub mod error; +pub mod output; + +use std::str; + +use self::error::Error; +use self::output::Output; +use crate::test::function_call::parser::lexical::token::lexeme::literal::hex::Hex; + +/// +/// The parser state. +/// +pub enum State { + /// The initial state. + LetterH, + /// The `h` has been parsed so far. + LetterE, + /// The `he` has been parsed so far. + LetterX, + /// The `hex` has been parsed so far. + DoubleQuoteOpen, + /// The `"` has been parsed so far. + Character, +} + +/// +/// Parses a hex literal. +/// +/// Example: +/// 'hex"1234"' +/// +pub fn parse(input: &str) -> Result { + let mut state = State::LetterH; + let mut size = 0; + let mut value = String::with_capacity(64); + + loop { + let character = input.chars().nth(size); + match state { + State::LetterH => match character { + Some('h') => { + size += 1; + state = State::LetterE; + } + _ => return Err(Error::NotAHex), + }, + State::LetterE => match character { + Some('e') => { + size += 1; + state = State::LetterX; + } + _ => return Err(Error::NotAHex), + }, + State::LetterX => match character { + Some('x') => { + size += 1; + state = State::DoubleQuoteOpen; + } + _ => return Err(Error::NotAHex), + }, + State::DoubleQuoteOpen => match character { + Some('\"') => { + size += 1; + state = State::Character; + } + _ => return Err(Error::NotAHex), + }, + State::Character => match character { + Some('\"') => { + size += 1; + return Ok(Output::new(size, Hex::new(value))); + } + Some(character) => { + size += 1; + if !Hex::CHARACTERS.contains(&character) { + return Err(Error::ExpectedOneOfHexadecimal { + found: character, + offset: size, + }); + } + value.push(character); + } + None => return Err(Error::UnterminatedDoubleQuote { offset: size }), + }, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/output.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/output.rs new file mode 100644 index 00000000..0d649235 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/output.rs @@ -0,0 +1,25 @@ +//! +//! The lexical hex literal parser output. +//! + +use crate::test::function_call::parser::lexical::token::lexeme::literal::hex::Hex; + +/// +/// The lexical hex literal parser output. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Output { + /// The number of characters in the hex. + pub size: usize, + /// The hex data. + pub hex: Hex, +} + +impl Output { + /// + /// A shortcut constructor. + /// + pub fn new(size: usize, hex: Hex) -> Self { + Self { size, hex } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/tests.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/tests.rs new file mode 100644 index 00000000..8b1b7d6c --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/hex/tests.rs @@ -0,0 +1,45 @@ +//! +//! The lexical hex literal parser tests. +//! + +use super::parse; +use super::Error; +use super::Output; +use crate::test::function_call::parser::lexical::token::lexeme::literal::hex::Hex; + +#[test] +fn ok() { + let input = r#"hex"1234abcd""#; + let expected = Ok(Output::new(input.len(), Hex::new("1234abcd".to_owned()))); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_not_a_hex() { + let input = r#"hex "abc""#; + let expected = Err(Error::NotAHex); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_unterminated_double_quote() { + let input = r#"hex"1234"#; + let expected = Err(Error::UnterminatedDoubleQuote { + offset: input.len(), + }); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_expected_one_of_hexadecimal() { + let input = r#"hex"12345g""#; + let expected = Err(Error::ExpectedOneOfHexadecimal { + found: 'g', + offset: input.len() - 1, + }); + let result = parse(input); + assert_eq!(result, expected); +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/error.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/error.rs new file mode 100644 index 00000000..70ac4785 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/error.rs @@ -0,0 +1,38 @@ +//! +//! The lexical integer literal parser error. +//! + +/// +/// The lexical integer literal parser error. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Error { + /// The lexeme is not an integer, which means that another parser must be run. + NotAnInteger, + /// The lexeme is `0x`, which is not a valid hexadecimal literal. + EmptyHexadecimalBody { + /// The position where the literal ends. + offset: usize, + }, + /// A non-decimal or `x` character is found after a single `0`. + ExpectedOneOfDecimalOrX { + /// The invalid character. + found: char, + /// The position of the invalid character. + offset: usize, + }, + /// A non-decimal character is found in a decimal literal. + ExpectedOneOfDecimal { + /// The invalid character. + found: char, + /// The position of the invalid character. + offset: usize, + }, + /// A non-hexadecimal character is found in a hexadecimal literal. + ExpectedOneOfHexadecimal { + /// The invalid character. + found: char, + /// The position of the invalid character. + offset: usize, + }, +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/mod.rs new file mode 100644 index 00000000..f44f5e94 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/mod.rs @@ -0,0 +1,149 @@ +//! +//! The lexical integer literal parser. +//! + +#[cfg(test)] +mod tests; + +pub mod error; +pub mod output; + +use self::error::Error; +use self::output::Output; +use crate::test::function_call::parser::lexical::token::lexeme::literal::integer::Integer; + +/// +/// The parser state. +/// +pub enum State { + /// The initial state. + Start, + /// The `-` has been parsed so far. + Minus, + /// The `0` has been parsed so far. + ZeroOrHexadecimal, + /// The non-zero decimal character or `-0` or `00` has been parsed so far. + Decimal, + /// The `0x` has been parsed so far. + Hexadecimal, +} + +/// +/// Parses an integer literal. +/// +/// Integer literals can be of two types: +/// +/// 1. Decimal +/// '-42' +/// +/// 2. Hexadecimal +/// '2a' +/// +pub fn parse(input: &str) -> Result { + let mut state = State::Start; + let mut size = 0; + + let mut integer = String::new(); + let mut negative = false; + + loop { + let character = input.chars().nth(size); + match state { + State::Start => match character { + Some(Integer::CHARACTER_ZERO) => { + integer.push(Integer::CHARACTER_ZERO); + size += 1; + state = State::ZeroOrHexadecimal; + } + Some(Integer::CHARACTER_MINUS) => { + negative = true; + size += 1; + state = State::Minus; + } + Some(character) => { + if !Integer::CHARACTERS_DECIMAL.contains(&character) { + return Err(Error::NotAnInteger); + } + integer.push(character); + size += 1; + state = State::Decimal; + } + None => return Err(Error::NotAnInteger), + }, + State::Minus => match character { + Some(character) => { + if Integer::CHARACTERS_DECIMAL.contains(&character) { + integer.push(character); + size += 1; + state = State::Decimal; + } else { + return Err(Error::NotAnInteger); + } + } + None => return Err(Error::NotAnInteger), + }, + State::ZeroOrHexadecimal => match character { + Some(Integer::CHARACTER_INITIAL_HEXADECIMAL) => { + size += 1; + integer.clear(); + state = State::Hexadecimal; + } + Some(character) => { + if Integer::CHARACTERS_DECIMAL.contains(&character) { + integer.push(character); + size += 1; + state = State::Decimal; + } else if character.is_ascii_alphabetic() { + return Err(Error::ExpectedOneOfDecimalOrX { + found: character, + offset: size, + }); + } else { + return Ok(Output::new(size, Integer::new_decimal(integer, negative))); + } + } + None => return Ok(Output::new(size, Integer::new_decimal(integer, negative))), + }, + State::Decimal => match character { + Some(character) => { + if Integer::CHARACTERS_DECIMAL.contains(&character) { + integer.push(character); + size += 1; + } else if character.is_ascii_alphabetic() { + return Err(Error::ExpectedOneOfDecimal { + found: character, + offset: size, + }); + } else { + return Ok(Output::new(size, Integer::new_decimal(integer, negative))); + } + } + None => return Ok(Output::new(size, Integer::new_decimal(integer, negative))), + }, + State::Hexadecimal => match character { + Some(character) => { + if Integer::CHARACTERS_HEXADECIMAL.contains(&character) { + integer.push(character.to_ascii_lowercase()); + size += 1; + } else if character.is_ascii_alphabetic() { + return Err(Error::ExpectedOneOfHexadecimal { + found: character, + offset: size, + }); + } else { + if integer.is_empty() { + return Err(Error::EmptyHexadecimalBody { offset: size }); + } + return Ok(Output::new(size, Integer::new_hexadecimal(integer))); + } + } + None => { + if integer.is_empty() { + return Err(Error::EmptyHexadecimalBody { offset: size }); + } + return Ok(Output::new(size, Integer::new_hexadecimal(integer))); + } + }, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/output.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/output.rs new file mode 100644 index 00000000..59c46154 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/output.rs @@ -0,0 +1,25 @@ +//! +//! The lexical integer literal parser output. +//! + +use crate::test::function_call::parser::lexical::token::lexeme::literal::integer::Integer; + +/// +/// The lexical integer literal parser output. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Output { + /// The number of characters in the integer. + pub size: usize, + /// The integer data. + pub integer: Integer, +} + +impl Output { + /// + /// A shortcut constructor. + /// + pub fn new(size: usize, integer: Integer) -> Self { + Self { size, integer } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/tests.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/tests.rs new file mode 100644 index 00000000..cb1e4f63 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/integer/tests.rs @@ -0,0 +1,128 @@ +//! +//! The lexical integer literal parser tests. +//! + +use super::parse; +use super::Error; +use super::Output; +use crate::test::function_call::parser::lexical::token::lexeme::literal::integer::Integer; + +#[test] +fn ok_decimal_zero() { + let input = "0"; + let expected = Ok(Output::new( + input.len(), + Integer::new_decimal(input.to_owned(), false), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_decimal() { + let input = "666"; + let expected = Ok(Output::new( + input.len(), + Integer::new_decimal(input.to_owned(), false), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_decimal_negative() { + let input = "-666"; + let expected = Ok(Output::new( + input.len(), + Integer::new_decimal("666".to_owned(), true), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_hexadecimal_lowercase() { + let input = "0xdead666beef"; + let filtered = "dead666beef"; + let expected = Ok(Output::new( + input.len(), + Integer::new_hexadecimal(filtered.to_owned()), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_hexadecimal_uppercase() { + let input = "0xDEAD666BEEF"; + let filtered = "dead666beef"; + let expected = Ok(Output::new( + input.len(), + Integer::new_hexadecimal(filtered.to_owned()), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_hexadecimal_mixed_case() { + let input = "0xdEaD666bEeF"; + let filtered = "dead666beef"; + let expected = Ok(Output::new( + input.len(), + Integer::new_hexadecimal(filtered.to_owned()), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_not_an_integer() { + let input = "xyz"; + let expected = Err(Error::NotAnInteger); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_empty_hexadecimal_body() { + let input = "0x"; + let expected = Err(Error::EmptyHexadecimalBody { + offset: input.len(), + }); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_expected_one_of_decimal_or_x() { + let input = "0f"; + let expected = Err(Error::ExpectedOneOfDecimalOrX { + found: 'f', + offset: input.len() - 1, + }); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_expected_one_of_decimal() { + let input = "25x"; + let expected = Err(Error::ExpectedOneOfDecimal { + found: 'x', + offset: input.len() - 1, + }); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_expected_one_of_hexadecimal() { + let input = "0xABCX"; + let expected = Err(Error::ExpectedOneOfHexadecimal { + found: 'X', + offset: input.len() - 1, + }); + let result = parse(input); + assert_eq!(result, expected); +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/mod.rs new file mode 100644 index 00000000..4bfd06e2 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/mod.rs @@ -0,0 +1,257 @@ +//! +//! The lexical token stream. +//! + +pub mod comment; +pub mod hex; +pub mod integer; +pub mod string; +pub mod symbol; +pub mod word; + +use std::cell::RefCell; +use std::collections::VecDeque; +use std::rc::Rc; + +use self::comment::error::Error as CommentParserError; +use self::hex::error::Error as HexParserError; +use self::integer::error::Error as IntegerParserError; +use self::string::error::Error as StringParserError; +use self::symbol::error::Error as SymbolParserError; +use crate::test::function_call::parser::lexical::error::Error; +use crate::test::function_call::parser::lexical::token::lexeme::identifier::Identifier; +use crate::test::function_call::parser::lexical::token::lexeme::literal::Literal; +use crate::test::function_call::parser::lexical::token::lexeme::Lexeme; +use crate::test::function_call::parser::lexical::token::location::Location; +use crate::test::function_call::parser::lexical::token::Token; + +/// +/// A token stream is initialized for each input file. +/// +pub struct TokenStream<'a> { + /// The input source code string reference + input: &'a str, + /// The number of bytes processed so far + offset_bytes: usize, + /// The number of characters processed so far + offset_chars: usize, + /// The current position in the file + location: Location, + /// The queue buffer where the characters acquired with the look-ahead method are stored. + /// If the queue is not empty, the next character will be taken therefrom. + look_ahead: VecDeque, +} + +impl<'a> TokenStream<'a> { + /// The initial capacity of the look-ahead buffer queue. + const LOOK_AHEAD_INITIAL_CAPACITY: usize = 16; + + /// + /// Initializes a stream with a file identifier. + /// The file identifier can be used to get its path from the global type index. + /// + pub fn new(input: &'a str) -> Self { + Self { + input, + offset_bytes: 0, + offset_chars: 0, + location: Location::new(), + look_ahead: VecDeque::with_capacity(Self::LOOK_AHEAD_INITIAL_CAPACITY), + } + } + + /// + /// Wraps the stream into `Rc>` simplifying most of initializations. + /// + pub fn wrap(self) -> Rc> { + Rc::new(RefCell::new(self)) + } + + /// + /// Picks a character from the look-ahead queue. + /// If the queue is empty, advances the stream iterator. + /// + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> Result { + let token = match self.look_ahead.pop_front() { + Some(token) => token, + None => self.advance()?, + }; + Ok(token) + } + + /// + /// Initializes a stream with an auto-generated file identifier. + /// The file identifier can be used to get its path from the global type index. + /// Used for testing purposes. + /// + #[allow(dead_code)] + pub fn test(input: &'a str) -> Self { + Self { + input, + offset_bytes: 0, + offset_chars: 0, + location: Location::new(), + look_ahead: VecDeque::with_capacity(Self::LOOK_AHEAD_INITIAL_CAPACITY), + } + } + + /// + /// The function checks if a character: + /// 1. Is a whitespace -> skip + /// 2. Starts a comment -> start the comment subparser + /// 3. Starts a string literal -> start the string subparser + /// 4. Starts a number -> start the number subparser + /// 5. Starts a word -> start the word subparser (also tries to parse boolean or hex literal) + /// 6. Starts a symbol -> start the operand subparser + /// 7. Is unknown -> yield an 'invalid character' error + /// + /// If the end of input has been reached, an 'EOF' token is returned for consequent calls. + /// + fn advance(&mut self) -> Result { + while let Some(character) = self.input.chars().nth(self.offset_chars) { + if character.is_ascii_whitespace() { + if character == '\n' { + self.location.line += 1; + self.location.column = 1; + } else if character != '\r' { + self.location.column += 1; + } + self.offset_bytes += 1; + self.offset_chars += 1; + continue; + } + + if character == '#' { + match self::comment::parse(&self.input[self.offset_bytes..]) { + Ok(output) => { + self.location = + self.location + .shifted_down(output.lines, output.column, output.column); + self.offset_bytes += output.length_bytes; + self.offset_chars += output.length_chars; + continue; + } + Err(CommentParserError::NotAComment) => {} + } + } + + if character == '\"' { + match self::string::parse(&self.input[self.offset_bytes..]) { + Ok(output) => { + let location = self.location; + self.location = + self.location + .shifted_down(output.lines, output.column, output.column); + self.offset_bytes += output.length_bytes; + self.offset_chars += output.length_chars; + return Ok(Token::new( + Lexeme::Literal(Literal::String(output.string)), + location, + )); + } + Err(StringParserError::NotAString) => {} + Err(StringParserError::UnterminatedDoubleQuote { lines, column }) => { + return Err(Error::unterminated_double_quote_string( + self.location, + self.location.shifted_down(lines, column, column - 1), + )); + } + } + } + + if character.is_ascii_digit() || character == '-' { + match self::integer::parse(&self.input[self.offset_bytes..]) { + Ok(output) => { + let location = self.location; + self.location.column += output.size; + self.offset_bytes += output.size; + self.offset_chars += output.size; + return Ok(Token::new( + Lexeme::Literal(Literal::Integer(output.integer)), + location, + )); + } + Err(IntegerParserError::NotAnInteger) => {} + Err(IntegerParserError::EmptyHexadecimalBody { offset }) => { + return Err(Error::unexpected_end(self.location.shifted_right(offset))); + } + Err(IntegerParserError::ExpectedOneOfDecimalOrX { found, offset }) => { + return Err(Error::expected_one_of_decimal_or_x_integer( + self.location.shifted_right(offset), + found, + )); + } + Err(IntegerParserError::ExpectedOneOfDecimal { found, offset }) => { + return Err(Error::expected_one_of_decimal_integer( + self.location.shifted_right(offset), + found, + )); + } + Err(IntegerParserError::ExpectedOneOfHexadecimal { found, offset }) => { + return Err(Error::expected_one_of_hexadecimal_integer( + self.location.shifted_right(offset), + found, + )); + } + } + } + + if Identifier::can_start_with(character) { + let output = self::word::parse(&self.input[self.offset_bytes..]); + if let Lexeme::Identifier(Identifier { inner }) = output.word.clone() { + if inner == "hex" { + match self::hex::parse(&self.input[self.offset_bytes..]) { + Ok(output) => { + let location = self.location; + self.location.column += output.size; + self.offset_bytes += output.size; + self.offset_chars += output.size; + return Ok(Token::new( + Lexeme::Literal(Literal::Hex(output.hex)), + location, + )); + } + Err(HexParserError::NotAHex) => {} + Err(HexParserError::UnterminatedDoubleQuote { offset }) => { + return Err(Error::unterminated_double_quote_hex( + self.location, + self.location.shifted_right(offset), + )); + } + Err(HexParserError::ExpectedOneOfHexadecimal { found, offset }) => { + return Err(Error::expected_one_of_hexadecimal_hex( + self.location.shifted_right(offset), + found, + )); + } + } + } + } + let location = self.location; + self.location.column += output.size; + self.offset_bytes += output.size; + self.offset_chars += output.size; + return Ok(Token::new(output.word, location)); + } + + return match self::symbol::parse(&self.input[self.offset_bytes..]) { + Ok(output) => { + let location = self.location; + self.location.column += output.size; + self.offset_bytes += output.size; + self.offset_chars += output.size; + Ok(Token::new(Lexeme::Symbol(output.symbol), location)) + } + Err(SymbolParserError::InvalidCharacter { found, offset }) => Err( + Error::invalid_character(self.location.shifted_right(offset), found), + ), + Err(SymbolParserError::UnexpectedEnd) => { + Err(Error::unexpected_end(self.location.shifted_right(1))) + } + }; + } + + Ok(Token::new(Lexeme::Eof, self.location)) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/string/error.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/error.rs new file mode 100644 index 00000000..9106e304 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/error.rs @@ -0,0 +1,19 @@ +//! +//! The lexical string literal parser error. +//! + +/// +/// The lexical string literal parser error. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + /// The lexeme is not a string, which means that another parser must be run. + NotAString, + /// The string has not been terminated, which ends up with an entire file treated as an unterminated string. + UnterminatedDoubleQuote { + /// The number of lines in the unterminated string. + lines: usize, + /// The column where the unterminated string ends. + column: usize, + }, +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/string/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/mod.rs new file mode 100644 index 00000000..e928cc66 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/mod.rs @@ -0,0 +1,79 @@ +//! +//! The lexical string literal parser. +//! + +#[cfg(test)] +mod tests; + +pub mod error; +pub mod output; + +use self::error::Error; +use self::output::Output; +use crate::test::function_call::parser::lexical::token::lexeme::literal::string::String as LexicalString; + +/// +/// The parser state. +/// +pub enum State { + /// The initial state. + DoubleQuoteOpen, + /// The `"` has been parsed so far. + Character, +} + +/// +/// Parses a string literal. +/// +/// Example: +/// '"abc"' +/// +pub fn parse(input: &str) -> Result { + let mut state = State::DoubleQuoteOpen; + let mut length_chars = 0; + let mut length_bytes = 0; + let mut lines = 0; + let mut column = 1; + let mut value = String::new(); + loop { + let character = input.chars().nth(length_chars); + match state { + State::DoubleQuoteOpen => match character { + Some('\"') => { + length_chars += 1; + length_bytes += 1; + column += 1; + state = State::Character; + } + _ => return Err(Error::NotAString), + }, + State::Character => match character { + Some('\"') => { + length_chars += 1; + length_bytes += 1; + return Ok(Output::new( + length_bytes, + length_chars, + lines, + column, + LexicalString::new(value), + )); + } + Some('\n') => { + value.push('\n'); + length_chars += 1; + length_bytes += 1; + lines += 1; + column += 1; + } + Some(character) => { + value.push(character); + length_chars += 1; + length_bytes += character.len_utf8(); + column += 1; + } + None => return Err(Error::UnterminatedDoubleQuote { lines, column }), + }, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/string/output.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/output.rs new file mode 100644 index 00000000..d48a7755 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/output.rs @@ -0,0 +1,43 @@ +//! +//! The lexical string literal parser output. +//! + +use crate::test::function_call::parser::lexical::token::lexeme::literal::string::String; + +/// +/// The lexical string literal parser output. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Output { + /// The number of bytes in the string. + pub length_bytes: usize, + /// The number of characters in the string. + pub length_chars: usize, + /// The numbers of lines in the string. + pub lines: usize, + /// The column where the string ends. + pub column: usize, + /// The string data. + pub string: String, +} + +impl Output { + /// + /// A shortcut constructor. + /// + pub fn new( + length_bytes: usize, + length_chars: usize, + lines: usize, + column: usize, + string: String, + ) -> Self { + Self { + length_bytes, + length_chars, + lines, + column, + string, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/string/tests.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/tests.rs new file mode 100644 index 00000000..e33b912a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/string/tests.rs @@ -0,0 +1,41 @@ +//! +//! The lexical string literal parser tests. +//! + +use super::parse; +use super::Error; +use super::Output; +use crate::test::function_call::parser::lexical::token::lexeme::literal::string::String; + +#[test] +fn ok() { + let input = r#""some string""#; + let expected = Ok(Output::new( + input.len(), + input.len(), + input.lines().count() - 1, + input.len(), + String::new("some string".to_owned()), + )); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_not_a_string() { + let input = r#"no double quote here"#; + let expected = Err(Error::NotAString); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_unterminated_double_quote() { + let input = r#""some string"#; + let expected = Err(Error::UnterminatedDoubleQuote { + lines: input.lines().count() - 1, + column: input.len() + 1, + }); + let result = parse(input); + assert_eq!(result, expected); +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/error.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/error.rs new file mode 100644 index 00000000..35019394 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/error.rs @@ -0,0 +1,19 @@ +//! +//! The lexical symbol parser error. +//! + +/// +/// The lexical symbol parser error. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Error { + /// An unexpected character resulting into an invalid symbol. + InvalidCharacter { + /// The invalid character. + found: char, + /// The position of the invalid character. + offset: usize, + }, + /// Unable to finish a symbol. + UnexpectedEnd, +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/mod.rs new file mode 100644 index 00000000..01ee49ea --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/mod.rs @@ -0,0 +1,78 @@ +//! +//! The lexical symbol parser. +//! + +#[cfg(test)] +mod tests; + +pub mod error; +pub mod output; + +use std::str; + +use self::error::Error; +use self::output::Output; +use crate::test::function_call::parser::lexical::token::lexeme::symbol::Symbol; + +/// +/// The parser state. +/// +pub enum State { + /// The initial state. + Start, + /// The `-` has been parsed so far. + Minus, +} + +/// +/// Parses a symbol. +/// +/// Returns the symbol and its size. +/// +pub fn parse(input: &str) -> Result { + let mut state = State::Start; + let mut size = 0; + + loop { + let character = input.chars().nth(size); + match state { + State::Start => match character { + Some('[') => return Ok(Output::new(size + 1, Symbol::BracketSquareLeft)), + Some(']') => return Ok(Output::new(size + 1, Symbol::BracketSquareRight)), + Some('(') => return Ok(Output::new(size + 1, Symbol::ParenthesisLeft)), + Some(')') => return Ok(Output::new(size + 1, Symbol::ParenthesisRight)), + Some('<') => return Ok(Output::new(size + 1, Symbol::Lesser)), + Some('>') => return Ok(Output::new(size + 1, Symbol::Greater)), + + Some(':') => return Ok(Output::new(size + 1, Symbol::Colon)), + Some(',') => return Ok(Output::new(size + 1, Symbol::Comma)), + Some('~') => return Ok(Output::new(size + 1, Symbol::Tilde)), + Some('#') => return Ok(Output::new(size + 1, Symbol::Number)), + + Some('-') => { + size += 1; + state = State::Minus; + } + Some(character) => { + return Err(Error::InvalidCharacter { + found: character, + offset: size, + }); + } + None => return Err(Error::UnexpectedEnd), + }, + State::Minus => { + return match character { + Some('>') => Ok(Output::new(size + 1, Symbol::Arrow)), + Some(character) => { + return Err(Error::InvalidCharacter { + found: character, + offset: size, + }); + } + None => return Err(Error::UnexpectedEnd), + } + } + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/output.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/output.rs new file mode 100644 index 00000000..1badb286 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/output.rs @@ -0,0 +1,25 @@ +//! +//! The lexical symbol parser output. +//! + +use crate::test::function_call::parser::lexical::token::lexeme::symbol::Symbol; + +/// +/// The lexical symbol parser output. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Output { + /// The number of characters in the symbol. + pub size: usize, + /// The symbol data. + pub symbol: Symbol, +} + +impl Output { + /// + /// A shortcut constructor. + /// + pub fn new(size: usize, symbol: Symbol) -> Self { + Self { size, symbol } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/tests.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/tests.rs new file mode 100644 index 00000000..28b01d2b --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/symbol/tests.rs @@ -0,0 +1,35 @@ +//! +//! The lexical symbol parser tests. +//! + +use super::parse; +use super::Error; +use super::Output; +use crate::test::function_call::parser::lexical::token::lexeme::symbol::Symbol; + +#[test] +fn ok() { + let input = "->"; + let expected = Ok(Output::new(input.len(), Symbol::Arrow)); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_invalid_character() { + let input = "@"; + let expected = Err(Error::InvalidCharacter { + found: input.chars().collect::>()[0], + offset: 0, + }); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn error_unexpected_end() { + let input = ""; + let expected = Err(Error::UnexpectedEnd); + let result = parse(input); + assert_eq!(result, expected); +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/word/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/word/mod.rs new file mode 100644 index 00000000..cddfdc17 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/word/mod.rs @@ -0,0 +1,75 @@ +//! +//! The lexical word parser. +//! + +#[cfg(test)] +mod tests; + +pub mod output; + +use std::str::FromStr; + +use crate::test::function_call::parser::lexical::token::lexeme::identifier::Error as IdentifierError; +use crate::test::function_call::parser::lexical::token::lexeme::identifier::Identifier; +use crate::test::function_call::parser::lexical::token::lexeme::literal::boolean::Boolean; +use crate::test::function_call::parser::lexical::token::lexeme::literal::Literal; +use crate::test::function_call::parser::lexical::token::lexeme::Lexeme; + +use self::output::Output; + +/// +/// The parser state. +/// +pub enum State { + /// The initial state. + Start, + /// The first character has been parsed so far. + Continue, +} + +/// +/// Parses a word. The word can result into several token types: +/// +/// 1. An identifier +/// 'value' +/// Any valid identifier which is not a keyword. +/// +/// 2. A boolean literal +/// 'true' +/// The literal is also a keyword, but is was decided to treat literals as a separate token type. +/// +/// 3. A keyword +/// 'emit' +/// Any keyword which is not a boolean literal. +/// +pub fn parse(input: &str) -> Output { + let mut state = State::Start; + let mut size = 0; + + while let Some(character) = input.chars().nth(size) { + match state { + State::Start => { + if !Identifier::can_start_with(character) { + break; + } + state = State::Continue; + } + State::Continue => { + if !Identifier::can_contain_after_start(character) { + break; + } + } + } + + size += 1; + } + + let lexeme = match Identifier::from_str(&input[..size]) { + Ok(identifier) => Lexeme::Identifier(identifier), + Err(IdentifierError::IsKeyword(keyword)) => match Boolean::try_from(keyword) { + Ok(boolean) => Lexeme::Literal(Literal::Boolean(boolean)), + Err(keyword) => Lexeme::Keyword(keyword), + }, + }; + Output::new(size, lexeme) +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/word/output.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/word/output.rs new file mode 100644 index 00000000..d634ed69 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/word/output.rs @@ -0,0 +1,25 @@ +//! +//! The lexical word parser output. +//! + +use crate::test::function_call::parser::lexical::token::lexeme::Lexeme; + +/// +/// The lexical word parser output. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Output { + /// The number of characters in the word. + pub size: usize, + /// The word lexeme data. + pub word: Lexeme, +} + +impl Output { + /// + /// A shortcut constructor. + /// + pub fn new(size: usize, word: Lexeme) -> Self { + Self { size, word } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/stream/word/tests.rs b/solidity_adapter/src/test/function_call/parser/lexical/stream/word/tests.rs new file mode 100644 index 00000000..8a1543f1 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/stream/word/tests.rs @@ -0,0 +1,95 @@ +//! +//! The lexical word parser tests. +//! + +use super::parse; +use super::Output; +use crate::test::function_call::parser::lexical::token::lexeme::identifier::Identifier; +use crate::test::function_call::parser::lexical::token::lexeme::keyword::Keyword; +use crate::test::function_call::parser::lexical::token::lexeme::literal::boolean::Boolean; +use crate::test::function_call::parser::lexical::token::lexeme::literal::Literal; +use crate::test::function_call::parser::lexical::token::lexeme::Lexeme; + +#[test] +fn ok_identifier() { + let input = "xyz"; + let expected = Output::new( + input.len(), + Lexeme::Identifier(Identifier::new(input.to_owned())), + ); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_identifier_below_field_range() { + let input = "uint0"; + let expected = Output::new( + input.len(), + Lexeme::Identifier(Identifier::new(input.to_owned())), + ); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_identifier_above_field_range() { + let input = "bytes33"; + let expected = Output::new( + input.len(), + Lexeme::Identifier(Identifier::new(input.to_owned())), + ); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_identifier_invalid_modulo() { + let input = "uint119"; + let expected = Output::new( + input.len(), + Lexeme::Identifier(Identifier::new(input.to_owned())), + ); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_keyword() { + let input = "gas"; + let expected = Output::new(input.len(), Lexeme::Keyword(Keyword::Gas)); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_keyword_signed_integer_min() { + let input = "int8"; + let expected = Output::new(input.len(), Lexeme::Keyword(Keyword::new_integer_signed(8))); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_keyword_unsigned_integer_max() { + let input = "uint256"; + let expected = Output::new( + input.len(), + Lexeme::Keyword(Keyword::new_integer_unsigned( + compiler_common::BIT_LENGTH_FIELD, + )), + ); + let result = parse(input); + assert_eq!(result, expected); +} + +#[test] +fn ok_literal_boolean() { + let input = "true"; + let expected = Output::new( + input.len(), + Lexeme::Literal(Literal::Boolean(Boolean::r#true())), + ); + let result = parse(input); + assert_eq!(result, expected); +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/tests.rs b/solidity_adapter/src/test/function_call/parser/lexical/tests.rs new file mode 100644 index 00000000..664ecb64 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/tests.rs @@ -0,0 +1,144 @@ +//! +//! The lexical parser tests. +//! + +use super::error::Error; +use super::stream::TokenStream; +use super::token::lexeme::identifier::Identifier; +use super::token::lexeme::keyword::Keyword; +use super::token::lexeme::literal::integer::Integer; +use super::token::lexeme::literal::Literal; +use super::token::lexeme::symbol::Symbol; +use super::token::lexeme::Lexeme; +use super::token::location::Location; +use super::token::Token; + +#[test] +fn ok() { + let input = r#" +# This is the mega ultra test application! +# +f(uint256): 2 -> -2 +"#; + + let expected = vec![ + Token { + lexeme: Lexeme::Identifier(Identifier::new("f".to_owned())), + location: Location::test(4, 1), + }, + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft), + location: Location::test(4, 2), + }, + Token { + lexeme: Lexeme::Keyword(Keyword::new_integer_unsigned(256)), + location: Location::test(4, 3), + }, + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + location: Location::test(4, 10), + }, + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + location: Location::test(4, 11), + }, + Token { + lexeme: Lexeme::Literal(Literal::Integer(Integer::new_decimal( + "2".to_owned(), + false, + ))), + location: Location::test(4, 13), + }, + Token { + lexeme: Lexeme::Symbol(Symbol::Arrow), + location: Location::test(4, 15), + }, + Token { + lexeme: Lexeme::Literal(Literal::Integer(Integer::new_decimal("2".to_owned(), true))), + location: Location::test(4, 18), + }, + ] + .into_iter() + .collect::>(); + + let mut result = Vec::with_capacity(expected.len()); + let mut stream = TokenStream::test(input); + loop { + match stream.next().expect("Always valid") { + Token { + lexeme: Lexeme::Eof, + .. + } => break, + token => result.push(token), + } + } + + assert_eq!(result, expected); +} + +#[test] +fn error_unterminated_double_quote_string() { + let input = "\"double quote string"; + + let expected: Result = Err(Error::unterminated_double_quote_string( + Location::test(1, 1), + Location::test(1, 21), + )); + + let result = TokenStream::test(input).next(); + + assert_eq!(result, expected); +} + +#[test] +fn error_expected_one_of_decimal() { + let input = "42x"; + + let expected: Result = Err(Error::expected_one_of_decimal_integer( + Location::test(1, 3), + 'x', + )); + + let result = TokenStream::test(input).next(); + + assert_eq!(result, expected); +} + +#[test] +fn error_expected_one_of_hexadecimal() { + let input = "0x42t"; + + let expected: Result = Err(Error::expected_one_of_hexadecimal_integer( + Location::test(1, 5), + 't', + )); + + let result = TokenStream::test(input).next(); + + assert_eq!(result, expected); +} + +#[test] +fn error_invalid_character() { + let input = "@"; + + let expected: Result = Err(Error::invalid_character( + Location::test(1, 1), + input.chars().collect::>()[0], + )); + + let result = TokenStream::test(input).next(); + + assert_eq!(result, expected); +} + +#[test] +fn error_unexpected_end() { + let input = "0x"; + + let expected: Result = Err(Error::unexpected_end(Location::test(1, 3))); + + let result = TokenStream::test(input).next(); + + assert_eq!(result, expected); +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/comment.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/comment.rs new file mode 100644 index 00000000..c17e5a52 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/comment.rs @@ -0,0 +1,29 @@ +//! +//! The lexical token comment lexeme. +//! + +use std::fmt; + +/// +/// The source code comment, which is dropped during the lexical analysis. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Comment { + /// The inner comment contents. + inner: String, +} + +impl Comment { + /// + /// Creates a comment. + /// + pub fn new(inner: String) -> Self { + Self { inner } + } +} + +impl fmt::Display for Comment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/identifier.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/identifier.rs new file mode 100644 index 00000000..930a1017 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/identifier.rs @@ -0,0 +1,88 @@ +//! +//! The lexical token identifier lexeme. +//! + +use std::fmt; +use std::str::FromStr; + +use crate::test::function_call::parser::lexical::token::lexeme::keyword::Keyword; + +/// +/// An identifier lexeme, which is usually used to name an item. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Identifier { + /// The identifier string contents. + pub inner: String, +} + +/// +/// The identifier parsing error. +/// +/// If the parser returns such an error, it means that the word is not a valid identifier, +/// but a keyword. +/// +#[derive(Debug)] +pub enum Error { + /// The word is a keyword. + IsKeyword(Keyword), +} + +impl Identifier { + /// The underscore character, which can appear at the beginning of an identifier. + pub const CHARACTER_DELIMITER: char = '_'; + + /// + /// Creates an identifier value. + /// + #[allow(dead_code)] + pub fn new(inner: String) -> Self { + Self { inner } + } + + /// + /// Checks if identifier can have the `character` at the first position. + /// + /// Only alphabetic characters and the underscore character are allowed. + /// + pub fn can_start_with(character: char) -> bool { + character.is_ascii_alphabetic() || character == Self::CHARACTER_DELIMITER + } + + /// + /// Checks if identifier can have the `character` at the second position. + /// + /// Only alphanumeric characters and the underscore character are allowed. + /// + pub fn can_contain_after_start(character: char) -> bool { + character.is_ascii_alphanumeric() || character == Self::CHARACTER_DELIMITER + } +} + +impl TryFrom for Identifier { + type Error = Error; + + fn try_from(input: String) -> Result { + Self::from_str(input.as_str()) + } +} + +impl FromStr for Identifier { + type Err = Error; + + fn from_str(input: &str) -> Result { + if let Ok(keyword) = Keyword::try_from(input) { + return Err(Error::IsKeyword(keyword)); + } + + Ok(Self { + inner: input.to_owned(), + }) + } +} + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/keyword.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/keyword.rs new file mode 100644 index 00000000..0833da86 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/keyword.rs @@ -0,0 +1,268 @@ +//! +//! The lexical token keyword lexeme. +//! + +use std::fmt; +use std::ops::RangeInclusive; +use std::str; + +/// +/// The keyword defined in the language. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Keyword { + /// The `left` keyword. + Left, + /// The `right` keyword. + Right, + /// The `FAILURE` keyword. + Failure, + + /// The `library` keyword. + Library, + /// The `emit` keyword. + Emit, + /// The `from` keyword. + From, + /// The `anonymous` keyword. + Anonymous, + /// The `ether` keyword. + Ether, + /// The `wei` keyword. + Wei, + + /// The `gas` keyword. + Gas, + /// The `ir` keyword. + Ir, + /// The `irOptimized` keyword. + IrOptimized, + /// The `legacy` keyword. + Legacy, + /// The `legacyOptimized` keyword. + LegacyOptimized, + + /// The `bool` type keyword. + Bool, + /// The `string` type keyword. + String, + /// The `address` type keyword. + Address, + /// The `function` type keyword. + Function, + /// The `uint{N}` type keyword. + IntegerUnsigned { + /// The unsigned type bit-length. + bit_length: usize, + }, + /// The `int{N}` type keyword. + IntegerSigned { + /// The signed type bit-length. + bit_length: usize, + }, + /// The `bytes{}` type keyword. + Bytes { + /// The bytes type byte-length. + byte_length: Option, + }, + + /// The `true` literal keyword. + True, + /// The `false` literal keyword. + False, +} + +impl Keyword { + /// The range including the minimal and maximal integer bit-lengths. + pub const INTEGER_BIT_LENGTH_RANGE: RangeInclusive = + compiler_common::BIT_LENGTH_BYTE..=compiler_common::BIT_LENGTH_FIELD; + + /// The range including the minimal and maximal bytes byte-lengths. + pub const BYTES_BYTE_LENGTH_RANGE: RangeInclusive = + compiler_common::BYTE_LENGTH_BYTE..=compiler_common::BYTE_LENGTH_FIELD; + + /// + /// Creates a `uint{N}` keyword. + /// + pub fn new_integer_unsigned(bit_length: usize) -> Self { + Self::IntegerUnsigned { bit_length } + } + + /// + /// Creates an `int{N}` keyword. + /// + pub fn new_integer_signed(bit_length: usize) -> Self { + Self::IntegerSigned { bit_length } + } + + /// + /// Creates an `bytes{N}` keyword. + /// + pub fn new_bytes(byte_length: Option) -> Self { + Self::Bytes { byte_length } + } +} + +/// +/// The keyword parsing error. +/// +/// If the parser returns such an error, it means that the word is not a keyword, +/// but an ordinar identifier or something else. +/// +#[derive(Debug)] +pub enum Error { + /// There is no number after the `uint` or `int` character. + IntegerBitLengthEmpty, + /// There is an invalid after the `uint` or `int` character. + IntegerBitLengthNotNumeric(String), + /// The bit-length is not multiple of `8`, which is forbidden. + IntegerBitLengthNotMultipleOfEight(usize, usize), + /// The bit-length is beyond the allowed range. + IntegerBitLengthOutOfRange(usize, RangeInclusive), + /// There is an invalid after the `bytes` character. + BytesByteLengthNotNumeric(String), + /// The byte-length is beyond the allowed range. + BytesByteLengthOutOfRange(usize, RangeInclusive), + /// The keyword is unknown, which means that the word is a valid identifier or something else. + Unknown(String), +} + +impl TryFrom<&str> for Keyword { + type Error = Error; + + /// + /// The converter checks if the number after the `uint` or `int` symbol represents a valid + /// amount of bits for an integer value (Also valid amount of bytes after `bytes`). If the + /// amount is not a valid, the word is treated as an ordinar identifier. + /// + fn try_from(input: &str) -> Result { + match input { + "left" => return Ok(Self::Left), + "right" => return Ok(Self::Right), + "FAILURE" => return Ok(Self::Failure), + + "library" => return Ok(Self::Library), + "emit" => return Ok(Self::Emit), + "from" => return Ok(Self::From), + "anonymous" => return Ok(Self::Anonymous), + "ether" => return Ok(Self::Ether), + "wei" => return Ok(Self::Wei), + + "gas" => return Ok(Self::Gas), + "ir" => return Ok(Self::Ir), + "irOptimized" => return Ok(Self::IrOptimized), + "legacy" => return Ok(Self::Legacy), + "legacyOptimized" => return Ok(Self::LegacyOptimized), + + "bool" => return Ok(Self::Bool), + "string" => return Ok(Self::String), + "address" => return Ok(Self::Address), + "function" => return Ok(Self::Function), + "bytes" => return Ok(Self::new_bytes(None)), + + "true" => return Ok(Self::True), + "false" => return Ok(Self::False), + + _ => {} + } + + if let Some("uint") = input.get(..4) { + let bit_length = &input[4..]; + if bit_length.is_empty() { + return Err(Error::IntegerBitLengthEmpty); + } + let bit_length = bit_length + .parse::() + .map_err(|_| Error::IntegerBitLengthNotNumeric(bit_length.to_owned()))?; + if !Self::INTEGER_BIT_LENGTH_RANGE.contains(&bit_length) { + return Err(Error::IntegerBitLengthOutOfRange( + bit_length, + Self::INTEGER_BIT_LENGTH_RANGE, + )); + } + if bit_length % compiler_common::BIT_LENGTH_BYTE != 0 { + return Err(Error::IntegerBitLengthNotMultipleOfEight( + bit_length, + compiler_common::BIT_LENGTH_BYTE, + )); + } + return Ok(Self::new_integer_unsigned(bit_length)); + } + + if let Some("int") = input.get(..3) { + let bit_length = &input[3..]; + if bit_length.is_empty() { + return Err(Error::IntegerBitLengthEmpty); + } + let bit_length = bit_length + .parse::() + .map_err(|_| Error::IntegerBitLengthNotNumeric(bit_length.to_owned()))?; + if !Self::INTEGER_BIT_LENGTH_RANGE.contains(&bit_length) { + return Err(Error::IntegerBitLengthOutOfRange( + bit_length, + Self::INTEGER_BIT_LENGTH_RANGE, + )); + } + if bit_length % compiler_common::BIT_LENGTH_BYTE != 0 { + return Err(Error::IntegerBitLengthNotMultipleOfEight( + bit_length, + compiler_common::BIT_LENGTH_BYTE, + )); + } + return Ok(Self::new_integer_signed(bit_length)); + } + + if let Some("bytes") = input.get(..5) { + let byte_length = &input[5..]; + let byte_length = byte_length + .parse::() + .map_err(|_| Error::BytesByteLengthNotNumeric(byte_length.to_owned()))?; + if !Self::BYTES_BYTE_LENGTH_RANGE.contains(&byte_length) { + return Err(Error::BytesByteLengthOutOfRange( + byte_length, + Self::BYTES_BYTE_LENGTH_RANGE, + )); + } + return Ok(Self::new_bytes(Some(byte_length))); + } + + Err(Error::Unknown(input.to_owned())) + } +} + +impl fmt::Display for Keyword { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Left => write!(f, "left"), + Self::Right => write!(f, "right"), + Self::Failure => write!(f, "FAILURE"), + + Self::Library => write!(f, "library"), + Self::Emit => write!(f, "emit"), + Self::From => write!(f, "from"), + Self::Anonymous => write!(f, "anonymous"), + Self::Ether => write!(f, "ether"), + Self::Wei => write!(f, "wei"), + + Self::Gas => write!(f, "gas"), + Self::Ir => write!(f, "ir"), + Self::IrOptimized => write!(f, "irOptimized"), + Self::Legacy => write!(f, "legacy"), + Self::LegacyOptimized => write!(f, "legacyOptimized"), + + Self::Bool => write!(f, "bool"), + Self::String => write!(f, "string"), + Self::Address => write!(f, "address"), + Self::Function => write!(f, "function"), + Self::IntegerUnsigned { bit_length } => write!(f, "uint{bit_length}"), + Self::IntegerSigned { bit_length } => write!(f, "int{bit_length}"), + Self::Bytes { byte_length: None } => write!(f, "bytes"), + Self::Bytes { + byte_length: Some(byte_length), + } => write!(f, "bytes{byte_length}"), + + Self::True => write!(f, "true"), + Self::False => write!(f, "false"), + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/boolean.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/boolean.rs new file mode 100644 index 00000000..b2c2ded3 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/boolean.rs @@ -0,0 +1,65 @@ +//! +//! The lexical token boolean literal lexeme. +//! + +use std::fmt; + +use crate::test::function_call::parser::lexical::token::lexeme::keyword::Keyword; + +/// +/// The lexical boolean literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Boolean { + /// Created from the `false` keyword. + False, + /// Created from the `true` keyword. + True, +} + +impl Boolean { + /// + /// Creates a `false` value. + /// + pub fn r#false() -> Self { + Self::False + } + + /// + /// Creates a `true` value. + /// + pub fn r#true() -> Self { + Self::True + } +} + +impl TryFrom for Boolean { + type Error = Keyword; + + fn try_from(keyword: Keyword) -> Result { + Ok(match keyword { + Keyword::False => Self::False, + Keyword::True => Self::True, + unknown => return Err(unknown), + }) + } +} + +#[allow(clippy::from_over_into)] +impl Into for Boolean { + fn into(self) -> bool { + match self { + Self::False => false, + Self::True => true, + } + } +} + +impl fmt::Display for Boolean { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::False => write!(f, "false"), + Self::True => write!(f, "true"), + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/hex.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/hex.rs new file mode 100644 index 00000000..3b0ff995 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/hex.rs @@ -0,0 +1,46 @@ +//! +//! The lexical token hex literal lexeme. +//! + +use std::fmt; + +/// +/// The lexical hex literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Hex { + /// The inner contents. + pub inner: std::string::String, +} + +impl Hex { + /// Characters allowed in the hex literal. + pub const CHARACTERS: [char; 22] = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', + 'C', 'D', 'E', 'F', + ]; + + /// + /// Creates a hex literal value. + /// + pub fn new(inner: std::string::String) -> Self { + Self { inner } + } +} + +#[allow(clippy::from_over_into)] +impl Into for Hex { + fn into(self) -> std::string::String { + let mut string = "hex\"".to_owned(); + string.push_str(&self.inner); + string.push('"'); + string + } +} + +impl fmt::Display for Hex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string: String = self.to_owned().into(); + write!(f, "{string}") + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/integer.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/integer.rs new file mode 100644 index 00000000..b425e24f --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/integer.rs @@ -0,0 +1,77 @@ +//! +//! The lexical token integer literal lexeme. +//! + +use std::fmt; + +/// +/// The lexical integer literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Integer { + /// An decimal literal, like `42`. + Decimal { + /// The inner without sign. + inner: String, + /// The is negative flag. + negative: bool, + }, + /// A hexadecimal literal, like `0xffff`. + Hexadecimal(String), +} + +impl Integer { + /// Characters allowed in the decimal literal. + pub const CHARACTERS_DECIMAL: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + /// Characters allowed in the hexadecimal literal. + pub const CHARACTERS_HEXADECIMAL: [char; 22] = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', + 'C', 'D', 'E', 'F', + ]; + + /// The minus character. + pub const CHARACTER_MINUS: char = '-'; + /// The zero character at the beginning hexadecimal literals. + pub const CHARACTER_ZERO: char = '0'; + /// The hexadecimal literal second character. + pub const CHARACTER_INITIAL_HEXADECIMAL: char = 'x'; + + /// + /// Creates a decimal value. + /// + pub fn new_decimal(inner: String, negative: bool) -> Self { + Self::Decimal { inner, negative } + } + + /// + /// Creates a hexadecimal value. + /// + pub fn new_hexadecimal(inner: String) -> Self { + Self::Hexadecimal(inner) + } +} + +#[allow(clippy::from_over_into)] +impl Into for Integer { + fn into(self) -> String { + match self { + Self::Decimal { + mut inner, + negative, + } => { + if negative { + inner.insert(0, Self::CHARACTER_MINUS); + } + inner + } + Self::Hexadecimal(inner) => inner, + } + } +} + +impl fmt::Display for Integer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string: String = self.to_owned().into(); + write!(f, "{string}") + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/mod.rs new file mode 100644 index 00000000..2b3ac3af --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/mod.rs @@ -0,0 +1,41 @@ +//! +//! The lexical token literal lexeme. +//! + +pub mod boolean; +pub mod hex; +pub mod integer; +pub mod string; + +use std::fmt; + +use self::boolean::Boolean; +use self::hex::Hex; +use self::integer::Integer; +use self::string::String; + +/// +/// The lexical literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Literal { + /// A boolean literal, like `true`, or `false`. + Boolean(Boolean), + /// An integer literal, like `42`, or `0xff`. + Integer(Integer), + /// A string literal, like `"message"`. + String(String), + /// A hex literal, like `hex"1234"`. + Hex(Hex), +} + +impl fmt::Display for Literal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Boolean(inner) => write!(f, "{inner}"), + Self::Integer(inner) => write!(f, "{inner}"), + Self::String(inner) => write!(f, "{inner}"), + Self::Hex(inner) => write!(f, "{inner}"), + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/string.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/string.rs new file mode 100644 index 00000000..987431ae --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/literal/string.rs @@ -0,0 +1,36 @@ +//! +//! The lexical token string literal lexeme. +//! + +use std::fmt; + +/// +/// The lexical string literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct String { + /// The inner string contents. + pub inner: std::string::String, +} + +impl String { + /// + /// Creates a string literal value. + /// + pub fn new(inner: std::string::String) -> Self { + Self { inner } + } +} + +#[allow(clippy::from_over_into)] +impl Into for String { + fn into(self) -> std::string::String { + self.inner + } +} + +impl fmt::Display for String { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/mod.rs new file mode 100644 index 00000000..5aac8e78 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/mod.rs @@ -0,0 +1,45 @@ +//! +//! The lexical token lexeme. +//! + +pub mod comment; +pub mod identifier; +pub mod keyword; +pub mod literal; +pub mod symbol; + +use std::fmt; + +use self::identifier::Identifier; +use self::keyword::Keyword; +use self::literal::Literal; +use self::symbol::Symbol; + +/// +/// The smallest logical piece of the source code. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Lexeme { + /// A keyword, like `library` or `gas`. + Keyword(Keyword), + /// An identifier, like `f` or `data`. + Identifier(Identifier), + /// A literal, like `true`, `42`, or `"message"`. + Literal(Literal), + /// A symbol, like `(` or `->`. + Symbol(Symbol), + /// An end of file, which is returned by the token stream when it reaches the end of a file. + Eof, +} + +impl fmt::Display for Lexeme { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Keyword(inner) => write!(f, "{inner}"), + Self::Identifier(inner) => write!(f, "{inner}"), + Self::Literal(inner) => write!(f, "{inner}"), + Self::Symbol(inner) => write!(f, "{inner}"), + Self::Eof => write!(f, "EOF"), + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/symbol.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/symbol.rs new file mode 100644 index 00000000..f9632b2a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/lexeme/symbol.rs @@ -0,0 +1,52 @@ +//! +//! The lexical token symbol lexeme. +//! + +use std::fmt; + +/// +/// The minimal logical character group, which is usually a delimiter, operator, or special symbol. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Symbol { + /// The ( character + ParenthesisLeft, + /// The ) character + ParenthesisRight, + /// The [ character + BracketSquareLeft, + /// The ] character + BracketSquareRight, + /// The < character + Lesser, + /// The > character + Greater, + /// The : character + Colon, + /// The , character + Comma, + /// The ~ character + Tilde, + /// The -> character + Arrow, + /// The # character + Number, +} + +impl fmt::Display for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ParenthesisLeft => write!(f, "("), + Self::ParenthesisRight => write!(f, ")"), + Self::BracketSquareLeft => write!(f, "["), + Self::BracketSquareRight => write!(f, "]"), + Self::Lesser => write!(f, "<"), + Self::Greater => write!(f, ">"), + Self::Colon => write!(f, ":"), + Self::Comma => write!(f, ","), + Self::Tilde => write!(f, "~"), + Self::Arrow => write!(f, "->"), + Self::Number => write!(f, "#"), + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/location/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/location/mod.rs new file mode 100644 index 00000000..d526cace --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/location/mod.rs @@ -0,0 +1,81 @@ +//! +//! The lexical token location. +//! + +use std::fmt; + +/// +/// The token location in the source code file. +/// +#[derive(Debug, Default, Clone, Copy, Eq)] +pub struct Location { + /// The line number, starting from 1. + pub line: usize, + /// The column number, starting from 1. + pub column: usize, +} + +impl Location { + /// + /// Creates a location with a file identifier. + /// The file identifier can be used to get its contents from the global index. + /// + pub fn new() -> Self { + Self { line: 1, column: 1 } + } + + /// + /// Creates a location by shifting the original one down by `lines` and + /// setting the column to `column`. If the `lines` equals zero, shift the original column by `size`. + /// + pub fn shifted_down(&self, lines: usize, column: usize, size: usize) -> Self { + if lines == 0 { + Self { + line: self.line, + column: self.column + size, + } + } else { + Self { + line: self.line + lines, + column, + } + } + } + + /// + /// Creates a location by shifting the original one rightward by `columns`. + /// + pub fn shifted_right(&self, columns: usize) -> Self { + Self { + line: self.line, + column: self.column + columns, + } + } + + /// + /// Creates a location for testing purposes. + /// + /// If the `file_index` feature is enabled, fetches the current file index + /// from the global storage. + /// + pub fn test(line: usize, column: usize) -> Self { + Self { line, column } + } +} + +impl PartialEq for Location { + fn eq(&self, other: &Self) -> bool { + self.line == other.line && self.column == other.column + } +} + +impl fmt::Display for Location { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match (self.line, self.column) { + (0, 0) => write!(f, ""), + (line, column) => { + write!(f, "{line}:{column}") + } + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/lexical/token/mod.rs b/solidity_adapter/src/test/function_call/parser/lexical/token/mod.rs new file mode 100644 index 00000000..95f04ea1 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/lexical/token/mod.rs @@ -0,0 +1,37 @@ +//! +//! The lexical token. +//! + +pub mod lexeme; +pub mod location; + +use std::fmt; + +use self::lexeme::Lexeme; +use self::location::Location; + +/// +/// The lexical token, which represents the smallest logical piece of the source code. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Token { + /// The inner lexeme with the token data. + pub lexeme: Lexeme, + /// The token location in the source code. + pub location: Location, +} + +impl Token { + /// + /// Creates a new token with a known location. + /// + pub fn new(lexeme: Lexeme, location: Location) -> Self { + Self { lexeme, location } + } +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {}", self.location, self.lexeme) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/mod.rs b/solidity_adapter/src/test/function_call/parser/mod.rs new file mode 100644 index 00000000..cfd70c0a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/mod.rs @@ -0,0 +1,32 @@ +//! +//! The Solidity tests metadata parser. +//! + +mod lexical; +mod syntax; + +pub use lexical::BooleanLiteral as LexicalBooleanLiteral; +pub use lexical::Error as LexicalError; +pub use lexical::HexLiteral as LexicalHexLiteral; +pub use lexical::IntegerLiteral as LexicalIntegerLiteral; +pub use lexical::StringLiteral as LexicalStringLiteral; +pub use syntax::Alignment; +pub use syntax::BooleanLiteral; +pub use syntax::Call; +pub use syntax::CallVariant; +pub use syntax::Error as SyntaxError; +pub use syntax::Event; +pub use syntax::EventVariant; +pub use syntax::Gas; +pub use syntax::GasVariant; +pub use syntax::HexLiteral; +pub use syntax::Identifier; +pub use syntax::IntegerLiteral; +pub use syntax::Literal; +pub use syntax::Parser; +pub use syntax::ParsingError; +pub use syntax::StringLiteral; +pub use syntax::Type; +pub use syntax::TypeVariant; +pub use syntax::Unit; +pub use syntax::Value; diff --git a/solidity_adapter/src/test/function_call/parser/syntax/error.rs b/solidity_adapter/src/test/function_call/parser/syntax/error.rs new file mode 100644 index 00000000..9f599844 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/error.rs @@ -0,0 +1,69 @@ +//! +//! The syntax parser error. +//! + +use crate::test::function_call::parser::lexical::Error as LexicalError; +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The syntax parser error. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Error { + /// The invalid lexeme location. + pub location: Location, + /// The list of the expected lexemes. + pub expected: String, + /// The invalid lexeme. + pub found: Lexeme, +} + +/// +/// The lexical and syntax errors wrapper. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum ParsingError { + /// The lexical analysis error. + Lexical(LexicalError), + /// The syntax analysis error. + Syntax(Error), +} + +impl From for ParsingError { + fn from(inner: LexicalError) -> Self { + Self::Lexical(inner) + } +} + +impl From for ParsingError { + fn from(inner: Error) -> Self { + Self::Syntax(inner) + } +} + +impl Error { + /// + /// A shortcut constructor. + /// + pub fn new(location: Location, expected: Vec<&'static str>, found: Lexeme) -> Self { + Self { + location, + expected: Self::format_one_of(expected.as_slice()), + found, + } + } + + /// + /// Converts a group of lexemes into a comma-separated list. + /// + /// E.g. ["ether", "wei"] turns into `ether`, `wei`. + /// + pub fn format_one_of(lexemes: &[&'static str]) -> String { + lexemes + .iter() + .map(|lexeme| format!("`{lexeme}`")) + .collect::>() + .join(", ") + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/mod.rs new file mode 100644 index 00000000..1795b154 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/mod.rs @@ -0,0 +1,28 @@ +//! +//! The Solidity tests metadata syntax parser. +//! + +mod error; +mod parser; +mod tree; + +pub use self::error::Error; +pub use self::error::ParsingError; +pub use self::parser::Parser; +pub use self::tree::call::variant::Variant as CallVariant; +pub use self::tree::call::Call; +pub use self::tree::event::variant::Variant as EventVariant; +pub use self::tree::event::Event; +pub use self::tree::gas::variant::Variant as GasVariant; +pub use self::tree::gas::Gas; +pub use self::tree::identifier::Identifier; +pub use self::tree::literal::alignment::Alignment; +pub use self::tree::literal::boolean::Literal as BooleanLiteral; +pub use self::tree::literal::hex::Literal as HexLiteral; +pub use self::tree::literal::integer::Literal as IntegerLiteral; +pub use self::tree::literal::string::Literal as StringLiteral; +pub use self::tree::literal::Literal; +pub use self::tree::r#type::variant::Variant as TypeVariant; +pub use self::tree::r#type::Type; +pub use self::tree::value::unit::Unit; +pub use self::tree::value::Value; diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/call.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/call.rs new file mode 100644 index 00000000..c55fd20a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/call.rs @@ -0,0 +1,747 @@ +//! +//! The call parser. +//! + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; +use crate::test::function_call::parser::lexical::Symbol; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::Error as SyntaxError; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser; +use crate::test::function_call::parser::syntax::parser::event::Parser as EventParser; +use crate::test::function_call::parser::syntax::parser::gas::Parser as GasParser; +use crate::test::function_call::parser::syntax::parser::literal::Parser as LiteralParser; +use crate::test::function_call::parser::syntax::parser::r#type::Parser as TypeParser; +use crate::test::function_call::parser::syntax::parser::value::Parser as ValueParser; +use crate::test::function_call::parser::syntax::tree::call::builder::Builder as CallBuilder; +use crate::test::function_call::parser::syntax::tree::call::Call; +use crate::test::function_call::parser::syntax::tree::identifier::Identifier; + +/// +/// The parser state. +/// +#[derive(Debug, Clone, Copy)] +pub enum State { + /// The initial state. + Start, + /// The `library` has been parsed so far. + Library, + /// The `library:` has been parsed so far. + ColonLibrary, + /// The `library: "{source file name}"` has been parsed so far. + Source, + /// The `library: "{source file name}":` has been parsed so far. + ColonSource, + /// The `{function name}` has been parsed so far. + FunctionName, + /// The `{function name}(` has been parsed so far. + ParenthesisLeft, + /// The `{function name}({type}` has been parsed so far. + Type, + /// The `{function name}({type},` has been parsed so far. + CommaType, + /// The `{function name}{types}` has been parsed so far. + Signature, + /// The `{signature}{value option}` has been parsed so far. + Value, + /// The `{signature}[value option]:` has been parsed so far. + Colon, + /// The `{signature}[value option]:{literal}` has been parsed so far. + Input, + /// The `{signature}[value option]:{literal},` has been parsed so far. + CommaInput, + /// The `...->` has been parsed so far. + Arrow, + /// The `...->{literal}` or `...->{failure}` has been parsed so far. + Expected, + /// The `...->{literal},` or `...->{failure},` has been parsed so far. + CommaExpected, + /// The `...{event}: ` has been parsed so far. + Event, + /// The `...{gas option}` has been parsed so far. + Gas, +} + +impl Default for State { + fn default() -> Self { + Self::Start + } +} + +/// +/// The call parser. +/// +#[derive(Default)] +pub struct Parser { + /// The parser state. + state: State, + /// The builder of the parsed value. + builder: CallBuilder, + /// The token returned from a subparser. + next: Option, +} + +impl Parser { + /// + /// Parses a even. + /// + /// ' + /// ~ emit Verified(string): 0x20, 0x16, "Successfully verified." + /// ~ emit + /// ' + /// + pub fn parse( + mut self, + stream: Rc>, + initial: Option, + ) -> Result<(Call, Option), ParsingError> { + self.next = initial; + + loop { + match self.state { + State::Start => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Keyword(Keyword::Library), + location, + } => { + self.builder.set_location(location); + self.state = State::Library; + } + Token { + lexeme: Lexeme::Identifier(identifier), + location, + } => { + self.builder.set_location(location); + self.builder + .set_call(Identifier::new(location, identifier.inner)); + self.state = State::FunctionName; + } + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft), + location, + } => { + self.builder.set_location(location); + self.builder.set_is_types(); + self.state = State::ParenthesisLeft; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["library", "{identifier}", "("], + lexeme, + ) + .into()) + } + }, + State::Library => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.state = State::ColonLibrary; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec![":"], lexeme).into()) + } + }, + State::ColonLibrary => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Identifier(identifier), + location, + } => { + self.builder + .set_library(Identifier::new(location, identifier.inner)); + return Ok((self.builder.finish(), None)); + } + Token { + lexeme: Lexeme::Literal(LexicalLiteral::String(string)), + .. + } => { + self.builder.set_library_source(string.inner); + self.state = State::Source; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["{identifier}", "{string literal}"], + lexeme, + ) + .into()) + } + } + } + State::Source => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.state = State::ColonSource; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec![":"], lexeme).into()) + } + }, + State::ColonSource => { + return match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Identifier(identifier), + location, + } => { + self.builder + .set_library(Identifier::new(location, identifier.inner)); + Ok((self.builder.finish(), None)) + } + Token { lexeme, location } => { + Err(SyntaxError::new(location, vec!["{identifier}"], lexeme).into()) + } + }; + } + State::FunctionName => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => { + let (value, next) = + ValueParser::default().parse(stream.clone(), None)?; + self.builder.set_value(value); + self.next = next; + self.state = State::Value; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.builder.set_is_input(); + self.state = State::Colon; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Arrow), + .. + } => { + self.builder.set_is_expected(); + self.state = State::Arrow; + } + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft), + .. + } => { + self.builder.set_is_types(); + self.state = State::ParenthesisLeft; + } + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + } + } + State::ParenthesisLeft => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + .. + } => { + self.state = State::Signature; + } + token => { + let (r#type, next) = + TypeParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_types(r#type); + self.next = next; + self.state = State::Type; + } + } + } + State::Type => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + .. + } => { + self.state = State::Signature; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => { + self.state = State::CommaType; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec![")", ","], lexeme).into()) + } + }, + State::CommaType => { + let (r#type, next) = TypeParser::default().parse(stream.clone(), self.next)?; + self.builder.push_types(r#type); + self.next = next; + self.state = State::Type; + } + State::Signature => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => { + let (value, next) = ValueParser::default().parse(stream.clone(), None)?; + self.builder.set_value(value); + self.next = next; + self.state = State::Value; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.builder.set_is_input(); + self.state = State::Colon; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Arrow), + .. + } => { + self.builder.set_is_expected(); + self.state = State::Arrow; + } + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + State::Value => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.builder.set_is_input(); + self.state = State::Colon; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Arrow), + .. + } => { + self.builder.set_is_expected(); + self.state = State::Arrow; + } + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + State::Colon => match parser::take_or_next(self.next.take(), stream.clone())? { + token @ Token { + lexeme: + Lexeme::Keyword(Keyword::Right) + | Lexeme::Keyword(Keyword::Left) + | Lexeme::Literal(_), + .. + } => { + let (input, next) = + LiteralParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_input(input); + self.next = next; + self.state = State::Input; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Arrow), + .. + } => { + self.builder.set_is_expected(); + self.state = State::Arrow; + } + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + State::Input => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => { + self.state = State::CommaInput; + } + Token { + lexeme: Lexeme::Symbol(Symbol::Arrow), + .. + } => { + self.builder.set_is_expected(); + self.state = State::Arrow; + } + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + State::CommaInput => { + let (input, next) = + LiteralParser::default().parse(stream.clone(), self.next)?; + self.builder.push_input(input); + self.next = next; + self.state = State::Input; + } + State::Arrow => match parser::take_or_next(self.next.take(), stream.clone())? { + token @ Token { + lexeme: + Lexeme::Keyword(Keyword::Right) + | Lexeme::Keyword(Keyword::Left) + | Lexeme::Literal(_), + .. + } => { + let (expected, next) = + LiteralParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_expected(expected); + self.next = next; + self.state = State::Expected; + } + Token { + lexeme: Lexeme::Keyword(Keyword::Failure), + .. + } => { + self.builder.set_failure(); + self.state = State::Expected; + } + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + State::Expected => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => { + self.state = State::CommaExpected; + } + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + State::CommaExpected => { + let (expected, next) = + LiteralParser::default().parse(stream.clone(), self.next)?; + self.builder.push_expected(expected); + self.next = next; + self.state = State::Expected; + } + State::Event => match parser::take_or_next(self.next.take(), stream.clone())? { + token @ Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + .. + } => { + let (event, next) = + EventParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_event(event); + self.next = next; + self.state = State::Event; + } + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + State::Gas => match parser::take_or_next(self.next.take(), stream.clone())? { + token @ Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + .. + } => { + let (gas, next) = + GasParser::default().parse(stream.clone(), Some(token))?; + self.builder.push_gas(gas); + self.next = next; + self.state = State::Gas; + } + token => return Ok((self.builder.finish(), Some(token))), + }, + } + } + } +} + +#[cfg(test)] +mod tests { + + use crate::test::function_call::parser::lexical::Lexeme; + + use crate::test::function_call::parser::lexical::Location; + use crate::test::function_call::parser::lexical::Symbol; + use crate::test::function_call::parser::lexical::Token; + use crate::test::function_call::parser::lexical::TokenStream; + + use super::Parser; + use crate::test::function_call::parser::syntax::error::Error as SyntaxError; + use crate::test::function_call::parser::syntax::error::ParsingError; + use crate::test::function_call::parser::syntax::tree::call::variant::Variant as CallVariant; + use crate::test::function_call::parser::syntax::tree::call::Call; + + use crate::test::function_call::parser::syntax::tree::event::variant::Variant as EventVariant; + use crate::test::function_call::parser::syntax::tree::event::Event; + use crate::test::function_call::parser::syntax::tree::gas::variant::Variant as GasVariant; + use crate::test::function_call::parser::syntax::tree::gas::Gas; + + use crate::test::function_call::parser::syntax::tree::r#type::variant::Variant as TypeVariant; + use crate::test::function_call::parser::syntax::tree::value::unit::Unit; + use crate::test::function_call::parser::syntax::tree::value::Value; + use crate::test::function_call::parser::syntax::Identifier; + + use crate::test::function_call::parser::syntax::Type; + + #[test] + fn ok_library() { + let input = r#"library: L"#; + let expected = Ok(( + Call::new( + Location::test(1, 1), + CallVariant::library(Identifier::new(Location::test(1, 10), "L".to_owned()), None), + ), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn ok_library_with_source() { + let input = r#"library: "a.sol":A"#; + let expected = Ok(( + Call::new( + Location::test(1, 1), + CallVariant::library( + Identifier::new(Location::test(1, 18), "A".to_owned()), + Some(String::from("a.sol")), + ), + ), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn ok() { + let input = r#"f(bytes2), 10 ether: -> FAILURE +gas legacy: 100("#; + let expected = Ok(( + Call::new( + Location::test(1, 1), + CallVariant::call( + Some(Identifier::new(Location::test(1, 1), "f".to_owned())), + Some(vec![Type::new( + Location::test(1, 3), + TypeVariant::bytes(Some(2)), + )]), + Some(Value::new( + Location::test(1, 12), + Unit::Ether, + "10".to_owned(), + )), + Some(Vec::new()), + Some(Vec::new()), + true, + Vec::new(), + vec![Gas::new( + Location::test(2, 1), + GasVariant::Legacy, + "100".to_owned(), + )], + ), + ), + Some(Token::new( + Lexeme::Symbol(Symbol::ParenthesisLeft), + Location::test(2, 16), + )), + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn ok_constructor() { + let input = r#"constructor -> +~ emit E(uint256)"#; + let expected = Ok(( + Call::new( + Location::test(1, 1), + CallVariant::call( + Some(Identifier::new( + Location::test(1, 1), + "constructor".to_owned(), + )), + None, + None, + None, + Some(Vec::new()), + false, + vec![Event::new( + Location::test(2, 1), + EventVariant::signature( + Identifier::new(Location::test(2, 8), "E".to_owned()), + vec![Type::new( + Location::test(2, 10), + TypeVariant::integer_unsigned(256), + )], + ), + None, + None, + )], + Vec::new(), + ), + ), + Some(Token::new(Lexeme::Eof, Location::test(2, 18))), + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn expected_library_or_call() { + let input = r#"~"#; + let expected = Err(ParsingError::Syntax(SyntaxError::new( + Location::test(1, 1), + vec!["library", "{identifier}", "("], + Lexeme::Symbol(Symbol::Tilde), + ))); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/event.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/event.rs new file mode 100644 index 00000000..96e7e24e --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/event.rs @@ -0,0 +1,430 @@ +//! +//! The event parser. +//! + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; +use crate::test::function_call::parser::lexical::Symbol; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::Error as SyntaxError; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser; +use crate::test::function_call::parser::syntax::parser::literal::Parser as LiteralParser; +use crate::test::function_call::parser::syntax::parser::r#type::Parser as TypeParser; +use crate::test::function_call::parser::syntax::tree::event::builder::Builder as EventBuilder; +use crate::test::function_call::parser::syntax::tree::event::literal::EventLiteral; +use crate::test::function_call::parser::syntax::tree::event::Event; +use crate::test::function_call::parser::syntax::tree::identifier::Identifier; + +/// +/// The parser state. +/// +#[derive(Debug, Clone, Copy)] +pub enum State { + /// The initial state. + Start, + /// The `~` has been parsed so far. + Emit, + /// The `~ emit` has been parsed so far. + IdentifierOrLesser, + /// The `~ emit {identifier}` has been parsed so far. + ParenthesisLeft, + /// The `~ emit {identifier}(` has been parsed so far. + TypeOrParenthesisRight, + /// The `~ emit {identifier}({type}` has been parsed so far. + CommaOrParenthesisRight, + /// The `~ emit {identifier}({type},` has been parsed so far. + Type, + /// The `~ emit <` has been parsed so far. + Anonymous, + /// The `~ emit ` has been parsed so far. + ColonFromOrEnd, + /// The `~ emit {identifier}({types}) from` or `~emit from` has been parsed so far. + Address, + /// The `~ emit {identifier}({types}) from {address}` or `~emit from {address}` has been parsed so far. + ColonOrEnd, + /// The `~ emit {identifier}({types}):` or `~emit :` has been parsed so far. + LiteralOrEnd, + /// The `...:#` has been parsed so far. + IndexedLiteral, + /// The `...:{literal}` has been parsed so far. + CommaOrEnd, + /// The `...:{literal},` has been parsed so far. + Literal, +} + +impl Default for State { + fn default() -> Self { + Self::Start + } +} + +/// +/// The event parser. +/// +#[derive(Default)] +pub struct Parser { + /// The parser state. + state: State, + /// The builder of the parsed value. + builder: EventBuilder, + /// The token returned from a subparser. + next: Option, +} + +impl Parser { + /// + /// Parses a even. + /// + /// ' + /// ~ emit Verified(string): 0x20, 0x16, "Successfully verified." + /// ~ emit + /// ' + /// + pub fn parse( + mut self, + stream: Rc>, + initial: Option, + ) -> Result<(Event, Option), ParsingError> { + self.next = initial; + + loop { + match self.state { + State::Start => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Tilde), + location, + } => { + self.builder.set_location(location); + self.state = State::Emit; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["~"], lexeme).into()); + } + }, + State::Emit => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Keyword(Keyword::Emit), + .. + } => { + self.state = State::IdentifierOrLesser; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["emit"], lexeme).into()); + } + }, + State::IdentifierOrLesser => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Lesser), + .. + } => { + self.state = State::Anonymous; + } + Token { + lexeme: Lexeme::Identifier(identifier), + location, + } => { + self.builder + .set_identifier(Identifier::new(location, identifier.inner)); + self.state = State::ParenthesisLeft; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["<", "{identifier}"], + lexeme, + ) + .into()); + } + } + } + State::ParenthesisLeft => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft), + .. + } => { + self.state = State::TypeOrParenthesisRight; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["("], lexeme).into()); + } + } + } + State::TypeOrParenthesisRight => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + .. + } => { + self.state = State::ColonFromOrEnd; + } + token => { + let (r#type, next) = + TypeParser::default().parse(stream.clone(), Some(token))?; + self.next = next; + self.builder.push_type(r#type); + self.state = State::CommaOrParenthesisRight; + } + } + } + State::CommaOrParenthesisRight => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => { + self.state = State::Type; + } + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + .. + } => { + self.state = State::ColonFromOrEnd; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec![",", ")"], lexeme).into()); + } + } + } + State::Type => { + let (r#type, next) = + TypeParser::default().parse(stream.clone(), self.next.take())?; + self.next = next; + self.builder.push_type(r#type); + self.state = State::CommaOrParenthesisRight; + } + State::Anonymous => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Keyword(Keyword::Anonymous), + .. + } => { + self.state = State::Greater; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["anonymous"], lexeme).into()); + } + }, + State::Greater => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Greater), + .. + } => { + self.state = State::ColonFromOrEnd; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec![">"], lexeme).into()); + } + }, + State::ColonFromOrEnd => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.builder.set_is_expected(); + self.state = State::LiteralOrEnd; + } + Token { + lexeme: Lexeme::Keyword(Keyword::From), + .. + } => { + self.state = State::Address; + } + token => return Ok((self.builder.finish(), Some(token))), + } + } + State::Address => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: + Lexeme::Literal(LexicalLiteral::Integer( + LexicalIntegerLiteral::Hexadecimal(hexadecimal_integer), + )), + .. + } => { + self.builder.set_address(hexadecimal_integer); + self.state = State::ColonOrEnd; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["{hexadecimal integer literal}"], + lexeme, + ) + .into()); + } + }, + State::ColonOrEnd => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.builder.set_is_expected(); + self.state = State::LiteralOrEnd; + } + token => return Ok((self.builder.finish(), Some(token))), + } + } + State::LiteralOrEnd => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Number), + .. + } => { + self.state = State::IndexedLiteral; + } + token => { + if let Ok((literal, next)) = + LiteralParser::default().parse(stream.clone(), Some(token)) + { + self.builder + .push_expected(EventLiteral::new(literal, false)); + self.next = next; + self.state = State::CommaOrEnd; + } else { + return Ok((self.builder.finish(), self.next)); + } + } + } + } + State::IndexedLiteral => { + let (r#literal, next) = + LiteralParser::default().parse(stream.clone(), self.next.take())?; + self.next = next; + self.builder + .push_expected(EventLiteral::new(r#literal, true)); + self.state = State::CommaOrEnd; + } + State::CommaOrEnd => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => { + self.state = State::Literal; + } + token => return Ok((self.builder.finish(), Some(token))), + } + } + State::Literal => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Number), + .. + } => { + self.state = State::IndexedLiteral; + } + token => { + let (r#literal, next) = + LiteralParser::default().parse(stream.clone(), Some(token))?; + self.next = next; + self.builder + .push_expected(EventLiteral::new(r#literal, false)); + self.state = State::CommaOrEnd; + } + }, + } + } + } +} + +#[cfg(test)] +mod tests { + + use crate::test::function_call::parser::lexical::Identifier as LexicalIdentifier; + + use crate::test::function_call::parser::lexical::Lexeme; + + use crate::test::function_call::parser::lexical::Location; + + use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; + use crate::test::function_call::parser::lexical::Token; + use crate::test::function_call::parser::lexical::TokenStream; + + use super::Parser; + use crate::test::function_call::parser::syntax::error::Error as SyntaxError; + use crate::test::function_call::parser::syntax::error::ParsingError; + use crate::test::function_call::parser::syntax::tree::event::literal::EventLiteral; + use crate::test::function_call::parser::syntax::tree::event::variant::Variant; + use crate::test::function_call::parser::syntax::tree::event::Event; + use crate::test::function_call::parser::syntax::tree::literal::alignment::Alignment; + use crate::test::function_call::parser::syntax::tree::literal::Literal; + use crate::test::function_call::parser::syntax::tree::r#type::variant::Variant as TypeVariant; + use crate::test::function_call::parser::syntax::Identifier; + use crate::test::function_call::parser::syntax::IntegerLiteral; + use crate::test::function_call::parser::syntax::Type; + + #[test] + fn ok() { + let input = r#"~ emit Verified(string) from 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0: #0x20, -10"#; + let expected = Ok(( + Event::new( + Location::test(1, 1), + Variant::Signature { + identifier: Identifier::new(Location::test(1, 8), "Verified".to_owned()), + types: vec![Type::new(Location::test(1, 17), TypeVariant::String)], + }, + Some("f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0".to_owned()), + Some(vec![ + EventLiteral::new( + Literal::Integer(IntegerLiteral::new( + Location::test(1, 75), + LexicalIntegerLiteral::Hexadecimal("20".to_owned()), + Alignment::Default, + )), + true, + ), + EventLiteral::new( + Literal::Integer(IntegerLiteral::new( + Location::test(1, 81), + LexicalIntegerLiteral::new_decimal("10".to_owned(), true), + Alignment::Default, + )), + false, + ), + ]), + ), + Some(Token::new(Lexeme::Eof, Location::test(1, 84))), + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn ok_anonymous() { + let input = r#"~ emit "#; + let expected = Ok(( + Event::new(Location::test(1, 1), Variant::Anonymous, None, None), + Some(Token::new(Lexeme::Eof, Location::test(1, 19))), + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn expected_emit() { + let input = r#"~ e_mit "#; + let expected = Err(ParsingError::Syntax(SyntaxError::new( + Location::test(1, 3), + vec!["emit"], + Lexeme::Identifier(LexicalIdentifier::new("e_mit".to_owned())), + ))); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/gas.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/gas.rs new file mode 100644 index 00000000..98441b9f --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/gas.rs @@ -0,0 +1,188 @@ +//! +//! The gas option parser. +//! + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; +use crate::test::function_call::parser::lexical::Symbol; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::Error as SyntaxError; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser; +use crate::test::function_call::parser::syntax::tree::gas::builder::Builder as GasBuilder; +use crate::test::function_call::parser::syntax::tree::gas::Gas; + +/// +/// The parser state. +/// +#[derive(Debug, Clone, Copy)] +pub enum State { + /// The initial state. + Start, + /// The `gas` has been parsed so far. + Variant, + /// The `gas {variant}` has been parsed so far. + Colon, + /// The `gas {variant}:` has been parsed so far. + Value, +} + +impl Default for State { + fn default() -> Self { + Self::Start + } +} + +/// +/// The event parser. +/// +#[derive(Default)] +pub struct Parser { + /// The parser state. + state: State, + /// The builder of the parsed value. + builder: GasBuilder, + /// The token returned from a subparser. + next: Option, +} + +impl Parser { + /// + /// Parses a gas option. + /// + /// ' + /// gas legacy: 10 + /// ' + /// + pub fn parse( + mut self, + stream: Rc>, + initial: Option, + ) -> Result<(Gas, Option), ParsingError> { + self.next = initial; + + loop { + match self.state { + State::Start => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Keyword(Keyword::Gas), + location, + } => { + self.builder.set_location(location); + self.state = State::Variant; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["gas"], lexeme).into()); + } + }, + State::Variant => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: + Lexeme::Keyword( + keyword @ Keyword::LegacyOptimized + | keyword @ Keyword::Legacy + | keyword @ Keyword::IrOptimized + | keyword @ Keyword::Ir, + ), + .. + } => { + self.builder.set_keyword(keyword); + self.state = State::Colon; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["legacyOptimized", "legacy", "irOptimized", "ir"], + lexeme, + ) + .into()); + } + }, + State::Colon => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Colon), + .. + } => { + self.state = State::Value; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec![":"], lexeme).into()); + } + }, + State::Value => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: + Lexeme::Literal(LexicalLiteral::Integer(LexicalIntegerLiteral::Decimal { + inner: decimal_integer, + negative: false, + })), + .. + } => { + self.builder.set_value(decimal_integer); + return Ok((self.builder.finish(), None)); + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["{not negative decimal integer literal}"], + lexeme, + ) + .into()); + } + }, + } + } + } +} + +#[cfg(test)] +mod tests { + + use crate::test::function_call::parser::lexical::Keyword; + use crate::test::function_call::parser::lexical::Lexeme; + + use crate::test::function_call::parser::lexical::Location; + + use crate::test::function_call::parser::lexical::TokenStream; + + use super::Parser; + use crate::test::function_call::parser::syntax::error::Error as SyntaxError; + use crate::test::function_call::parser::syntax::error::ParsingError; + use crate::test::function_call::parser::syntax::tree::gas::variant::Variant; + use crate::test::function_call::parser::syntax::tree::gas::Gas; + + #[test] + fn ok() { + let input = r#"gas ir: 202020"#; + + let expected = Ok(( + Gas::new(Location::test(1, 1), Variant::ir(), "202020".to_owned()), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn error_expected_gas_variant() { + let input = r#"gas gas: 10"#; + + let expected = Err(ParsingError::from(SyntaxError::new( + Location::test(1, 5), + vec!["legacyOptimized", "legacy", "irOptimized", "ir"], + Lexeme::Keyword(Keyword::Gas), + ))); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/literal.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/literal.rs new file mode 100644 index 00000000..d407d022 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/literal.rs @@ -0,0 +1,218 @@ +//! +//! The literal parser. +//! + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Symbol; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::Error as SyntaxError; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser; +use crate::test::function_call::parser::syntax::tree::literal::alignment::Alignment; +use crate::test::function_call::parser::syntax::tree::literal::builder::Builder as LiteralBuilder; +use crate::test::function_call::parser::syntax::tree::literal::Literal; + +/// +/// The parser state. +/// +#[derive(Debug, Clone, Copy)] +pub enum State { + /// The initial state. + Start, + /// The `{alignment}` has been parsed so far. + Alignment, + /// The `{alignment}(` has been parsed so far. + ParenthesisLeft, + /// The `{alignment}([-]{literal}` has been parsed so far. + Literal, +} + +impl Default for State { + fn default() -> Self { + Self::Start + } +} + +/// +/// The literal parser. +/// +#[derive(Default)] +pub struct Parser { + /// The parser state. + state: State, + /// The builder of the parsed value. + builder: LiteralBuilder, + /// The token returned from a subparser. + next: Option, +} + +impl Parser { + /// + /// Parses a literal. + /// + /// ' + /// -1234 + /// left(0x12) + /// hex"1234" + /// "abc" + /// ' + /// + pub fn parse( + mut self, + stream: Rc>, + initial: Option, + ) -> Result<(Literal, Option), ParsingError> { + self.next = initial; + + loop { + match self.state { + State::Start => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Literal(literal), + location, + } => { + self.builder.set_location(location); + self.builder.set_literal(literal); + return Ok((self.builder.finish(), None)); + } + Token { + lexeme: Lexeme::Keyword(Keyword::Left), + location, + } => { + self.builder.set_location(location); + self.builder.set_alignment(Alignment::left()); + self.state = State::Alignment; + } + Token { + lexeme: Lexeme::Keyword(Keyword::Right), + location, + } => { + self.builder.set_location(location); + self.builder.set_alignment(Alignment::right()); + self.state = State::Alignment; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["{literal}", "left", "right"], + lexeme, + ) + .into()); + } + }, + State::Alignment => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft), + .. + } => { + self.state = State::ParenthesisLeft; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["("], lexeme).into()); + } + }, + State::ParenthesisLeft => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Literal(literal), + .. + } => { + self.builder.set_literal(literal); + self.state = State::Literal; + } + Token { lexeme, location } => { + return Err( + SyntaxError::new(location, vec!["{literal}"], lexeme).into() + ); + } + } + } + State::Literal => { + return match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + .. + } => Ok((self.builder.finish(), None)), + Token { lexeme, location } => { + Err(SyntaxError::new(location, vec![")"], lexeme).into()) + } + }; + } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::test::function_call::parser::lexical::HexLiteral as LexicalHexLiteral; + use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; + use crate::test::function_call::parser::lexical::Lexeme; + use crate::test::function_call::parser::lexical::Location; + use crate::test::function_call::parser::lexical::Symbol; + use crate::test::function_call::parser::lexical::TokenStream; + + use super::Parser; + use crate::test::function_call::parser::syntax::error::Error as SyntaxError; + use crate::test::function_call::parser::syntax::error::ParsingError; + use crate::test::function_call::parser::syntax::tree::literal::alignment::Alignment; + use crate::test::function_call::parser::syntax::tree::literal::hex::Literal as HexLiteral; + use crate::test::function_call::parser::syntax::tree::literal::integer::Literal as IntegerLiteral; + use crate::test::function_call::parser::syntax::tree::literal::Literal; + + #[test] + fn ok_integer() { + let input = r#"-1234"#; + + let expected = Ok(( + Literal::Integer(IntegerLiteral::new( + Location::test(1, 1), + LexicalIntegerLiteral::new_decimal("1234".to_owned(), true), + Alignment::Default, + )), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn ok_hex() { + let input = r#"right(hex"1234")"#; + + let expected = Ok(( + Literal::Hex(HexLiteral::new( + Location::test(1, 1), + LexicalHexLiteral::new("1234".to_owned()), + Alignment::Right, + )), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn error_expected_parenthesis_left() { + let input = r#"left~"#; + + let expected = Err(ParsingError::from(SyntaxError::new( + Location::test(1, 5), + vec!["("], + Lexeme::Symbol(Symbol::Tilde), + ))); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/mod.rs new file mode 100644 index 00000000..dd8a8194 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/mod.rs @@ -0,0 +1,68 @@ +//! +//! The syntax parser. +//! + +pub mod call; +pub mod event; +pub mod gas; +pub mod literal; +pub mod r#type; +pub mod value; + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser::call::Parser as CallParser; +use crate::test::function_call::parser::syntax::tree::call::Call; + +/// +/// The calls parser. +/// +#[derive(Default)] +pub struct Parser { + /// The token returned from a subparser. + next: Option, +} + +impl Parser { + /// + /// Parses a function calls. + /// + pub fn parse(mut self, input: &str) -> Result, ParsingError> { + let stream = TokenStream::new(input).wrap(); + + let mut calls = Vec::new(); + loop { + match take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Eof, + .. + } => break, + token => { + let (call, next) = CallParser::default().parse(stream.clone(), Some(token))?; + self.next = next; + calls.push(call); + } + } + } + + Ok(calls) + } +} + +/// +/// Returns the `token` value if it is `Some(_)`, otherwise takes the next token from the `stream`. +/// +pub fn take_or_next( + mut token: Option, + stream: Rc>, +) -> Result { + match token.take() { + Some(token) => Ok(token), + None => Ok(stream.borrow_mut().next()?), + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/type/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/type/mod.rs new file mode 100644 index 00000000..d3c8788a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/type/mod.rs @@ -0,0 +1,267 @@ +//! +//! The type parser. +//! + +pub mod tuple; + +use std::cell::RefCell; +use std::rc::Rc; + +use self::tuple::Parser as TupleParser; +use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; +use crate::test::function_call::parser::lexical::Symbol; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::Error as SyntaxError; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser; +use crate::test::function_call::parser::syntax::tree::r#type::builder::Builder as TypeBuilder; +use crate::test::function_call::parser::syntax::tree::r#type::Type; + +/// +/// The parser state. +/// +#[derive(Debug, Clone, Copy)] +pub enum State { + /// The initial state. + Start, + /// The `{type}` has been parsed so far. + BracketSquareLeftOrEnd, + /// The `{type}[` has been parsed so far. + SizeOrBracketSquareRight, + /// The `{type}[{size}` has been parsed so far. + BracketSquareRight, +} + +impl Default for State { + fn default() -> Self { + Self::Start + } +} + +/// +/// The type parser. +/// +#[derive(Default)] +pub struct Parser { + /// The parser state. + state: State, + /// The builder of the parsed value. + builder: TypeBuilder, + /// The token returned from a subparser. + next: Option, +} + +impl Parser { + /// + /// Parses a type. + /// + /// 'bool' + /// 'uint8[16]' + /// '(uint8, uint256, bool)' + /// + pub fn parse( + mut self, + stream: Rc>, + initial: Option, + ) -> Result<(Type, Option), ParsingError> { + self.next = initial; + let mut tuple = None; + + loop { + match self.state { + State::Start => match parser::take_or_next(self.next.take(), stream.clone())? { + token @ Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft), + .. + } => { + let (value, next) = + TupleParser::default().parse(stream.clone(), Some(token))?; + tuple = Some(value); + self.next = next; + self.state = State::BracketSquareLeftOrEnd; + } + Token { + lexeme: + Lexeme::Keyword( + keyword @ Keyword::Bool + | keyword @ Keyword::String + | keyword @ Keyword::Address + | keyword @ Keyword::Function + | keyword @ Keyword::IntegerUnsigned { .. } + | keyword @ Keyword::IntegerSigned { .. } + | keyword @ Keyword::Bytes { .. }, + ), + location, + } => { + self.builder.set_location(location); + self.builder.set_keyword(keyword); + self.state = State::BracketSquareLeftOrEnd; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec![ + "(", "bool", "string", "address", "function", "uint{n}", "int{n}", + "bytes", "bytes{n}", + ], + lexeme, + ) + .into()) + } + }, + State::BracketSquareLeftOrEnd => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::BracketSquareLeft), + .. + } => { + let base_type = if let Some(tuple) = tuple.take() { + tuple + } else { + self.builder.finish() + }; + self.builder = TypeBuilder::default(); + self.builder.set_location(base_type.location); + self.builder.set_array_type(base_type); + self.state = State::SizeOrBracketSquareRight; + } + token => { + return Ok(( + tuple.unwrap_or_else(|| self.builder.finish()), + Some(token), + )); + } + } + } + State::SizeOrBracketSquareRight => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: + Lexeme::Literal(LexicalLiteral::Integer( + LexicalIntegerLiteral::Decimal { + inner: decimal_integer, + negative: false, + }, + )), + .. + } => { + self.builder.set_array_size(decimal_integer); + self.state = State::BracketSquareRight; + } + Token { + lexeme: Lexeme::Symbol(Symbol::BracketSquareRight), + .. + } => { + self.state = State::BracketSquareLeftOrEnd; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["{positive decimal integer literal}", "]"], + lexeme, + ) + .into()) + } + } + } + State::BracketSquareRight => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::BracketSquareRight), + .. + } => { + self.state = State::BracketSquareLeftOrEnd; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["]"], lexeme).into()) + } + } + } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; + + use crate::test::function_call::parser::lexical::Lexeme; + use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; + use crate::test::function_call::parser::lexical::Location; + + use crate::test::function_call::parser::lexical::Token; + use crate::test::function_call::parser::lexical::TokenStream; + + use super::Parser; + use crate::test::function_call::parser::syntax::error::Error as SyntaxError; + use crate::test::function_call::parser::syntax::error::ParsingError; + + use crate::test::function_call::parser::syntax::tree::r#type::variant::Variant as TypeVariant; + use crate::test::function_call::parser::syntax::tree::r#type::Type; + + #[test] + fn ok_integer() { + let input = r#"uint232"#; + + let expected = Ok(( + Type::new(Location::test(1, 1), TypeVariant::integer_unsigned(232)), + Some(Token::new(Lexeme::Eof, Location::test(1, 8))), + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn ok_two_dimensional_array() { + let input = r#"bytes[5][]"#; + + let expected = Ok(( + Type::new( + Location::test(1, 1), + TypeVariant::array( + Type::new( + Location::test(1, 1), + TypeVariant::array( + Type::new(Location::test(1, 1), TypeVariant::bytes(None)), + Some("5".to_owned()), + ), + ), + None, + ), + ), + Some(Token::new(Lexeme::Eof, Location::test(1, 11))), + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn error_expected_type() { + let input = r#"42"#; + + let expected = Err(ParsingError::Syntax(SyntaxError::new( + Location::test(1, 1), + vec![ + "(", "bool", "string", "address", "function", "uint{n}", "int{n}", "bytes", + "bytes{n}", + ], + Lexeme::Literal(LexicalLiteral::Integer(LexicalIntegerLiteral::new_decimal( + "42".to_owned(), + false, + ))), + ))); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/type/tuple.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/type/tuple.rs new file mode 100644 index 00000000..62c0a01d --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/type/tuple.rs @@ -0,0 +1,200 @@ +//! +//! The tuple type parser. +//! + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Symbol; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::Error as SyntaxError; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser; +use crate::test::function_call::parser::syntax::parser::r#type::Parser as TypeParser; +use crate::test::function_call::parser::syntax::tree::r#type::builder::Builder as TypeBuilder; +use crate::test::function_call::parser::syntax::tree::r#type::Type; + +/// +/// The parser state. +/// +#[derive(Debug, Clone, Copy)] +pub enum State { + /// The initial state. + ParenthesisLeft, + /// The `(` has been parsed so far. + TypeOrParenthesisRight, + /// The `( {type}` has been parsed so far. + CommaOrParenthesisRight, + /// The `( {type},` has been parsed so far. + Type, +} + +impl Default for State { + fn default() -> Self { + Self::ParenthesisLeft + } +} + +/// +/// The tuple type parser. +/// +#[derive(Default)] +pub struct Parser { + /// The parser state. + state: State, + /// The token returned from a subparser. + next: Option, + /// The builder of the parsed type. + builder: TypeBuilder, +} + +impl Parser { + /// + /// Parses a tuple type literal. + /// + /// '(uint8, uint256, bool)' + /// + pub fn parse( + mut self, + stream: Rc>, + initial: Option, + ) -> Result<(Type, Option), ParsingError> { + self.next = initial; + + loop { + match self.state { + State::ParenthesisLeft => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisLeft), + location, + } => { + self.builder.set_location(location); + self.state = State::TypeOrParenthesisRight; + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["("], lexeme).into()) + } + } + } + State::TypeOrParenthesisRight => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + .. + } => { + return Ok((self.builder.finish(), None)); + } + token => { + let (element_type, next) = + TypeParser::default().parse(stream.clone(), Some(token))?; + self.next = next; + self.builder.push_tuple_element_type(element_type); + self.state = State::CommaOrParenthesisRight; + } + } + } + State::CommaOrParenthesisRight => { + match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Symbol(Symbol::Comma), + .. + } => self.state = State::Type, + Token { + lexeme: Lexeme::Symbol(Symbol::ParenthesisRight), + .. + } => return Ok((self.builder.finish(), None)), + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec![",", ")"], lexeme).into()) + } + } + } + State::Type => { + let token = parser::take_or_next(self.next.take(), stream.clone())?; + let (element_type, next) = + TypeParser::default().parse(stream.clone(), Some(token))?; + self.next = next; + self.builder.push_tuple_element_type(element_type); + self.state = State::CommaOrParenthesisRight; + } + } + } + } +} + +#[cfg(test)] +mod tests { + + use crate::test::function_call::parser::lexical::Lexeme; + use crate::test::function_call::parser::lexical::Location; + use crate::test::function_call::parser::lexical::Symbol; + use crate::test::function_call::parser::lexical::TokenStream; + + use super::Parser; + use crate::test::function_call::parser::syntax::error::Error as SyntaxError; + use crate::test::function_call::parser::syntax::error::ParsingError; + + use crate::test::function_call::parser::syntax::tree::r#type::variant::Variant as TypeVariant; + use crate::test::function_call::parser::syntax::tree::r#type::Type; + + #[test] + fn ok_empty() { + let input = r#"()"#; + + let expected = Ok(( + Type::new(Location::test(1, 1), TypeVariant::tuple(Vec::new())), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn ok() { + let input = r#"(uint256, (), bytes[4])"#; + + let expected = Ok(( + Type::new( + Location::test(1, 1), + TypeVariant::tuple(vec![ + Type::new( + Location::test(1, 2), + TypeVariant::integer_unsigned(compiler_common::BIT_LENGTH_FIELD), + ), + Type::new(Location::test(1, 11), TypeVariant::tuple(Vec::new())), + Type::new( + Location::test(1, 15), + TypeVariant::array( + Type::new(Location::test(1, 15), TypeVariant::bytes(None)), + Some("4".to_owned()), + ), + ), + ]), + ), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn error_expected_comma_or_parenthesis_right() { + let input = r#"(uint256:"#; + + let expected = Err(ParsingError::Syntax(SyntaxError::new( + Location::test(1, 9), + vec![",", ")"], + Lexeme::Symbol(Symbol::Colon), + ))); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/parser/value.rs b/solidity_adapter/src/test/function_call/parser/syntax/parser/value.rs new file mode 100644 index 00000000..e96da35d --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/parser/value.rs @@ -0,0 +1,153 @@ +//! +//! The value parser. +//! + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Lexeme; +use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; +use crate::test::function_call::parser::lexical::Token; +use crate::test::function_call::parser::lexical::TokenStream; +use crate::test::function_call::parser::syntax::error::Error as SyntaxError; +use crate::test::function_call::parser::syntax::error::ParsingError; +use crate::test::function_call::parser::syntax::parser; +use crate::test::function_call::parser::syntax::tree::value::builder::Builder as ValueBuilder; +use crate::test::function_call::parser::syntax::tree::value::Value; + +/// +/// The parser state. +/// +#[derive(Debug, Clone, Copy)] +pub enum State { + /// The initial state. + Start, + /// The `{integer literal}` has been parsed so far. + Unit, +} + +impl Default for State { + fn default() -> Self { + Self::Start + } +} + +/// +/// The value parser. +/// +#[derive(Default)] +pub struct Parser { + /// The parser state. + state: State, + /// The builder of the parsed value. + builder: ValueBuilder, + /// The token returned from a subparser. + next: Option, +} + +impl Parser { + /// + /// Parses a value. + /// + /// ' + /// 10 ether + /// ' + /// + pub fn parse( + mut self, + stream: Rc>, + initial: Option, + ) -> Result<(Value, Option), ParsingError> { + self.next = initial; + + loop { + match self.state { + State::Start => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: + Lexeme::Literal(LexicalLiteral::Integer(LexicalIntegerLiteral::Decimal { + inner: decimal_integer, + negative: false, + })), + location, + } => { + self.builder.set_amount(decimal_integer); + self.builder.set_location(location); + self.state = State::Unit; + } + Token { lexeme, location } => { + return Err(SyntaxError::new( + location, + vec!["{positive decimal integer literal}"], + lexeme, + ) + .into()); + } + }, + State::Unit => match parser::take_or_next(self.next.take(), stream.clone())? { + Token { + lexeme: Lexeme::Keyword(keyword @ Keyword::Ether | keyword @ Keyword::Wei), + .. + } => { + self.builder.set_keyword(keyword); + return Ok((self.builder.finish(), None)); + } + Token { lexeme, location } => { + return Err(SyntaxError::new(location, vec!["ether", "wei"], lexeme).into()); + } + }, + } + } + } +} + +#[cfg(test)] +mod tests { + + use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; + use crate::test::function_call::parser::lexical::Lexeme; + use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; + use crate::test::function_call::parser::lexical::Location; + + use crate::test::function_call::parser::lexical::TokenStream; + + use super::Parser; + use crate::test::function_call::parser::syntax::error::Error as SyntaxError; + use crate::test::function_call::parser::syntax::error::ParsingError; + use crate::test::function_call::parser::syntax::tree::value::unit::Unit; + use crate::test::function_call::parser::syntax::tree::value::Value; + + #[test] + fn ok() { + let input = r#"10 ether"#; + + let expected = Ok(( + Value::new(Location::test(1, 1), Unit::Ether, "10".to_string()), + None, + )); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } + + #[test] + fn error_decimal_integer_literal() { + let input = r#"-5 wei"#; + + let expected = Err(ParsingError::from(SyntaxError::new( + Location::test(1, 1), + vec!["{positive decimal integer literal}"], + Lexeme::Literal(LexicalLiteral::Integer(LexicalIntegerLiteral::new_decimal( + "5".to_owned(), + true, + ))), + ))); + + let result = Parser::default().parse(TokenStream::test(input).wrap(), None); + + assert_eq!(result, expected); + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/call/builder.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/call/builder.rs new file mode 100644 index 00000000..96e9464b --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/call/builder.rs @@ -0,0 +1,209 @@ +//! +//! The function call builder. +//! + +use crate::test::function_call::parser::lexical::Location; +use crate::test::function_call::parser::syntax::tree::call::variant::Variant; +use crate::test::function_call::parser::syntax::tree::call::Call; +use crate::test::function_call::parser::syntax::tree::event::Event; +use crate::test::function_call::parser::syntax::tree::gas::Gas; +use crate::test::function_call::parser::syntax::tree::identifier::Identifier; +use crate::test::function_call::parser::syntax::tree::literal::Literal; +use crate::test::function_call::parser::syntax::tree::r#type::Type; +use crate::test::function_call::parser::syntax::tree::value::Value; + +/// +/// The function call builder. +/// +#[derive(Default)] +pub struct Builder { + /// The location of the syntax construction. + location: Option, + /// The function name. + call: Option, + /// The library name. + library: Option, + /// The library source file name. + library_source: Option, + /// The params types. + types: Vec, + /// The flag if for empty types should be empty vector. + is_types: bool, + /// The value option. + value: Option, + /// The input values. + input: Vec, + /// The flag if for empty input should be empty vector. + is_input: bool, + /// The expected values. + expected: Vec, + /// The flag if for empty expected should be empty vector. + is_expected: bool, + /// The failure expected flag. + failure: bool, + /// The expected events. + events: Vec, + /// The gas options. + gas: Vec, +} + +impl Builder { + /// + /// Sets the corresponding builder value. + /// + pub fn set_location(&mut self, value: Location) { + self.location = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_call(&mut self, value: Identifier) { + self.call = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_library(&mut self, value: Identifier) { + self.library = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_library_source(&mut self, value: String) { + self.library_source = Some(value); + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_types(&mut self, value: Type) { + self.types.push(value) + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_is_types(&mut self) { + self.is_types = true; + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_value(&mut self, value: Value) { + self.value = Some(value); + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_input(&mut self, value: Literal) { + self.input.push(value) + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_is_input(&mut self) { + self.is_input = true; + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_expected(&mut self, value: Literal) { + self.expected.push(value) + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_is_expected(&mut self) { + self.is_expected = true; + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_failure(&mut self) { + self.failure = true; + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_event(&mut self, value: Event) { + self.events.push(value) + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_gas(&mut self, value: Gas) { + self.gas.push(value) + } + + /// + /// Finalizes the builder and returns the built value. + /// + /// # Panics + /// If some of the required items has not been set. + /// + pub fn finish(mut self) -> Call { + let location = self + .location + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "location")); + + let variant = if let Some(identifier) = self.library.take() { + Variant::library(identifier, self.library_source) + } else { + let identifier = self.call.take(); + let types = if self.types.is_empty() { + if self.is_types { + Some(Vec::new()) + } else { + None + } + } else { + Some(self.types) + }; + + let input = if self.input.is_empty() { + if self.is_input { + Some(Vec::new()) + } else { + None + } + } else { + Some(self.input) + }; + + let expected = if self.expected.is_empty() { + if self.is_expected { + Some(Vec::new()) + } else { + None + } + } else { + Some(self.expected) + }; + + Variant::call( + identifier, + types, + self.value, + input, + expected, + self.failure, + self.events, + self.gas, + ) + }; + + Call::new(location, variant) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/call/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/call/mod.rs new file mode 100644 index 00000000..44d1f0f8 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/call/mod.rs @@ -0,0 +1,29 @@ +//! +//! The call. +//! + +pub mod builder; +pub mod variant; + +use self::variant::Variant; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The call. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Call { + /// The location of the syntax construction. + pub location: Location, + /// The variant. + pub variant: Variant, +} + +impl Call { + /// + /// Creates a function call. + /// + pub fn new(location: Location, variant: Variant) -> Self { + Self { location, variant } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/call/variant.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/call/variant.rs new file mode 100644 index 00000000..b8a63fbe --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/call/variant.rs @@ -0,0 +1,78 @@ +//! +//! The type variant. +//! + +use crate::test::function_call::parser::syntax::tree::event::Event; +use crate::test::function_call::parser::syntax::tree::gas::Gas; +use crate::test::function_call::parser::syntax::tree::literal::Literal; +use crate::test::function_call::parser::syntax::tree::r#type::Type; +use crate::test::function_call::parser::syntax::tree::value::Value; +use crate::test::function_call::parser::syntax::Identifier; + +/// +/// The type variant. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Variant { + /// `library` in the source code. + Library { + /// The library name. + identifier: Identifier, + /// The source file name. + source: Option, + }, + /// The function call. + Call { + /// The function name. + identifier: Option, + /// The params types. + types: Option>, + /// The value option. + value: Option, + /// The input values. + input: Option>, + /// The expected values. + expected: Option>, + /// The failure expected flag. + failure: bool, + /// The expected events. + events: Vec, + /// The gas options. + gas: Vec, + }, +} + +impl Variant { + /// + /// A shortcut constructor. + /// + pub fn library(identifier: Identifier, source: Option) -> Self { + Self::Library { identifier, source } + } + + /// + /// A shortcut constructor. + /// + #[allow(clippy::too_many_arguments)] + pub fn call( + identifier: Option, + types: Option>, + value: Option, + input: Option>, + expected: Option>, + failure: bool, + events: Vec, + gas: Vec, + ) -> Self { + Self::Call { + identifier, + types, + value, + input, + expected, + failure, + events, + gas, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/event/builder.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/builder.rs new file mode 100644 index 00000000..e1b58180 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/builder.rs @@ -0,0 +1,104 @@ +//! +//! The function call builder. +//! + +use crate::test::function_call::parser::lexical::Location; +use crate::test::function_call::parser::syntax::tree::event::literal::EventLiteral; +use crate::test::function_call::parser::syntax::tree::event::variant::Variant; +use crate::test::function_call::parser::syntax::tree::event::Event; +use crate::test::function_call::parser::syntax::tree::identifier::Identifier; +use crate::test::function_call::parser::syntax::tree::r#type::Type; + +/// +/// The function call builder. +/// +#[derive(Default)] +pub struct Builder { + /// The location of the syntax construction. + location: Option, + /// The event name. + identifier: Option, + /// The types. + types: Vec, + /// The expected values. + expected: Vec, + /// The address. + address: Option, + /// The flag if for empty expected should be empty vector. + is_expected: bool, +} + +impl Builder { + /// + /// Sets the corresponding builder value. + /// + pub fn set_location(&mut self, value: Location) { + self.location = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_is_expected(&mut self) { + self.is_expected = true; + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_identifier(&mut self, value: Identifier) { + self.identifier = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_address(&mut self, address: String) { + self.address = Some(address); + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_expected(&mut self, value: EventLiteral) { + self.expected.push(value) + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_type(&mut self, value: Type) { + self.types.push(value) + } + + /// + /// Finalizes the builder and returns the built value. + /// + /// # Panics + /// If some of the required items has not been set. + /// + pub fn finish(mut self) -> Event { + let location = self + .location + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "location")); + + let expected = if self.expected.is_empty() { + if !self.is_expected { + None + } else { + Some(Vec::new()) + } + } else { + Some(self.expected) + }; + + let variant = if let Some(identifier) = self.identifier.take() { + Variant::signature(identifier, self.types) + } else { + Variant::anonymous() + }; + + Event::new(location, variant, self.address, expected) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/event/literal.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/literal.rs new file mode 100644 index 00000000..b08cd2a2 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/literal.rs @@ -0,0 +1,25 @@ +//! +//! The event literal. +//! + +use crate::test::function_call::parser::syntax::tree::literal::Literal; + +/// +/// The event literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EventLiteral { + /// The inner literal. + pub inner: Literal, + /// The indexed flag. + pub indexed: bool, +} + +impl EventLiteral { + /// + /// Creates an event literal. + /// + pub fn new(inner: Literal, indexed: bool) -> Self { + Self { inner, indexed } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/event/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/mod.rs new file mode 100644 index 00000000..c58291de --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/mod.rs @@ -0,0 +1,45 @@ +//! +//! The type. +//! + +pub mod builder; +pub mod literal; +pub mod variant; + +use self::literal::EventLiteral; +use self::variant::Variant; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The type. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Event { + /// The location of the syntax construction. + pub location: Location, + /// The signature variant. + pub variant: Variant, + /// The address. + pub address: Option, + /// The expected values. + pub expected: Option>, +} + +impl Event { + /// + /// Creates a function call. + /// + pub fn new( + location: Location, + variant: Variant, + address: Option, + expected: Option>, + ) -> Self { + Self { + location, + variant, + address, + expected, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/event/variant.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/variant.rs new file mode 100644 index 00000000..1aad3691 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/event/variant.rs @@ -0,0 +1,37 @@ +//! +//! The event signature variant. +//! + +use crate::test::function_call::parser::syntax::{Identifier, Type}; + +/// +/// The event signature variant. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Variant { + /// `` in the source code. + Anonymous, + /// `{identifier}({types})` in the source code. + Signature { + /// The function name. + identifier: Identifier, + /// The function input types. + types: Vec, + }, +} + +impl Variant { + /// + /// A shortcut constructor. + /// + pub fn anonymous() -> Self { + Self::Anonymous + } + + /// + /// A shortcut constructor. + /// + pub fn signature(identifier: Identifier, types: Vec) -> Self { + Self::Signature { identifier, types } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/builder.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/builder.rs new file mode 100644 index 00000000..10f1d076 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/builder.rs @@ -0,0 +1,77 @@ +//! +//! The gas option builder. +//! + +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Location; +use crate::test::function_call::parser::syntax::tree::gas::variant::Variant as GasVariant; +use crate::test::function_call::parser::syntax::tree::gas::Gas; + +/// +/// The gas option builder. +/// +#[derive(Default)] +pub struct Builder { + /// The location of the syntax construction. + location: Option, + /// The gas option variant keyword. + keyword: Option, + /// The gas value. + value: Option, +} + +/// The invalid type keyword panic, which is prevented by the gas option parser. +static BUILDER_GAS_INVALID_KEYWORD: &str = + "The type builder has got an unexpected non-gas keyword: "; + +impl Builder { + /// + /// Sets the corresponding builder value. + /// + pub fn set_location(&mut self, value: Location) { + self.location = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_keyword(&mut self, value: Keyword) { + self.keyword = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_value(&mut self, value: String) { + self.value = Some(value); + } + + /// + /// Finalizes the builder and returns the built value. + /// + /// # Panics + /// If some of the required items has not been set. + /// + pub fn finish(mut self) -> Gas { + let location = self + .location + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "location")); + + let variant = match self.keyword.take() { + Some(Keyword::IrOptimized) => GasVariant::ir_optimized(), + Some(Keyword::Legacy) => GasVariant::legacy(), + Some(Keyword::LegacyOptimized) => GasVariant::legacy_optimized(), + Some(Keyword::Ir) => GasVariant::ir(), + Some(keyword) => panic!("{}{}", self::BUILDER_GAS_INVALID_KEYWORD, keyword), + None => panic!("{}{}", "Mandatory value missing: ", "keyword"), + }; + + let value = self + .value + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "value")); + + Gas::new(location, variant, value) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/mod.rs new file mode 100644 index 00000000..22b03291 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/mod.rs @@ -0,0 +1,35 @@ +//! +//! The gas option. +//! + +pub mod builder; +pub mod variant; + +use self::variant::Variant; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The gas option. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Gas { + /// The location of the syntax construction. + pub location: Location, + /// The gas option variant. + pub variant: Variant, + /// The gas value. + pub value: String, +} + +impl Gas { + /// + /// Creates a gas option. + /// + pub fn new(location: Location, variant: Variant, value: String) -> Self { + Self { + location, + variant, + value, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/variant.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/variant.rs new file mode 100644 index 00000000..554feaa6 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/gas/variant.rs @@ -0,0 +1,48 @@ +//! +//! The gas option variant. +//! + +/// +/// The gas option variant. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Variant { + /// `irOptimized` in the source code. + IrOptimized, + /// `legacy` in the source code. + Legacy, + /// `legacyOptimized` in the source code. + LegacyOptimized, + /// `ir` in the source code. + Ir, +} + +impl Variant { + /// + /// A shortcut constructor. + /// + pub fn ir_optimized() -> Self { + Self::IrOptimized + } + + /// + /// A shortcut constructor. + /// + pub fn legacy() -> Self { + Self::Legacy + } + + /// + /// A shortcut constructor. + /// + pub fn legacy_optimized() -> Self { + Self::LegacyOptimized + } + + /// + /// A shortcut constructor. + /// + pub fn ir() -> Self { + Self::Ir + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/identifier.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/identifier.rs new file mode 100644 index 00000000..7f2ea942 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/identifier.rs @@ -0,0 +1,25 @@ +//! +//! The identifier. +//! + +use crate::test::function_call::parser::lexical::Location; + +/// +/// The identifier. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Identifier { + /// The location of the syntax construction. + pub location: Location, + /// The identifier string contents. + pub name: String, +} + +impl Identifier { + /// + /// Creates an identifier. + /// + pub fn new(location: Location, name: String) -> Self { + Self { location, name } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/alignment.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/alignment.rs new file mode 100644 index 00000000..b1a88232 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/alignment.rs @@ -0,0 +1,39 @@ +//! +//! The alignment option. +//! + +/// +/// The alignment option. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Alignment { + /// `left({literal})` in the source code. + Left, + /// `right({literal})` in the source code. + Right, + /// `{literal}` in the source code. + Default, +} + +impl Alignment { + /// + /// A shortcut constructor. + /// + pub fn left() -> Self { + Self::Left + } + + /// + /// A shortcut constructor. + /// + pub fn right() -> Self { + Self::Right + } + + /// + /// A shortcut constructor. + /// + pub fn default() -> Self { + Self::Default + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/boolean.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/boolean.rs new file mode 100644 index 00000000..02b2df8a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/boolean.rs @@ -0,0 +1,48 @@ +//! +//! The boolean literal. +//! + +use super::alignment::Alignment; +use crate::test::function_call::parser::lexical::BooleanLiteral as LexicalBooleanLiteral; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The boolean literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Literal { + /// The location of the syntax construction. + pub location: Location, + /// The inner lexical literal. + pub inner: LexicalBooleanLiteral, + /// The alignment. + pub alignment: Alignment, +} + +impl Literal { + /// + /// Creates a new literal value. + /// + pub fn new(location: Location, inner: LexicalBooleanLiteral, alignment: Alignment) -> Self { + Self { + location, + inner, + alignment, + } + } + + /// + /// Converts literal to bytes. + /// + pub fn as_bytes_be(&self) -> Vec { + let mut result = vec![0u8; compiler_common::BYTE_LENGTH_FIELD]; + if self.inner == LexicalBooleanLiteral::True { + if self.alignment == Alignment::Left { + result[0] = 1; + } else { + *result.last_mut().expect("Always valid") = 1; + } + } + result + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/builder.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/builder.rs new file mode 100644 index 00000000..7f65b28f --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/builder.rs @@ -0,0 +1,79 @@ +//! +//! The literal builder. +//! + +use crate::test::function_call::parser::lexical::Literal as LexicalLiteral; +use crate::test::function_call::parser::lexical::Location; +use crate::test::function_call::parser::syntax::tree::literal::alignment::Alignment; +use crate::test::function_call::parser::syntax::tree::literal::boolean::Literal as BooleanLiteral; +use crate::test::function_call::parser::syntax::tree::literal::hex::Literal as HexLiteral; +use crate::test::function_call::parser::syntax::tree::literal::integer::Literal as IntegerLiteral; +use crate::test::function_call::parser::syntax::tree::literal::string::Literal as StringLiteral; +use crate::test::function_call::parser::syntax::tree::literal::Literal; + +/// +/// The literal builder. +/// +#[derive(Default)] +pub struct Builder { + /// The location of the syntax construction. + location: Option, + /// The identifier string contents. + literal: Option, + /// The alignment. + alignment: Option, +} + +impl Builder { + /// + /// Sets the corresponding builder value. + /// + pub fn set_location(&mut self, value: Location) { + self.location = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_literal(&mut self, value: LexicalLiteral) { + self.literal = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_alignment(&mut self, value: Alignment) { + self.alignment = Some(value); + } + + /// + /// Finalizes the builder and returns the built value. + /// + /// # Panics + /// If some of the required items has not been set. + /// + pub fn finish(mut self) -> Literal { + let location = self + .location + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "location")); + + let alignment = self.alignment.take().unwrap_or(Alignment::Default); + + match self.literal { + Some(LexicalLiteral::Integer(integer)) => { + Literal::Integer(IntegerLiteral::new(location, integer, alignment)) + } + Some(LexicalLiteral::String(string)) => { + Literal::String(StringLiteral::new(location, string, alignment)) + } + Some(LexicalLiteral::Boolean(boolean)) => { + Literal::Boolean(BooleanLiteral::new(location, boolean, alignment)) + } + Some(LexicalLiteral::Hex(hex)) => { + Literal::Hex(HexLiteral::new(location, hex, alignment)) + } + None => panic!("{}{}", "Mandatory value missing: ", "literal"), + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/hex.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/hex.rs new file mode 100644 index 00000000..7221d217 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/hex.rs @@ -0,0 +1,47 @@ +//! +//! The hex literal. +//! + +use std::str::FromStr; + +use super::alignment::Alignment; +use crate::test::function_call::parser::lexical::HexLiteral as LexicalHexLiteral; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The hex literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Literal { + /// The location of the syntax construction. + pub location: Location, + /// The inner lexical literal. + pub inner: LexicalHexLiteral, + /// The alignment. + pub alignment: Alignment, +} + +impl Literal { + /// + /// Creates a new literal value. + /// + pub fn new(location: Location, inner: LexicalHexLiteral, alignment: Alignment) -> Self { + Self { + location, + inner, + alignment, + } + } + + /// + /// Converts literal to bytes. + /// + pub fn as_bytes_be(&self) -> Vec { + let mut result = vec![0u8; compiler_common::BYTE_LENGTH_FIELD]; + web3::types::U256::from_str(self.inner.inner.as_str()) + .expect("Always valid") + .to_big_endian(&mut result); + result = result[result.len() - (self.inner.inner.len() + 1) / 2..].to_owned(); + result + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/integer.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/integer.rs new file mode 100644 index 00000000..fed3c492 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/integer.rs @@ -0,0 +1,74 @@ +//! +//! The integer literal. +//! + +use std::ops::Add; +use std::ops::BitXor; +use std::str::FromStr; + +use super::alignment::Alignment; +use crate::test::function_call::parser::lexical::IntegerLiteral as LexicalIntegerLiteral; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The integer literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Literal { + /// The location of the syntax construction. + pub location: Location, + /// The inner lexical literal. + pub inner: LexicalIntegerLiteral, + /// The alignment. + pub alignment: Alignment, +} + +impl Literal { + /// + /// Creates a new literal value. + /// + pub fn new(location: Location, inner: LexicalIntegerLiteral, alignment: Alignment) -> Self { + Self { + location, + inner, + alignment, + } + } + + /// + /// Converts literal to bytes. + /// + pub fn as_bytes_be(&self) -> Vec { + let mut result = vec![0u8; compiler_common::BYTE_LENGTH_FIELD]; + match &self.inner { + LexicalIntegerLiteral::Decimal { inner, negative } => { + let mut number = + web3::types::U256::from_dec_str(inner.as_str()).expect("Always valid"); + if *negative { + number = number.bitxor(web3::types::U256::max_value()); + number = number.add(web3::types::U256::one()); + } + number.to_big_endian(&mut result); + let first = result + .iter() + .position(|byte| *byte != 0) + .unwrap_or(result.len() - 1); + result = result[first..].to_owned(); + } + LexicalIntegerLiteral::Hexadecimal(inner) => { + web3::types::U256::from_str(inner) + .expect("Always valid") + .to_big_endian(&mut result); + result = result[result.len() - (inner.len() + 1) / 2..].to_owned(); + } + } + if self.alignment == Alignment::Left { + result.extend(vec![0; compiler_common::BYTE_LENGTH_FIELD - result.len()]); + } else { + let mut zeroes = vec![0; compiler_common::BYTE_LENGTH_FIELD - result.len()]; + zeroes.extend(result); + result = zeroes; + } + result + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/mod.rs new file mode 100644 index 00000000..e526f6e2 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/mod.rs @@ -0,0 +1,46 @@ +//! +//! The literal. +//! + +pub mod alignment; +pub mod boolean; +pub mod builder; +pub mod hex; +pub mod integer; +pub mod string; + +use self::boolean::Literal as BooleanLiteral; +use self::hex::Literal as HexLiteral; +use self::integer::Literal as IntegerLiteral; +use self::string::Literal as StringLiteral; + +/// +/// The literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Literal { + /// The boolean literal. + Boolean(BooleanLiteral), + /// The integer literal. + Integer(IntegerLiteral), + /// The string literal. + String(StringLiteral), + /// The hex literal. + Hex(HexLiteral), +} + +impl Literal { + /// + /// Converts literal to bytes. + /// + pub fn as_bytes_be(&self) -> anyhow::Result> { + match self { + Literal::Boolean(boolean) => Ok(boolean.as_bytes_be()), + Literal::Integer(integer) => Ok(integer.as_bytes_be()), + Literal::Hex(hex) => Ok(hex.as_bytes_be()), + Literal::String(string) => string + .as_bytes_be() + .map_err(|err| anyhow::anyhow!("Failed to process string: {}", err)), + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/string.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/string.rs new file mode 100644 index 00000000..cc198c2a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/literal/string.rs @@ -0,0 +1,109 @@ +//! +//! The string literal. +//! + +use std::str::FromStr; + +use super::alignment::Alignment; +use crate::test::function_call::parser::lexical::Location; +use crate::test::function_call::parser::lexical::StringLiteral as LexicalStringLiteral; + +/// +/// The string literal. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Literal { + /// The location of the syntax construction. + pub location: Location, + /// The inner lexical literal. + pub inner: LexicalStringLiteral, + /// The alignment. + pub alignment: Alignment, +} + +impl Literal { + /// + /// Creates a new literal value. + /// + pub fn new(location: Location, inner: LexicalStringLiteral, alignment: Alignment) -> Self { + Self { + location, + inner, + alignment, + } + } + + /// + /// Converts literal to bytes. + /// + pub fn as_bytes_be(&self) -> anyhow::Result> { + /// The helper state enum for converting a string literal to bytes. + #[derive(PartialEq)] + enum State { + /// The initial state or character has been processed so far. + Char, + /// The `\` has been processed so far. + Backslash, + /// The `\x` has been processed so far. + HexFirst, + /// The first hexadecimal symbol has been processed so far. + HexSecond, + } + let mut result = Vec::new(); + let mut state = State::Char; + let mut code = String::new(); + for char in self.inner.inner.chars() { + match state { + State::Char => match char { + '\\' => { + state = State::Backslash; + } + _ => { + result.extend(char.to_string().as_bytes()); + } + }, + State::Backslash => match char { + 'x' => { + state = State::HexFirst; + } + '0' => { + result.push(0); + state = State::Char; + } + _ => { + anyhow::bail!("Invalid escape sequence: expected 'x' or '0' after '\\'"); + } + }, + State::HexFirst => { + code.push(char); + state = State::HexSecond; + } + State::HexSecond => { + code.push(char); + let code_u8 = web3::types::U256::from_str(code.as_str()) + .map_err(|err| anyhow::anyhow!("Invalid escape sequence: {}", err))? + .as_u32() as u8; + code.clear(); + result.push(code_u8); + state = State::Char; + } + } + } + if state != State::Char { + anyhow::bail!("Unterminated escape sequence"); + } + let mut pad_len = 0; + if result.len() % compiler_common::BYTE_LENGTH_FIELD != 0 || result.is_empty() { + pad_len = compiler_common::BYTE_LENGTH_FIELD + - result.len() % compiler_common::BYTE_LENGTH_FIELD; + } + if self.alignment == Alignment::Right { + let mut zeroes = vec![0; pad_len]; + zeroes.extend(result); + result = zeroes; + } else { + result.extend(vec![0; pad_len]); + } + Ok(result) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/mod.rs new file mode 100644 index 00000000..4b780b47 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/mod.rs @@ -0,0 +1,11 @@ +//! +//! The syntax tree. +//! + +pub mod call; +pub mod event; +pub mod gas; +pub mod identifier; +pub mod literal; +pub mod r#type; +pub mod value; diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/type/builder.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/type/builder.rs new file mode 100644 index 00000000..2211955a --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/type/builder.rs @@ -0,0 +1,102 @@ +//! +//! The type builder. +//! + +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Location; +use crate::test::function_call::parser::syntax::tree::r#type::variant::Variant as TypeVariant; +use crate::test::function_call::parser::syntax::tree::r#type::Type; + +/// +/// The type builder. +/// +#[derive(Default)] +pub struct Builder { + /// The location of the syntax construction. + location: Option, + /// The type keyword, which means that the type is intrinsic. + keyword: Option, + /// The array type, which means that the type is an array. + array_type: Option, + /// The array size expression, which means that the type is an array. + array_size: Option, + /// The tuple elements, which means that the type is a tuple. + tuple_element_types: Vec, +} + +/// The invalid type keyword panic, which is prevented by the type parser. +static BUILDER_TYPE_INVALID_KEYWORD: &str = + "The type builder has got an unexpected non-type keyword: "; + +impl Builder { + /// + /// Sets the corresponding builder value. + /// + pub fn set_location(&mut self, value: Location) { + self.location = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_keyword(&mut self, value: Keyword) { + self.keyword = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_array_type(&mut self, value: Type) { + self.array_type = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_array_size(&mut self, value: String) { + self.array_size = Some(value); + } + + /// + /// Pushes the corresponding builder value. + /// + pub fn push_tuple_element_type(&mut self, value: Type) { + self.tuple_element_types.push(value) + } + + /// + /// Finalizes the builder and returns the built value. + /// + /// # Panics + /// If some of the required items has not been set. + /// + pub fn finish(mut self) -> Type { + let location = self + .location + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "location")); + + let variant = if let Some(keyword) = self.keyword.take() { + match keyword { + Keyword::Bool => TypeVariant::boolean(), + Keyword::String => TypeVariant::string(), + Keyword::Address => TypeVariant::address(), + Keyword::Function => TypeVariant::function(), + Keyword::IntegerUnsigned { bit_length } => { + TypeVariant::integer_unsigned(bit_length) + } + Keyword::IntegerSigned { bit_length } => TypeVariant::integer_signed(bit_length), + Keyword::Bytes { byte_length } => TypeVariant::bytes(byte_length), + keyword => panic!("{}{}", self::BUILDER_TYPE_INVALID_KEYWORD, keyword), + } + } else if let Some(array_type) = self.array_type.take() { + TypeVariant::array(array_type, self.array_size.take()) + } else if !self.tuple_element_types.is_empty() { + TypeVariant::tuple(self.tuple_element_types) + } else { + TypeVariant::tuple(Vec::new()) + }; + + Type::new(location, variant) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/type/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/type/mod.rs new file mode 100644 index 00000000..76e59fc1 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/type/mod.rs @@ -0,0 +1,60 @@ +//! +//! The type. +//! + +pub mod builder; +pub mod variant; + +use self::variant::Variant; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The type. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Type { + /// The location of the syntax construction. + pub location: Location, + /// The type variant. + pub variant: Variant, +} + +impl Type { + /// + /// Creates a type. + /// + pub fn new(location: Location, variant: Variant) -> Self { + Self { location, variant } + } +} + +impl ToString for Type { + fn to_string(&self) -> String { + match &self.variant { + Variant::String => "string".to_owned(), + Variant::Boolean => "bool".to_owned(), + Variant::Address => "address".to_owned(), + Variant::Function => "function".to_owned(), + Variant::Bytes { byte_length } => match byte_length { + Some(byte_length) => format!("bytes{byte_length}"), + None => "bytes".to_owned(), + }, + Variant::IntegerUnsigned { bit_length } => format!("uint{bit_length}"), + Variant::IntegerSigned { bit_length } => format!("int{bit_length}"), + Variant::Array { inner, size } => match size { + Some(size) => format!("{}[{}]", inner.to_string(), size), + None => format!("{}[]", inner.to_string()), + }, + Variant::Tuple { inners } => { + format!( + "({})", + inners + .iter() + .map(|r#type| r#type.to_string()) + .collect::>() + .join(",") + ) + } + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/type/variant.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/type/variant.rs new file mode 100644 index 00000000..ce4303cd --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/type/variant.rs @@ -0,0 +1,126 @@ +//! +//! The type variant. +//! + +use crate::test::function_call::parser::syntax::tree::r#type::Type; + +/// +/// The type variant. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Variant { + /// `bool` in the source code. + Boolean, + /// `string` in the source code. + String, + /// `address` in the source code. + Address, + /// `function` in the source code. + Function, + /// `uint{N}` in the source code. + IntegerUnsigned { + /// The unsigned integer bit-length. + bit_length: usize, + }, + /// `int{N}` in the source code. + IntegerSigned { + /// The signed integer bit-length. + bit_length: usize, + }, + /// `bytes{N}` in the source code. + Bytes { + /// The bytes byte-length. + byte_length: Option, + }, + /// `{type}[{expression}]` in the source code. + Array { + /// The array element type. + inner: Box, + /// The array size. + size: Option, + }, + /// `({type1}, {type2}, ...)` in the source code. + Tuple { + /// The tuple element types. + inners: Vec, + }, +} + +impl Variant { + /// + /// A shortcut constructor. + /// + pub fn boolean() -> Self { + Self::Boolean + } + + /// + /// A shortcut constructor. + /// + pub fn string() -> Self { + Self::String + } + + /// + /// A shortcut constructor. + /// + pub fn address() -> Self { + Self::Address + } + + /// + /// A shortcut constructor. + /// + pub fn function() -> Self { + Self::Function + } + + /// + /// A shortcut constructor. + /// + pub fn integer(is_signed: bool, bit_length: usize) -> Self { + if is_signed { + Self::integer_signed(bit_length) + } else { + Self::integer_unsigned(bit_length) + } + } + + /// + /// A shortcut constructor. + /// + pub fn integer_unsigned(bit_length: usize) -> Self { + Self::IntegerUnsigned { bit_length } + } + + /// + /// A shortcut constructor. + /// + pub fn integer_signed(bit_length: usize) -> Self { + Self::IntegerSigned { bit_length } + } + + /// + /// A shortcut constructor. + /// + pub fn bytes(byte_length: Option) -> Self { + Self::Bytes { byte_length } + } + + /// + /// A shortcut constructor. + /// + pub fn array(inner: Type, size: Option) -> Self { + Self::Array { + inner: Box::new(inner), + size, + } + } + + /// + /// A shortcut constructor. + /// + pub fn tuple(inners: Vec) -> Self { + Self::Tuple { inners } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/value/builder.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/value/builder.rs new file mode 100644 index 00000000..e2eb7f65 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/value/builder.rs @@ -0,0 +1,75 @@ +//! +//! The value option builder. +//! + +use crate::test::function_call::parser::lexical::Keyword; +use crate::test::function_call::parser::lexical::Location; +use crate::test::function_call::parser::syntax::tree::value::unit::Unit; +use crate::test::function_call::parser::syntax::tree::value::Value; + +/// +/// The value option builder. +/// +#[derive(Default)] +pub struct Builder { + /// The location of the syntax construction. + location: Option, + /// The unit keyword. + keyword: Option, + /// The amount. + amount: Option, +} + +/// The invalid type keyword panic, which is prevented by the gas option parser. +static BUILDER_VALUE_INVALID_KEYWORD: &str = + "The type builder has got an unexpected non-unit keyword: "; + +impl Builder { + /// + /// Sets the corresponding builder value. + /// + pub fn set_location(&mut self, value: Location) { + self.location = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_keyword(&mut self, value: Keyword) { + self.keyword = Some(value); + } + + /// + /// Sets the corresponding builder value. + /// + pub fn set_amount(&mut self, value: String) { + self.amount = Some(value); + } + + /// + /// Finalizes the builder and returns the built value. + /// + /// # Panics + /// If some of the required items has not been set. + /// + pub fn finish(mut self) -> Value { + let location = self + .location + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "location")); + + let unit = match self.keyword.take() { + Some(Keyword::Ether) => Unit::ether(), + Some(Keyword::Wei) => Unit::wei(), + Some(keyword) => panic!("{}{}", self::BUILDER_VALUE_INVALID_KEYWORD, keyword), + None => panic!("{}{}", "Mandatory value missing: ", "keyword"), + }; + + let amount = self + .amount + .take() + .unwrap_or_else(|| panic!("{}{}", "Mandatory value missing: ", "amount")); + + Value::new(location, unit, amount) + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/value/mod.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/value/mod.rs new file mode 100644 index 00000000..3be009c8 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/value/mod.rs @@ -0,0 +1,35 @@ +//! +//! The value option. +//! + +pub mod builder; +pub mod unit; + +use self::unit::Unit; +use crate::test::function_call::parser::lexical::Location; + +/// +/// The value option. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Value { + /// The location of the syntax construction. + pub location: Location, + /// The unit. + pub unit: Unit, + /// The amount. + pub amount: String, +} + +impl Value { + /// + /// Creates a value option. + /// + pub fn new(location: Location, unit: Unit, amount: String) -> Self { + Self { + location, + unit, + amount, + } + } +} diff --git a/solidity_adapter/src/test/function_call/parser/syntax/tree/value/unit.rs b/solidity_adapter/src/test/function_call/parser/syntax/tree/value/unit.rs new file mode 100644 index 00000000..a5ede709 --- /dev/null +++ b/solidity_adapter/src/test/function_call/parser/syntax/tree/value/unit.rs @@ -0,0 +1,30 @@ +//! +//! The unit. +//! + +/// +/// The unit. +/// +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Unit { + /// `ether` in the source code. + Ether, + /// `wei` in the source code. + Wei, +} + +impl Unit { + /// + /// A shortcut constructor. + /// + pub fn ether() -> Self { + Self::Ether + } + + /// + /// A shortcut constructor. + /// + pub fn wei() -> Self { + Self::Wei + } +} diff --git a/solidity_adapter/src/test/mod.rs b/solidity_adapter/src/test/mod.rs new file mode 100644 index 00000000..4e2a4f8b --- /dev/null +++ b/solidity_adapter/src/test/mod.rs @@ -0,0 +1,181 @@ +//! +//! The semantic test instance. +//! + +pub mod function_call; +pub mod params; + +use std::fs; +use std::io::Read; +use std::path::Path; +use std::path::PathBuf; + +use regex::Regex; + +use self::function_call::FunctionCall; +use self::params::Params; + +/// +/// The semantic test instance. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Test { + /// The source code files. + pub sources: Vec<(String, String)>, + /// The test params. + pub params: Params, + /// The function calls. + pub calls: Vec, +} + +impl TryFrom<&Path> for Test { + type Error = anyhow::Error; + + fn try_from(path: &Path) -> Result { + let mut file = fs::File::open(path)?; + + let mut data = String::new(); + file.read_to_string(&mut data) + .map_err(|error| anyhow::anyhow!("Failed to read test file: {}", error))?; + + let comment_start = if path + .extension() + .ok_or_else(|| anyhow::anyhow!("Failed to get file extension"))? + == compiler_common::EXTENSION_VYPER + { + "# ".to_owned() + } else { + "// ".to_owned() + }; + + let sources = process_sources(&data, path)?; + + let (data, function_calls) = data + .split_once(&format!("{comment_start}----\n")) + .ok_or_else(|| anyhow::anyhow!("Invalid test format"))?; + + let params = data + .split_once(&format!("{comment_start}====\n")) + .map(|parts| parts.1) + .unwrap_or_default(); + + let params = params + .lines() + .filter_map(|line| line.strip_prefix(&comment_start)) + .map(|line| { + let mut line = line.to_owned(); + line.push('\n'); + line + }) + .collect::>() + .join(""); + + let params = Params::try_from(params.as_str()) + .map_err(|err| anyhow::anyhow!("Failed to parse params: {}", err))?; + + let function_calls = function_calls + .lines() + .filter_map(|line| line.strip_prefix(&comment_start)) + .map(|line| { + let mut line = line.to_owned(); + line.push('\n'); + line + }) + .collect::>() + .join(""); + + let calls = FunctionCall::parse_calls(function_calls.as_str()) + .map_err(|err| anyhow::anyhow!("Failed to parse function calls: {}", err))?; + + Ok(Self { + sources, + params, + calls, + }) + } +} + +/// +/// Returns sources. +/// +fn process_sources(data: &str, path: &Path) -> anyhow::Result> { + let mut sources = Vec::new(); + + let mut source_name = None; + let mut source = String::new(); + + let regex = Regex::new(r"^==== (.*): (.*) ====$").expect("Always valid"); + + for (index, line) in data.lines().enumerate() { + let captures = match regex.captures(line) { + Some(captures) => captures, + None => { + source.push_str(line); + source.push('\n'); + continue; + } + }; + + match captures.get(1).expect("Always exists").as_str() { + "ExternalSource" => { + let data = captures.get(2).expect("Always exists").as_str(); + let regex = Regex::new("^([^=]*)=(.*)$").expect("Always valid"); + let (name, relative_path) = match regex.captures(data) { + Some(captures) => ( + captures.get(1).expect("Always exists").as_str().to_owned(), + PathBuf::try_from(captures.get(2).expect("Always exists").as_str()), + ), + None => (data.to_owned(), PathBuf::try_from(data)), + }; + let relative_path = relative_path.map_err(|err| { + anyhow::anyhow!("Invalid path on line {}: {}", index + 1, err) + })?; + + let path = path + .parent() + .ok_or_else(|| anyhow::anyhow!("Failed to get parent directory of file"))? + .join(relative_path); + + let mut file = fs::File::open(path)?; + + let mut data = String::new(); + file.read_to_string(&mut data).map_err(|error| { + anyhow::anyhow!("Failed to read source code file: {}", error) + })?; + + sources.push((name, data)); + } + "Source" => { + let name = captures.get(2).expect("Always exists").as_str().to_owned(); + + if let Some(source_name) = source_name { + sources.push((source_name, source)); + source = String::new(); + } + + // For sources without names + if !source.is_empty() { + sources.push((String::new(), source)); + source = String::new(); + } + + source_name = Some(name); + } + word => anyhow::bail!( + "Expected \"Source\" or \"ExternalSource\" on line {}, found: {}", + index + 1, + word + ), + } + } + + if !source.is_empty() { + let name = match source_name { + Some(source_name) => source_name, + None => path.to_string_lossy().to_string(), + }; + sources.push((name, source)); + } + + Ok(sources) +} diff --git a/solidity_adapter/src/test/params/abi_encoder_v1_only.rs b/solidity_adapter/src/test/params/abi_encoder_v1_only.rs new file mode 100644 index 00000000..220c3c19 --- /dev/null +++ b/solidity_adapter/src/test/params/abi_encoder_v1_only.rs @@ -0,0 +1,28 @@ +//! +//! ABIEncoderV1Only param values. +//! + +/// +/// ABIEncoderV1Only param values. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum ABIEncoderV1Only { + /// `true` in the metadata. + True, + /// not specified in the metadata. + Default, +} + +impl TryFrom<&str> for ABIEncoderV1Only { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + Ok(match value { + "true" => ABIEncoderV1Only::True, + word => anyhow::bail!( + r#"Expected "true" as ABIEncoderV1Only value, found: {}"#, + word + ), + }) + } +} diff --git a/solidity_adapter/src/test/params/allow_non_existing_functions.rs b/solidity_adapter/src/test/params/allow_non_existing_functions.rs new file mode 100644 index 00000000..bd3df647 --- /dev/null +++ b/solidity_adapter/src/test/params/allow_non_existing_functions.rs @@ -0,0 +1,28 @@ +//! +//! allowNonExistingFunctions param values. +//! + +/// +/// allowNonExistingFunctions param values. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum AllowNonExistingFunctions { + /// `true` in the metadata. + True, + /// not specified in the metadata. + Default, +} + +impl TryFrom<&str> for AllowNonExistingFunctions { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + Ok(match value { + "true" => AllowNonExistingFunctions::True, + word => anyhow::bail!( + r#"Expected "true" as allowNonExistingFunctions value, found: {}"#, + word + ), + }) + } +} diff --git a/solidity_adapter/src/test/params/compile_to_ewasm.rs b/solidity_adapter/src/test/params/compile_to_ewasm.rs new file mode 100644 index 00000000..10ea3893 --- /dev/null +++ b/solidity_adapter/src/test/params/compile_to_ewasm.rs @@ -0,0 +1,31 @@ +//! +//! compileToEwasm param values. +//! + +/// +/// compileToEwasm param values. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum CompileToEwasm { + /// `also` in the metadata. + Also, + /// `false` in the metadata. + False, + /// not specified in the metadata. + Default, +} + +impl TryFrom<&str> for CompileToEwasm { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + Ok(match value { + "also" => CompileToEwasm::Also, + "false" => CompileToEwasm::False, + word => anyhow::bail!( + r#"Expected "also", or "false" as compileToEwasm value, found: {}"#, + word + ), + }) + } +} diff --git a/solidity_adapter/src/test/params/compile_via_yul.rs b/solidity_adapter/src/test/params/compile_via_yul.rs new file mode 100644 index 00000000..ee939ff5 --- /dev/null +++ b/solidity_adapter/src/test/params/compile_via_yul.rs @@ -0,0 +1,34 @@ +//! +//! compileViaYul param values. +//! + +/// +/// compileViaYul param values. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum CompileViaYul { + /// `also` in the metadata. + Also, + /// `true` in the metadata. + True, + /// `false` in the metadata. + False, + /// not specified in the metadata. + Default, +} + +impl TryFrom<&str> for CompileViaYul { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + Ok(match value { + "also" => CompileViaYul::Also, + "true" => CompileViaYul::True, + "false" => CompileViaYul::False, + word => anyhow::bail!( + r#"Expected "also", "true", or "false" as compileViaYul, found: {}"#, + word + ), + }) + } +} diff --git a/solidity_adapter/src/test/params/evm_version.rs b/solidity_adapter/src/test/params/evm_version.rs new file mode 100644 index 00000000..381d208f --- /dev/null +++ b/solidity_adapter/src/test/params/evm_version.rs @@ -0,0 +1,126 @@ +//! +//! EVM version param values. +//! + +use regex::Regex; + +/// +/// EVM version param values. +/// +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EVMVersion { + /// Equals specified. + Equals(EVM), + /// Greater than specified. + Greater(EVM), + /// Lesser than specified. + Lesser(EVM), + /// Greater or equals than specified. + GreaterEquals(EVM), + /// Lesser or equals than specified. + LesserEquals(EVM), + /// Not specified. + Default, +} + +impl EVMVersion { + /// + /// Checks whether the specified version matches the requirement. + /// + pub fn matches(&self, version: &EVM) -> bool { + match self { + Self::Equals(inner) => version == inner, + Self::Greater(inner) => version > inner, + Self::Lesser(inner) => version < inner, + Self::GreaterEquals(inner) => version >= inner, + Self::LesserEquals(inner) => version <= inner, + Self::Default => true, + } + } + + /// + /// Checks whether the specified versions matches the requirement. + /// + pub fn matches_any(&self, versions: &[EVM]) -> bool { + for version in versions.iter() { + if self.matches(version) { + return true; + } + } + + false + } +} + +impl TryFrom<&str> for EVMVersion { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + let regex = Regex::new(r#"^(=|>|<|>=|<=)(\w*)$"#).expect("Always valid"); + + let captures = regex + .captures(value) + .ok_or_else(|| anyhow::anyhow!("Invalid EVM version description: {}", value))?; + + let symbol = captures.get(1).expect("Always exists").as_str(); + let version = captures.get(2).expect("Always exists").as_str(); + + let version: EVM = version.try_into()?; + + Ok(match symbol { + "=" => EVMVersion::Equals(version), + ">" => EVMVersion::Greater(version), + "<" => EVMVersion::Lesser(version), + ">=" => EVMVersion::GreaterEquals(version), + "<=" => EVMVersion::LesserEquals(version), + _ => anyhow::bail!("Invalid symbol before EVM version: {}", symbol), + }) + } +} + +/// +/// EVM version. +/// +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd)] +pub enum EVM { + /// Homestead EVM version. + Homestead, + /// TangerineWhistle EVM version. + TangerineWhistle, + /// SpuriousDragon EVM version. + SpuriousDragon, + /// Byzantium EVM version. + Byzantium, + /// Constantinople EVM version. + Constantinople, + /// Petersburg EVM version. + Petersburg, + /// Istanbul EVM version. + Istanbul, + /// Berlin EVM version. + Berlin, + /// London EVM version. + London, + /// Paris EVM version. + Paris, +} + +impl TryFrom<&str> for EVM { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + Ok(match value { + "homestead" => EVM::Homestead, + "tangerineWhistle" => EVM::TangerineWhistle, + "spuriousDragon" => EVM::SpuriousDragon, + "byzantium" => EVM::Byzantium, + "constantinople" => EVM::Constantinople, + "petersburg" => EVM::Petersburg, + "istanbul" => EVM::Istanbul, + "berlin" => EVM::Berlin, + "london" => EVM::London, + "paris" => EVM::Paris, + _ => anyhow::bail!("Invalid EVM: {}", value), + }) + } +} diff --git a/solidity_adapter/src/test/params/mod.rs b/solidity_adapter/src/test/params/mod.rs new file mode 100644 index 00000000..e78b94ea --- /dev/null +++ b/solidity_adapter/src/test/params/mod.rs @@ -0,0 +1,111 @@ +//! +//! The test file params. +//! + +pub mod abi_encoder_v1_only; +pub mod allow_non_existing_functions; +pub mod compile_to_ewasm; +pub mod compile_via_yul; +pub mod evm_version; +pub mod revert_strings; + +use regex::Regex; + +use self::abi_encoder_v1_only::ABIEncoderV1Only; +use self::allow_non_existing_functions::AllowNonExistingFunctions; +use self::compile_to_ewasm::CompileToEwasm; +use self::compile_via_yul::CompileViaYul; +use self::evm_version::EVMVersion; +use self::revert_strings::RevertStrings; + +/// +/// The test file params. +/// +#[derive(Debug, PartialEq, Eq)] +pub struct Params { + /// The compileViaYul param value. + pub compile_via_yul: CompileViaYul, + /// The compileToEwasm param value. + pub compile_to_ewasm: CompileToEwasm, + /// The ABIEncoderV1Only param value. + pub abi_encoder_v1_only: ABIEncoderV1Only, + /// EVM versions param value. + pub evm_version: EVMVersion, + /// revertStrings param value. + pub revert_strings: RevertStrings, + /// allowNonExistingFunctions param value. + pub allow_non_existing_functions: AllowNonExistingFunctions, +} + +impl TryFrom<&str> for Params { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + let mut compile_via_yul = CompileViaYul::Default; + let mut compile_to_ewasm = CompileToEwasm::Default; + let mut abi_encoder_v1_only = ABIEncoderV1Only::Default; + let mut evm_version = EVMVersion::Default; + let mut revert_strings = RevertStrings::Default; + let mut allow_non_existing_functions = AllowNonExistingFunctions::Default; + + for (index, line) in value.lines().enumerate() { + let regex = Regex::new("^(.*): (.*)$").expect("Always valid"); + let captures = regex.captures(line).ok_or_else(|| { + anyhow::anyhow!( + "Expected option description on line: {}, found: {}", + index + 1, + line + ) + })?; + + let param = captures.get(1).expect("Always exists").as_str(); + let value = captures.get(2).expect("Always exists").as_str(); + match param { + "compileViaYul" => { + compile_via_yul = value + .try_into() + .map_err(|error| anyhow::anyhow!("{} on line {}", error, index + 1))?; + } + "compileToEwasm" => { + compile_to_ewasm = value + .try_into() + .map_err(|error| anyhow::anyhow!("{} on line {}", error, index + 1))?; + } + "ABIEncoderV1Only" => { + abi_encoder_v1_only = value + .try_into() + .map_err(|error| anyhow::anyhow!("{} on line {}", error, index + 1))?; + } + "EVMVersion" => { + evm_version = value + .try_into() + .map_err(|error| anyhow::anyhow!("{} on line {}", error, index + 1))?; + } + "revertStrings" => { + revert_strings = value + .try_into() + .map_err(|error| anyhow::anyhow!("{} on line {}", error, index + 1))?; + } + "allowNonExistingFunctions" => { + allow_non_existing_functions = value + .try_into() + .map_err(|error| anyhow::anyhow!("{} on line {}", error, index + 1))?; + } + word => anyhow::bail!( + r#"Expected "compileViaYul", "compileToEwasm", "ABIEncoderV1Only", "revertStrings", or "EVMVersion" on line {}, found: {}"#, + index + 1, + word + ), + } + } + + Ok(Self { + compile_via_yul, + compile_to_ewasm, + abi_encoder_v1_only, + evm_version, + revert_strings, + allow_non_existing_functions, + }) + } +} diff --git a/solidity_adapter/src/test/params/revert_strings.rs b/solidity_adapter/src/test/params/revert_strings.rs new file mode 100644 index 00000000..4ec04ccf --- /dev/null +++ b/solidity_adapter/src/test/params/revert_strings.rs @@ -0,0 +1,28 @@ +//! +//! revertStrings param values. +//! + +/// +/// revertStrings param values. +/// +#[derive(Debug, PartialEq, Eq)] +pub enum RevertStrings { + /// `debug` in the metadata. + Debug, + /// not specified in the metadata. + Default, +} + +impl TryFrom<&str> for RevertStrings { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result { + Ok(match value { + "debug" => RevertStrings::Debug, + word => anyhow::bail!( + r#"Expected "debug" as revertStrings value, found: {}"#, + word + ), + }) + } +} diff --git a/solidity_adapter/src/tests_updater/arguments.rs b/solidity_adapter/src/tests_updater/arguments.rs new file mode 100644 index 00000000..0e44673d --- /dev/null +++ b/solidity_adapter/src/tests_updater/arguments.rs @@ -0,0 +1,47 @@ +//! +//! The tests updater binary arguments. +//! + +use std::path::PathBuf; + +use structopt::StructOpt; + +/// +/// The tests updater binary arguments. +/// +#[derive(Debug, StructOpt)] +#[structopt(name = "tests-updater", about = "The Solidity tests updater")] +pub struct Arguments { + /// The tests index path + #[structopt( + default_value = "tests/solidity/ethereum/index.yaml", + short = "i", + long = "index" + )] + pub index: PathBuf, + + /// The tests update source. + #[structopt( + default_value = "solidity/test/libsolidity/semanticTests", + short = "s", + long = "source" + )] + pub source: PathBuf, + + /// The tests update destination. + #[structopt( + default_value = "tests/solidity/ethereum", + short = "d", + long = "destination" + )] + pub destination: PathBuf, +} + +impl Arguments { + /// + /// A shortcut constructor. + /// + pub fn new() -> Self { + Self::from_args() + } +} diff --git a/solidity_adapter/src/tests_updater/main.rs b/solidity_adapter/src/tests_updater/main.rs new file mode 100644 index 00000000..99a72981 --- /dev/null +++ b/solidity_adapter/src/tests_updater/main.rs @@ -0,0 +1,62 @@ +//! +//! The tests updater binary. +//! + +pub(crate) mod arguments; + +use std::fs::OpenOptions; +use std::io::BufReader; +use std::io::Write; + +use self::arguments::Arguments; + +/// +/// Run updating +/// +fn main() { + let arguments = Arguments::new(); + + let file = OpenOptions::new() + .read(true) + .open(arguments.index.clone()) + .expect("Failed to open file"); + let reader = BufReader::new(file); + let old_index: solidity_adapter::FSEntity = + serde_yaml::from_reader(reader).expect("Failed to read index"); + + let mut new_index = + solidity_adapter::FSEntity::index(&arguments.source).expect("Failed to update index"); + let (created, deleted, updated) = old_index + .update(&mut new_index, arguments.destination.as_path()) + .expect("Failed to update tests"); + + println!("{} files created:\n", created.len()); + for file in created { + println!("{}", file.to_string_lossy()); + } + println!(); + + println!("{} files deleted:\n", deleted.len()); + for file in deleted { + println!("{}", file.to_string_lossy()); + } + println!(); + + println!("{} files updated:\n", updated.len()); + for (file, conflicts) in updated { + println!("{} ({})", file.to_string_lossy(), conflicts); + } + println!(); + + let new_index = serde_yaml::to_string(&new_index).expect("Serialization"); + let mut file_to_write = OpenOptions::new() + .write(true) + .truncate(true) + .open(arguments.index) + .expect("Failed to open file"); + file_to_write + .write_all(new_index.as_bytes()) + .expect("Failed to write to the output file"); + + println!("Test files successfully updated"); +} diff --git a/system-contracts b/system-contracts new file mode 160000 index 00000000..d268a02b --- /dev/null +++ b/system-contracts @@ -0,0 +1 @@ +Subproject commit d268a02b52edebc380acabb006f944725d2f12cc diff --git a/system-contracts-stable-build b/system-contracts-stable-build new file mode 100644 index 00000000..8380bc7d Binary files /dev/null and b/system-contracts-stable-build differ diff --git a/tests b/tests new file mode 160000 index 00000000..f4a7e663 --- /dev/null +++ b/tests @@ -0,0 +1 @@ +Subproject commit f4a7e663a7d8185fdbaae464fe506b5bdf01a16a