diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ae2b6..8b2371e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Mocks for the `msg::sender()` #14 +- Mocks for the external contract calls. + Two and more contracts can be injected into test #14 +- Option to inject `Account` or `Address` in the test #14 + +### Changed (Breaking) + +- `Contract<..>` wrapper is mandatory for the contract's argument #14 +- To call contract's function it is mandatory to explicitly indicate the sender, + through `Contract::<..>::sender(..)` function #14 +- `Contract`'s `T` type should implement `TopLevelStorage` #14 + ## [0.3.0] - 2025-01-07 ### Changed (Breaking) diff --git a/Cargo.lock b/Cargo.lock index d14463a..4e971ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,22 +17,46 @@ version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9db948902dfbae96a73c2fbf1f7abec62af034ab883e4c777c3fd29702bd6e2c" dependencies = [ + "alloy-rlp", + "arbitrary", "bytes", "cfg-if", "const-hex", + "derive_arbitrary", "derive_more", + "foldhash", + "getrandom", "hex-literal", + "indexmap", "itoa", + "k256", + "keccak-asm", "paste", + "proptest", + "proptest-derive", + "rand", "ruint", + "rustc-hash", + "serde", + "sha3", "tiny-keccak", ] +[[package]] +name = "alloy-rlp" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097" +dependencies = [ + "arrayvec", + "bytes", +] + [[package]] name = "alloy-sol-macro" -version = "0.8.18" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07b74d48661ab2e4b50bb5950d74dbff5e61dd8ed03bb822281b706d54ebacb" +checksum = "d9d64f851d95619233f74b310f12bcf16e0cbc27ee3762b6115c14a84809280a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -44,9 +68,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.18" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19cc9c7f20b90f9be1a8f71a3d8e283a43745137b0837b1a1cb13159d37cad72" +checksum = "6bf7ed1574b699f48bf17caab4e6e54c6d12bc3c006ab33d58b1e227c1c3559f" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -62,9 +86,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.18" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713b7e6dfe1cb2f55c80fb05fd22ed085a1b4e48217611365ed0ae598a74c6ac" +checksum = "8c02997ccef5f34f9c099277d4145f183b422938ed5322dc57a089fe9b9ad9ee" dependencies = [ "const-hex", "dunce", @@ -86,18 +110,204 @@ dependencies = [ "const-hex", ] +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -107,6 +317,12 @@ dependencies = [ "generic-array", ] +[[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.5.0" @@ -119,6 +335,15 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +[[package]] +name = "cc" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -138,6 +363,12 @@ dependencies = [ "serde", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" version = "0.6.0" @@ -168,6 +399,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -192,6 +435,16 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derivative" version = "2.2.0" @@ -203,6 +456,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -224,6 +488,15 @@ dependencies = [ "unicode-xid", ] +[[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.7" @@ -231,7 +504,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", ] [[package]] @@ -240,12 +515,118 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "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 = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "generic-array" version = "0.14.7" @@ -254,13 +635,36 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", ] [[package]] name = "glob" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] [[package]] name = "hashbrown" @@ -292,14 +696,53 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "indexmap" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "arbitrary", + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ - "equivalent", - "hashbrown 0.15.2", + "either", ] [[package]] @@ -308,6 +751,19 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + [[package]] name = "keccak" version = "0.1.5" @@ -317,6 +773,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "keccak-const" version = "0.2.0" @@ -341,6 +807,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -361,6 +833,8 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" name = "motsu" version = "0.3.0" dependencies = [ + "alloy-primitives", + "alloy-sol-types", "const-hex", "dashmap", "motsu-proc", @@ -382,6 +856,25 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -398,6 +891,32 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "parking_lot_core" version = "0.9.10" @@ -417,6 +936,27 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pest" +version = "2.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -426,6 +966,26 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -487,14 +1047,37 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ + "bit-set", + "bit-vec", "bitflags", + "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", "unarray", ] +[[package]] +name = "proptest-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "1.0.37" @@ -504,12 +1087,20 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[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", ] @@ -528,6 +1119,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "rand_xorshift" @@ -576,14 +1170,45 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[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 = "ruint" version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ + "alloy-rlp", + "arbitrary", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", "proptest", "rand", + "rlp", "ruint-macro", "serde", "valuable", @@ -596,6 +1221,64 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" +dependencies = [ + "rand", +] + +[[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.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.24", +] + +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.18" @@ -608,6 +1291,44 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" + +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.216" @@ -630,9 +1351,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -649,22 +1370,75 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "sha3" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stylus-proc" version = "0.7.0" @@ -702,6 +1476,12 @@ dependencies = [ "stylus-proc", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -726,9 +1506,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.18" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e89d8bf2768d277f40573c83a02a099e96d96dd3104e13ea676194e61ac4b0" +checksum = "219389c1ebe89f8333df8bdfb871f6631c552ff399c23cac02480b6088aad8f0" dependencies = [ "paste", "proc-macro2", @@ -736,12 +1516,31 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target-triple" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" +[[package]] +name = "tempfile" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -751,6 +1550,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -815,6 +1634,24 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[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 = "unarray" version = "0.1.4" @@ -851,6 +1688,21 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi-util" version = "0.1.9" @@ -935,13 +1787,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -968,3 +1829,17 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] diff --git a/crates/motsu-proc/src/test.rs b/crates/motsu-proc/src/test.rs index 2e88961..097eeb4 100644 --- a/crates/motsu-proc/src/test.rs +++ b/crates/motsu-proc/src/test.rs @@ -15,11 +15,6 @@ pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream { let fn_block = &item_fn.block; let fn_args = &sig.inputs; - // Currently, more than one contract per unit test is not supported. - if fn_args.len() > 1 { - error!(fn_args, "expected at most one contract in test signature"); - } - // Whether 1 or none contracts will be declared. let arg_binding_and_ty = match fn_args .into_iter() @@ -27,9 +22,9 @@ pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream { let FnArg::Typed(arg) = arg else { error!(@arg, "unexpected receiver argument in test signature"); }; - let contract_arg_binding = &arg.pat; - let contract_ty = &arg.ty; - Ok((contract_arg_binding, contract_ty)) + let arg_binding = &arg.pat; + let arg_ty = &arg.ty; + Ok((arg_binding, arg_ty)) }) .collect::, _>>() { @@ -37,35 +32,28 @@ pub(crate) fn test(_attr: &TokenStream, input: TokenStream) -> TokenStream { Err(err) => return err.to_compile_error().into(), }; - let contract_arg_defs = - arg_binding_and_ty.iter().map(|(arg_binding, contract_ty)| { - // Test case assumes, that contract's variable has `&mut` reference - // to contract's type. - quote! { - #arg_binding: &mut #contract_ty - } - }); + // Collect argument definitions. + let arg_defs = arg_binding_and_ty.iter().map(|(arg_binding, arg_ty)| { + quote! { + #arg_binding: #arg_ty + } + }); - let contract_args = - arg_binding_and_ty.iter().map(|(_arg_binding, contract_ty)| { - // Pass mutable reference to the contract. - quote! { - &mut <#contract_ty>::default() - } - }); + // Collect argument initializations. + let arg_inits = arg_binding_and_ty.iter().map(|(_arg_binding, arg_ty)| { + quote! { + <#arg_ty>::random() + } + }); // Declare test case closure. - // Pass mut ref to the test closure and call it. - // Reset storage for the test context and return test's output. + // Pass arguments to the test closure and call it. quote! { #( #attrs )* #[test] fn #fn_name() #fn_return_type { - use ::motsu::prelude::DefaultStorage; - let test = | #( #contract_arg_defs ),* | #fn_block; - let res = test( #( #contract_args ),* ); - ::motsu::prelude::Context::current().reset_storage(); - res + let test = | #( #arg_defs ),* | #fn_block; + test( #( #arg_inits ),* ) } } .into() diff --git a/crates/motsu/Cargo.toml b/crates/motsu/Cargo.toml index 5bfbe22..ec201b6 100644 --- a/crates/motsu/Cargo.toml +++ b/crates/motsu/Cargo.toml @@ -15,6 +15,8 @@ tiny-keccak.workspace = true stylus-sdk.workspace = true motsu-proc.workspace = true dashmap.workspace = true +alloy-primitives = { workspace = true, features = ["arbitrary", "rand"] } +alloy-sol-types.workspace = true [lints] workspace = true diff --git a/crates/motsu/src/context.rs b/crates/motsu/src/context.rs index 381d504..c742bbc 100644 --- a/crates/motsu/src/context.rs +++ b/crates/motsu/src/context.rs @@ -1,30 +1,41 @@ //! Unit-testing context for Stylus contracts. -use std::{collections::HashMap, ptr}; +use std::{collections::HashMap, ptr, slice, thread::ThreadId}; -use dashmap::DashMap; +use alloy_primitives::Address; +use dashmap::{mapref::one::RefMut, DashMap}; use once_cell::sync::Lazy; -use stylus_sdk::{alloy_primitives::uint, prelude::StorageType}; +use stylus_sdk::{alloy_primitives::uint, prelude::StorageType, ArbResult}; -use crate::prelude::{Bytes32, WORD_BYTES}; +use crate::{ + prelude::{Bytes32, WORD_BYTES}, + router::{RouterContext, TestRouter}, +}; + +/// Storage mock. +/// +/// A global mutable key-value store that allows concurrent access. +/// +/// The key is the test [`Context`], an id of the test thread. +/// +/// The value is the [`MockStorage`], a storage of the test case. +/// +/// NOTE: The [`DashMap`] will deadlock execution, when the same key is +/// accessed twice from the same thread. +static STORAGE: Lazy> = Lazy::new(DashMap::new); /// Context of stylus unit tests associated with the current test thread. #[allow(clippy::module_name_repetitions)] +#[derive(Hash, Eq, PartialEq, Copy, Clone)] pub struct Context { - thread_name: ThreadName, + thread_id: ThreadId, } impl Context { /// Get test context associated with the current test thread. #[must_use] pub fn current() -> Self { - Self { thread_name: ThreadName::current() } - } - - /// Get the value at `key` in storage. - pub(crate) fn get_bytes(self, key: &Bytes32) -> Bytes32 { - let storage = STORAGE.entry(self.thread_name).or_default(); - storage.contract_data.get(key).copied().unwrap_or_default() + Self { thread_id: std::thread::current().id() } } /// Get the raw value at `key` in storage and write it to `value`. @@ -34,10 +45,18 @@ impl Context { write_bytes32(value, self.get_bytes(&key)); } - /// Set the value at `key` in storage to `value`. - pub(crate) fn set_bytes(self, key: Bytes32, value: Bytes32) { - let mut storage = STORAGE.entry(self.thread_name).or_default(); - storage.contract_data.insert(key, value); + /// Get the value at `key` in storage. + fn get_bytes(self, key: &Bytes32) -> Bytes32 { + let storage = self.storage(); + let contract_address = + storage.contract_address.expect("contract_address should be set"); + storage + .contract_data + .get(&contract_address) + .expect("contract receiver should have a storage initialised") + .get(key) + .copied() + .unwrap_or_default() } /// Set the raw value at `key` in storage to `value`. @@ -46,41 +65,192 @@ impl Context { self.set_bytes(key, value); } - /// Clears storage, removing all key-value pairs associated with the current - /// test thread. - pub fn reset_storage(self) { - STORAGE.remove(&self.thread_name); + /// Set the value at `key` in storage to `value`. + fn set_bytes(self, key: Bytes32, value: Bytes32) { + let mut storage = self.storage(); + let contract_address = + storage.contract_address.expect("contract_address should be set"); + storage + .contract_data + .get_mut(&contract_address) + .expect("contract receiver should have a storage initialised") + .insert(key, value); } -} -/// Storage mock: A global mutable key-value store. -/// Allows concurrent access. -/// -/// The key is the name of the test thread, and the value is the storage of the -/// test case. -static STORAGE: Lazy> = - Lazy::new(DashMap::new); + /// Set the message sender address. + fn set_msg_sender(self, msg_sender: Address) -> Option
{ + self.storage().msg_sender.replace(msg_sender) + } -/// Test thread name metadata. -#[derive(Clone, Eq, PartialEq, Hash)] -struct ThreadName(String); + /// Get the message sender address. + #[must_use] + pub fn msg_sender(self) -> Option
{ + self.storage().msg_sender + } -impl ThreadName { - /// Get the name of the current test thread. - fn current() -> Self { - let current_thread_name = std::thread::current() - .name() - .expect("should retrieve current thread name") - .to_string(); - Self(current_thread_name) + /// Set the address of the contract, that is called. + fn set_contract_address(self, address: Address) -> Option
{ + self.storage().contract_address.replace(address) } -} -/// Storage for unit test's mock data. -#[derive(Default)] -struct MockStorage { - /// Contract's mock data storage. - contract_data: HashMap, + /// Get the address of the contract, that is called. + pub(crate) fn contract_address(self) -> Option
{ + self.storage().contract_address + } + + /// Initialise contract's storage for the current test thread and + /// `contract_address`. + fn init_storage( + self, + contract_address: Address, + ) { + if STORAGE + .entry(self) + .or_default() + .contract_data + .insert(contract_address, HashMap::new()) + .is_some() + { + panic!("contract storage already initialized for contract_address `{contract_address}`"); + } + + self.router(contract_address).init_storage::(); + } + + /// Reset storage for the current [`Context`] and `contract_address`. + /// + /// If all test contracts are removed, flush storage for the current + /// test [`Context`]. + fn reset_storage(self, contract_address: Address) { + let mut storage = self.storage(); + storage.contract_data.remove(&contract_address); + + // if no more contracts left, + if storage.contract_data.is_empty() { + // drop guard to a concurrent hash map to avoid a deadlock, + drop(storage); + // and erase the test context. + let _ = STORAGE.remove(&self); + } + + self.router(contract_address).reset_storage(); + } + + /// Call the contract at raw `address` with the given raw `calldata`. + pub(crate) unsafe fn call_contract_raw( + self, + address: *const u8, + calldata: *const u8, + calldata_len: usize, + return_data_len: *mut usize, + ) -> u8 { + let address_bytes = slice::from_raw_parts(address, 20); + let address = Address::from_slice(address_bytes); + + let input = slice::from_raw_parts(calldata, calldata_len); + let selector = + u32::from_be_bytes(TryInto::try_into(&input[..4]).unwrap()); + + match self.call_contract(address, selector, &input[4..]) { + Ok(res) => { + return_data_len.write(res.len()); + self.set_return_data(res); + 0 + } + Err(err) => { + return_data_len.write(err.len()); + self.set_return_data(err); + 1 + } + } + } + + /// Call the function associated with the given `selector` and pass `input` + /// to it, at the given `contract_address`. + fn call_contract( + self, + contract_address: Address, + selector: u32, + input: &[u8], + ) -> ArbResult { + // Set the caller contract as message sender and callee contract as + // a receiver (`contract_address`). + let previous_contract_address = self + .set_contract_address(contract_address) + .expect("contract_address should be set"); + let previous_msg_sender = self + .set_msg_sender(previous_contract_address) + .expect("msg_sender should be set"); + + // Call external contract. + let result = self + .router(contract_address) + .route(selector, input) + .unwrap_or_else(|| { + panic!("selector not found - selector is {selector}") + }); + + // Set the previous message sender and contract address back. + let _ = self.set_contract_address(previous_contract_address); + let _ = self.set_msg_sender(previous_msg_sender); + + result + } + + /// Set return data as bytes. + fn set_return_data(self, data: Vec) { + let mut call_storage = self.storage(); + let _ = call_storage.call_output_len.insert(data.len()); + let _ = call_storage.call_output.insert(data); + } + + /// Read the return data (with a given `size`) from the last contract call + /// to the `dest` pointer. + pub(crate) unsafe fn read_return_data_raw( + self, + dest: *mut u8, + size: usize, + ) -> usize { + let data = self.return_data(); + ptr::copy(data.as_ptr(), dest, size); + data.len() + } + + /// Return data's size in bytes from the last contract call. + pub(crate) fn return_data_size(self) -> usize { + self.storage() + .call_output_len + .take() + .expect("call_output_len should be set") + } + + /// Return data's bytes from the last contract call. + fn return_data(self) -> Vec { + self.storage().call_output.take().expect("call_output should be set") + } + + /// Check if the contract at raw `address` has code. + pub(crate) unsafe fn has_code_raw(self, address: *const u8) -> bool { + let address_bytes = slice::from_raw_parts(address, 20); + let address = Address::from_slice(address_bytes); + self.has_code(address) + } + + /// Check if the contract at `address` has code. + #[must_use] + fn has_code(self, address: Address) -> bool { + self.router(address).exists() + } + + /// Get reference to the storage for the current test thread. + fn storage(self) -> RefMut<'static, Context, MockStorage> { + STORAGE.get_mut(&self).expect("contract should be initialised first") + } + + /// Get router for the contract at `address`. + fn router(self, address: Address) -> RouterContext { + RouterContext::new(self.thread_id, address) + } } /// Read the word from location pointed by `ptr`. @@ -95,15 +265,162 @@ unsafe fn write_bytes32(ptr: *mut u8, bytes: Bytes32) { ptr::copy(bytes.as_ptr(), ptr, WORD_BYTES); } -/// Initializes fields of contract storage and child contract storages with -/// default values. -pub trait DefaultStorage: StorageType { - /// Initializes fields of contract storage and child contract storages with - /// default values. - #[must_use] +/// Storage for unit test's mock data. +#[derive(Default)] +struct MockStorage { + /// Address of the message sender. + msg_sender: Option
, + /// Address of the contract that is currently receiving the message. + contract_address: Option
, + /// Contract's address to mock data storage mapping. + contract_data: HashMap, + // Output of a contract call. + call_output: Option>, + // Output length of a contract call. + call_output_len: Option, +} + +type ContractStorage = HashMap; + +/// Contract call entity, related to the contract type `ST` and the caller's +/// account. +pub struct ContractCall<'a, ST: StorageType> { + storage: ST, + caller_address: Address, + /// We need to hold a reference to [`Contract`], because + /// `Contract::::new().sender(alice)` can accidentally drop + /// [`Contract`]. + /// + /// With `contract_ref` code like: `Contract::::new().sender(alice)` + /// will not compile. + contract_ref: &'a Contract, +} + +impl ContractCall<'_, ST> { + /// Get the contract's address. + pub fn address(&self) -> Address { + self.contract_ref.address + } + + /// Preset the call parameters. + fn set_call_params(&self) { + let _ = Context::current().set_msg_sender(self.caller_address); + let _ = Context::current().set_contract_address(self.address()); + } +} + +impl ::core::ops::Deref for ContractCall<'_, ST> { + type Target = ST; + + #[inline] + fn deref(&self) -> &Self::Target { + self.set_call_params(); + &self.storage + } +} + +impl ::core::ops::DerefMut for ContractCall<'_, ST> { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.set_call_params(); + &mut self.storage + } +} + +/// Contract deployed in the test environment. +pub struct Contract { + phantom: ::core::marker::PhantomData, + address: Address, +} + +impl Drop for Contract { + fn drop(&mut self) { + Context::current().reset_storage(self.address); + } +} + +impl Default for Contract { fn default() -> Self { - unsafe { Self::new(uint!(0_U256), 0) } + Contract::new_at(Address::default()) + } +} + +impl Contract { + /// Create a new contract with default storage on the random address. + #[must_use] + pub fn new() -> Self { + Self::random() + } + + /// Create a new contract with the given `address`. + #[must_use] + pub fn new_at(address: Address) -> Self { + Context::current().init_storage::(address); + + Self { phantom: ::core::marker::PhantomData, address } + } + + /// Initialize the contract with an `initializer` function, and on behalf of + /// the given `account`. + pub fn init, Output>( + &self, + account: A, + initializer: impl FnOnce(&mut ST) -> Output, + ) -> Output { + initializer(&mut self.sender(account.into())) + } + + /// Create a new contract with default storage on the random address. + #[must_use] + pub fn random() -> Self { + Self::new_at(Address::random()) + } + + /// Get contract's test address. + #[must_use] + pub fn address(&self) -> Address { + self.address + } + + /// Call contract `self` with `account` as a sender. + #[must_use] + pub fn sender>(&self, account: A) -> ContractCall { + ContractCall { + storage: unsafe { ST::new(uint!(0_U256), 0) }, + caller_address: account.into(), + contract_ref: self, + } } } -impl DefaultStorage for ST {} +/// Account used to call contracts. +#[derive(Clone, Copy)] +pub struct Account { + address: Address, +} + +impl From for Address { + fn from(value: Account) -> Self { + value.address + } +} + +impl Account { + /// Create a new account with the given `address`. + #[must_use] + pub const fn new_at(address: Address) -> Self { + Self { address } + } + + /// Create a new account with random address. + #[must_use] + pub fn random() -> Self { + Self::new_at(Address::random()) + } + + /// Get account's address. + #[must_use] + pub fn address(&self) -> Address { + self.address + } +} diff --git a/crates/motsu/src/lib.rs b/crates/motsu/src/lib.rs index 7636b75..620f5a6 100644 --- a/crates/motsu/src/lib.rs +++ b/crates/motsu/src/lib.rs @@ -19,11 +19,16 @@ //! ```rust //! #[cfg(test)] //! mod tests { -//! use contracts::token::erc20::Erc20; +//! use openzeppelin_stylus::token::erc20::Erc20; +//! use motsu::prelude::{Account, Contract}; +//! use stylus_sdk::alloy_primitives::{Address, U256}; //! //! #[motsu::test] -//! fn reads_balance(contract: Erc20) { -//! let balance = contract.balance_of(Address::ZERO); // Access storage. +//! fn reads_balance( +//! contract: Contract, +//! alice: Account, +//! ) { +//! let balance = contract.sender(alice).balance_of(Address::ZERO); // Access storage. //! assert_eq!(balance, U256::ZERO); //! } //! } @@ -43,8 +48,292 @@ //! ``` //! //! [test_attribute]: crate::test +#[cfg(test)] +extern crate alloc; mod context; pub mod prelude; +mod router; mod shims; pub use motsu_proc::test; + +#[cfg(test)] +mod ping_pong_tests { + #![deny(rustdoc::broken_intra_doc_links)] + use alloy_primitives::uint; + use alloy_sol_types::{sol, SolError}; + use stylus_sdk::{ + alloy_primitives::{Address, U256}, + call::Call, + contract, msg, + prelude::{public, storage, AddressVM, SolidityError, TopLevelStorage}, + storage::{StorageAddress, StorageU256}, + }; + + use crate::context::{Account, Contract}; + + #[storage] + struct PingContract { + pings_count: StorageU256, + pinged_from: StorageAddress, + contract_address: StorageAddress, + } + + #[public] + impl PingContract { + fn ping(&mut self, to: Address, value: U256) -> Result> { + let receiver = IPongContract::new(to); + let call = Call::new_in(self); + let value = receiver.pong(call, value)?; + + let pings_count = self.pings_count.get(); + self.pings_count.set(pings_count + uint!(1_U256)); + + self.pinged_from.set(msg::sender()); + self.contract_address.set(contract::address()); + + Ok(value) + } + + fn can_ping(&mut self, to: Address) -> Result> { + let receiver = IPongContract::new(to); + let call = Call::new_in(self); + Ok(receiver.can_pong(call)?) + } + + fn has_pong(&self, to: Address) -> bool { + to.has_code() + } + } + + unsafe impl TopLevelStorage for PingContract {} + + stylus_sdk::stylus_proc::sol_interface! { + interface IPongContract { + #[allow(missing_docs)] + function pong(uint256 value) external returns (uint256); + + #[allow(missing_docs)] + function canPong() external view returns (bool); + } + } + + sol! { + #[derive(Debug)] + #[allow(missing_docs)] + error MagicError(uint256 value); + } + + #[derive(SolidityError, Debug)] + pub enum PongError { + MagicError(MagicError), + } + + const MAGIC_ERROR_VALUE: U256 = uint!(42_U256); + + #[storage] + struct PongContract { + pongs_count: StorageU256, + ponged_from: StorageAddress, + contract_address: StorageAddress, + } + + #[public] + impl PongContract { + fn pong(&mut self, value: U256) -> Result { + if value == MAGIC_ERROR_VALUE { + return Err(PongError::MagicError(MagicError { value })); + } + + let pongs_count = self.pongs_count.get(); + self.pongs_count.set(pongs_count + uint!(1_U256)); + + self.ponged_from.set(msg::sender()); + self.contract_address.set(contract::address()); + + Ok(value + uint!(1_U256)) + } + + fn can_pong(&self) -> bool { + true + } + } + + unsafe impl TopLevelStorage for PongContract {} + + #[motsu_proc::test] + fn external_call( + ping: Contract, + pong: Contract, + alice: Account, + ) { + let value = uint!(10_U256); + let ponged_value = ping + .sender(alice) + .ping(pong.address(), value) + .expect("should ping successfully"); + + assert_eq!(ponged_value, value + uint!(1_U256)); + assert_eq!(ping.sender(alice).pings_count.get(), uint!(1_U256)); + assert_eq!(pong.sender(alice).pongs_count.get(), uint!(1_U256)); + } + + #[motsu_proc::test] + fn external_call_error( + ping: Contract, + pong: Contract, + alice: Account, + ) { + let value = MAGIC_ERROR_VALUE; + let err = ping + .sender(alice) + .ping(pong.address(), value) + .expect_err("should fail to ping"); + + assert_eq!(err, MagicError { value }.abi_encode()); + } + + #[motsu_proc::test] + fn external_static_call( + ping: Contract, + pong: Contract, + alice: Account, + ) { + let can_ping = ping + .sender(alice) + .can_ping(pong.address()) + .expect("should ping successfully"); + assert!(can_ping); + } + + #[motsu_proc::test] + fn msg_sender( + ping: Contract, + pong: Contract, + alice: Account, + ) { + assert_eq!(ping.sender(alice).pinged_from.get(), Address::ZERO); + assert_eq!(pong.sender(alice).ponged_from.get(), Address::ZERO); + + let _ = ping + .sender(alice) + .ping(pong.address(), uint!(10_U256)) + .expect("should ping successfully"); + + assert_eq!(ping.sender(alice).pinged_from.get(), alice.address()); + assert_eq!(pong.sender(alice).ponged_from.get(), ping.address()); + } + + #[motsu_proc::test] + fn has_code( + ping: Contract, + pong: Contract, + alice: Account, + ) { + assert!(ping.sender(alice).has_pong(pong.address())); + } + + #[motsu_proc::test] + fn contract_address( + ping: Contract, + pong: Contract, + alice: Account, + ) { + assert_eq!(ping.sender(alice).contract_address.get(), Address::ZERO); + assert_eq!(pong.sender(alice).contract_address.get(), Address::ZERO); + + let _ = ping + .sender(alice) + .ping(pong.address(), uint!(10_U256)) + .expect("should ping successfully"); + + assert_eq!(ping.sender(alice).contract_address.get(), ping.address()); + assert_eq!(pong.sender(alice).contract_address.get(), pong.address()); + } + + #[motsu_proc::test] + fn contract_should_not_drop() { + let alice = Account::random(); + let ping = Contract::::new(); + let mut ping = ping.sender(alice); + let pong = Contract::::new(); + let pong = pong.sender(alice); + + let _ = ping + .ping(pong.address(), uint!(10_U256)) + .expect("contract ping should not drop"); + } +} + +#[cfg(test)] +mod proxies_tests { + use alloy_primitives::{uint, Address, U256}; + use stylus_sdk::{ + call::Call, + prelude::{public, storage, TopLevelStorage}, + storage::StorageAddress, + }; + + use crate::context::{Account, Contract}; + + stylus_sdk::stylus_proc::sol_interface! { + interface IProxy { + #[allow(missing_docs)] + function callProxy(uint256 value) external returns (uint256); + } + } + + #[storage] + struct Proxy { + next_proxy: StorageAddress, + } + + #[public] + impl Proxy { + fn call_proxy(&mut self, value: U256) -> U256 { + let next_proxy = self.next_proxy.get(); + + // Add one to the value. + let value = value + uint!(1_U256); + + // If there is no next proxy, return the value. + if next_proxy.is_zero() { + value + } else { + // Otherwise, call the next proxy. + let proxy = IProxy::new(next_proxy); + let call = Call::new_in(self); + proxy.call_proxy(call, value).expect("should call proxy") + } + } + } + + unsafe impl TopLevelStorage for Proxy {} + + #[motsu_proc::test] + fn three_proxies( + proxy1: Contract, + proxy2: Contract, + proxy3: Contract, + alice: Account, + ) { + // Set up a chain of three proxies. + // With the given call chain: proxy1 -> proxy2 -> proxy3. + proxy1.init(alice, |storage| { + storage.next_proxy.set(proxy2.address()); + }); + proxy2.init(alice, |storage| { + storage.next_proxy.set(proxy3.address()); + }); + proxy3.init(alice, |storage| { + storage.next_proxy.set(Address::ZERO); + }); + + // Call the first proxy. + let value = uint!(10_U256); + let result = proxy1.sender(alice).call_proxy(value); + + // The value is incremented by 1 for each proxy. + assert_eq!(result, value + uint!(3_U256)); + } +} diff --git a/crates/motsu/src/prelude.rs b/crates/motsu/src/prelude.rs index 5c87982..a881a05 100644 --- a/crates/motsu/src/prelude.rs +++ b/crates/motsu/src/prelude.rs @@ -1,5 +1,5 @@ //! Common imports for `motsu` tests. pub use crate::{ - context::{Context, DefaultStorage}, + context::{Account, Context, Contract, ContractCall}, shims::*, }; diff --git a/crates/motsu/src/router.rs b/crates/motsu/src/router.rs new file mode 100644 index 0000000..f12ea20 --- /dev/null +++ b/crates/motsu/src/router.rs @@ -0,0 +1,116 @@ +//! Router context for external calls mocks. +//! +//! NOTE: [`ROUTER_STORAGE`] should be separated from the main test storage to +//! avoid deadlocks. + +use std::{borrow::BorrowMut, sync::Mutex, thread::ThreadId}; + +use alloy_primitives::{uint, Address}; +use dashmap::{mapref::one::RefMut, DashMap}; +use once_cell::sync::Lazy; +use stylus_sdk::{ + abi::Router, + prelude::{StorageType, TopLevelStorage}, + ArbResult, +}; + +/// Router Storage. +/// +/// A global mutable key-value store that allows concurrent access. +/// +/// The key is the [`RouterContext`], a combination of [`ThreadId`] and +/// [`Address`] to avoid a deadlock, while calling more than two contracts +/// consecutive. +/// +/// The value is the [`RouterStorage`], a router of the contract generated by +/// `stylus-sdk`. +/// +/// NOTE: The [`DashMap`] will deadlock execution, when the same key is +/// accessed twice from the same thread. +static ROUTER_STORAGE: Lazy> = + Lazy::new(DashMap::new); + +/// Context for the router of a test contract for current test thread and +/// contract's address. +#[derive(Hash, Eq, PartialEq, Copy, Clone)] +pub(crate) struct RouterContext { + thread_id: ThreadId, + contract_address: Address, +} + +impl RouterContext { + /// Create a new router context. + pub(crate) fn new(thread: ThreadId, contract_address: Address) -> Self { + Self { thread_id: thread, contract_address } + } + + /// Get reference to the call storage for the current test thread. + fn storage(self) -> RefMut<'static, RouterContext, RouterStorage> { + ROUTER_STORAGE + .get_mut(&self) + .expect("contract should be initialised first") + } + + /// Check if the router exists for the contract. + pub(crate) fn exists(self) -> bool { + ROUTER_STORAGE.contains_key(&self) + } + + pub(crate) fn route( + self, + selector: u32, + input: &[u8], + ) -> Option { + let router = &self.storage().router; + let mut router = router.lock().expect("should lock test router"); + router.route(selector, input) + } + + /// Initialise contract router for the current test thread and + /// `contract_address`. + pub(crate) fn init_storage(self) { + let contract_address = self.contract_address; + if ROUTER_STORAGE + .insert( + self, + RouterStorage { + router: Mutex::new(Box::new(unsafe { + ST::new(uint!(0_U256), 0) + })), + }, + ) + .is_some() + { + panic!("contract's router is already initialized - contract_address is {contract_address}"); + } + } + + /// Reset router storage for the current [`RouterContext`]. + pub(crate) fn reset_storage(self) { + ROUTER_STORAGE.remove(&self); + } +} + +/// Metadata related to the router of an external contract. +struct RouterStorage { + // Contract's router. + // NOTE: Mutex is important since contract type is not `Sync`. + router: Mutex>, +} + +/// A trait for routing messages to the appropriate selector in tests. +#[allow(clippy::module_name_repetitions)] +pub trait TestRouter: Send { + /// Tries to find and execute a method for the given `selector`, returning + /// `None` if the `selector` wasn't found. + fn route(&mut self, selector: u32, input: &[u8]) -> Option; +} + +impl TestRouter for R +where + R: Router + TopLevelStorage + BorrowMut + Send, +{ + fn route(&mut self, selector: u32, input: &[u8]) -> Option { + >::route(self, selector, input) + } +} diff --git a/crates/motsu/src/shims.rs b/crates/motsu/src/shims.rs index 162f9e2..95c639a 100644 --- a/crates/motsu/src/shims.rs +++ b/crates/motsu/src/shims.rs @@ -112,24 +112,23 @@ pub unsafe extern "C" fn storage_cache_bytes32( /// of [`SSTORE`]. /// /// [`SSTORE`]: https://www.evm.codes/#55 -pub fn storage_flush_cache(_: bool) { +#[no_mangle] +pub unsafe extern "C" fn storage_flush_cache(_: bool) { // No-op: we don't use the cache in our unit-tests. } -/// Dummy msg sender set for tests. -pub const MSG_SENDER: &[u8; 42] = b"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"; - -/// Dummy contract address set for tests. -pub const CONTRACT_ADDRESS: &[u8; 42] = - b"0xdCE82b5f92C98F27F116F70491a487EFFDb6a2a9"; - /// Arbitrum's CHAID ID. pub const CHAIN_ID: u64 = 42161; -/// Externally Owned Account (EOA) code hash. +/// Externally Owned Account (EOA) code hash (wallet account). pub const EOA_CODEHASH: &[u8; 66] = b"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; +/// Contract Account (CA) code hash (smart contract code). +/// NOTE: can be any 256-bit value to pass `has_code` check. +pub const CA_CODEHASH: &[u8; 66] = + b"0x1111111111111111111111111111111111111111111111111111111111111111"; + /// Gets the address of the account that called the program. /// /// For normal L2-to-L2 transactions the semantics are equivalent to that of the @@ -148,8 +147,16 @@ pub const EOA_CODEHASH: &[u8; 66] = /// May panic if fails to parse `MSG_SENDER` as an address. #[no_mangle] pub unsafe extern "C" fn msg_sender(sender: *mut u8) { - let addr = const_hex::const_decode_to_array::<20>(MSG_SENDER).unwrap(); - std::ptr::copy(addr.as_ptr(), sender, 20); + let msg_sender = + Context::current().msg_sender().expect("msg_sender should be set"); + std::ptr::copy(msg_sender.as_ptr(), sender, 20); +} + +/// Get the ETH value (U256) in wei sent to the program. +#[no_mangle] +pub unsafe extern "C" fn msg_value(value: *mut u8) { + let dummy_msg_value: Bytes32 = Bytes32::default(); + std::ptr::copy(dummy_msg_value.as_ptr(), value, 32); } /// Gets the address of the current program. The semantics are equivalent to @@ -162,9 +169,10 @@ pub unsafe extern "C" fn msg_sender(sender: *mut u8) { /// May panic if fails to parse `CONTRACT_ADDRESS` as an address. #[no_mangle] pub unsafe extern "C" fn contract_address(address: *mut u8) { - let addr = - const_hex::const_decode_to_array::<20>(CONTRACT_ADDRESS).unwrap(); - std::ptr::copy(addr.as_ptr(), address, 20); + let contract_address = Context::current() + .contract_address() + .expect("contract_address should be set"); + std::ptr::copy(contract_address.as_ptr(), address, 20); } /// Gets the chain ID of the current chain. The semantics are equivalent to @@ -206,9 +214,15 @@ pub unsafe extern "C" fn emit_log(_: *const u8, _: usize, _: usize) { /// /// May panic if fails to parse `ACCOUNT_CODEHASH` as a keccack hash. #[no_mangle] -pub unsafe extern "C" fn account_codehash(_address: *const u8, dest: *mut u8) { +pub unsafe extern "C" fn account_codehash(address: *const u8, dest: *mut u8) { + let code_hash = if Context::current().has_code_raw(address) { + CA_CODEHASH + } else { + EOA_CODEHASH + }; + let account_codehash = - const_hex::const_decode_to_array::<32>(EOA_CODEHASH).unwrap(); + const_hex::const_decode_to_array::<32>(code_hash).unwrap(); std::ptr::copy(account_codehash.as_ptr(), dest, 32); } @@ -222,10 +236,7 @@ pub unsafe extern "C" fn account_codehash(_address: *const u8, dest: *mut u8) { /// [`RETURN_DATA_SIZE`]: https://www.evm.codes/#3d #[no_mangle] pub unsafe extern "C" fn return_data_size() -> usize { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 + Context::current().return_data_size() } /// Copies the bytes of the last EVM call or deployment return result. @@ -239,14 +250,11 @@ pub unsafe extern "C" fn return_data_size() -> usize { /// [`RETURN_DATA_COPY`]: https://www.evm.codes/#3e #[no_mangle] pub unsafe extern "C" fn read_return_data( - _dest: *mut u8, + dest: *mut u8, _offset: usize, - _size: usize, + size: usize, ) -> usize { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 + Context::current().read_return_data_raw(dest, size) } /// Calls the contract at the given address with options for passing value and @@ -265,17 +273,19 @@ pub unsafe extern "C" fn read_return_data( /// [`CALL`]: https://www.evm.codes/#f1 #[no_mangle] pub unsafe extern "C" fn call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, + contract: *const u8, + calldata: *const u8, + calldata_len: usize, _value: *const u8, _gas: u64, - _return_data_len: *mut usize, + return_data_len: *mut usize, ) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 + Context::current().call_contract_raw( + contract, + calldata, + calldata_len, + return_data_len, + ) } /// Static calls the contract at the given address, with the option to limit the @@ -294,16 +304,18 @@ pub unsafe extern "C" fn call_contract( /// [`STATIC_CALL`]: https://www.evm.codes/#FA #[no_mangle] pub unsafe extern "C" fn static_call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, + contract: *const u8, + calldata: *const u8, + calldata_len: usize, _gas: u64, - _return_data_len: *mut usize, + return_data_len: *mut usize, ) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 + Context::current().call_contract_raw( + contract, + calldata, + calldata_len, + return_data_len, + ) } /// Delegate calls the contract at the given address, with the option to limit @@ -322,16 +334,18 @@ pub unsafe extern "C" fn static_call_contract( /// [`DELEGATE_CALL`]: https://www.evm.codes/#F4 #[no_mangle] pub unsafe extern "C" fn delegate_call_contract( - _contract: *const u8, - _calldata: *const u8, - _calldata_len: usize, + contract: *const u8, + calldata: *const u8, + calldata_len: usize, _gas: u64, - _return_data_len: *mut usize, + return_data_len: *mut usize, ) -> u8 { - // TODO: #156 - // No-op: we do not use this function in our unit-tests, - // but the binary does include it. - 0 + Context::current().call_contract_raw( + contract, + calldata, + calldata_len, + return_data_len, + ) } /// Gets a bounded estimate of the Unix timestamp at which the Sequencer