diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 7cea2c730..184ef0961 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -50,6 +50,8 @@ jobs: run: | wasm-opt --version cargo -vV + cargo install cargo-dylint dylint-link + cargo dylint --version cargo run -- contract --version cargo run -- contract new foobar echo "[workspace]" >> foobar/Cargo.toml diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index e9a179bf3..087efe73a 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -53,6 +53,8 @@ jobs: run: | wasm-opt --version cargo -vV + cargo install cargo-dylint dylint-link + cargo dylint --version cargo run -- contract --version cargo run -- contract new foobar echo "[workspace]" >> foobar/Cargo.toml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 99f0db2bc..87258d9e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,11 +39,17 @@ workflow: - cargo -vV - rustc -vV - rustup show + - cargo dylint --version - bash --version - ./scripts/ci/pre_cache.sh # global RUSTFLAGS overrides the linker args so this way is better to pass the flags - printf '[build]\nrustflags = ["-C", "link-dead-code"]\n' > ${CARGO_HOME}/config - sccache -s + + # needed until https://github.com/mozilla/sccache/issues/1000 is fixed. + # this is unfortunate, since it disables `sccache` for this entire file. + - unset RUSTC_WRAPPER + - git show rules: - if: $CI_PIPELINE_SOURCE == "web" @@ -81,6 +87,26 @@ clippy: #### stage: test (all features) +test-dylint: + stage: test + <<: *docker-env + script: + - cd ink_linting/ + + # Installing these components here is necessary because + # `ink_linting/` has a fixed `rust-toolchain` file. + # We can't move this line to the Docker container, since + # that would then make it impossible to upgrade the + # `ink_linting/rust-toolchain` file while still having + # this CI job succeed. + - rustup component add rustfmt clippy rust-src + + - cargo check --verbose + - cargo fmt --verbose --all -- --check + - cargo clippy --verbose -- -D warnings; + + - cargo test --verbose --workspace --all-features + test: stage: test <<: *docker-env diff --git a/Cargo.lock b/Cargo.lock index b248953eb..46d2e2db4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.4", "once_cell", "version_check", ] @@ -234,9 +234,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.0.3" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" +checksum = "677d306121baf53310a3fd342d88dc0824f6bbeace68347593658525565abee8" [[package]] name = "async-trait" @@ -268,15 +268,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" dependencies = [ "addr2line", "cc", @@ -332,7 +332,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94ba84325db59637ffc528bbe8c7f86c02c57cff5c0e2b9b00f9a851f42f309" dependencies = [ - "digest 0.10.1", + "digest 0.10.3", ] [[package]] @@ -363,16 +363,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] name = "block-buffer" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] @@ -421,9 +421,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.8.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "byte-slice-cast" @@ -457,9 +457,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "camino" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d74260d9bf6944e2208aa46841b4b8f0d7ffc0849a06837b2f510337f86b2b" +checksum = "6f3132262930b0522068049f5870a856ab8affc80c70d08b6ecb785771a6fc23" dependencies = [ "serde", ] @@ -531,9 +531,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ae6de944143141f6155a473a6b02f66c7c3f9f47316f802f80204ebfe6e12" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", @@ -553,9 +553,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -598,9 +598,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.46" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b858541263efe664aead4a5209a4ae5c5d2811167d4ed4ee0944503f8d2089" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" dependencies = [ "cc", ] @@ -651,9 +651,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -676,9 +676,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] @@ -721,9 +721,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" dependencies = [ "cfg-if", "crossbeam-utils", @@ -742,9 +742,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" dependencies = [ "cfg-if", "crossbeam-utils", @@ -755,9 +755,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ "cfg-if", "lazy_static", @@ -771,11 +771,12 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", + "typenum", ] [[package]] @@ -784,7 +785,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", "subtle", ] @@ -794,7 +795,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", "subtle", ] @@ -942,18 +943,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.4", + "generic-array 0.14.5", ] [[package]] name = "digest" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" dependencies = [ - "block-buffer 0.10.0", + "block-buffer 0.10.2", "crypto-common", - "generic-array 0.14.4", "subtle", ] @@ -1015,7 +1015,7 @@ dependencies = [ "ed25519", "rand 0.7.3", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "zeroize", ] @@ -1055,9 +1055,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" [[package]] name = "fake-simd" @@ -1067,9 +1067,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastrand" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" dependencies = [ "instant", ] @@ -1081,7 +1081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ "byteorder", - "rand 0.8.4", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -1245,9 +1245,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", "version_check", @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" dependencies = [ "cfg-if", "libc", @@ -1291,15 +1291,14 @@ checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" [[package]] name = "gloo-timers" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f16c88aa13d2656ef20d1c042086b8767bbe2bdb62526894275a1b062161b2e" +checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e" dependencies = [ "futures-channel", "futures-core", "js-sys", "wasm-bindgen", - "web-sys", ] [[package]] @@ -1332,6 +1331,15 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +dependencies = [ + "ahash", +] + [[package]] name = "heck" version = "0.3.3" @@ -1389,19 +1397,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.4", + "generic-array 0.14.5", "hmac 0.8.1", ] [[package]] name = "http" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" dependencies = [ "bytes", "fnv", - "itoa 0.4.8", + "itoa 1.0.1", ] [[package]] @@ -1417,9 +1425,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" [[package]] name = "httpdate" @@ -1435,9 +1443,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.16" +version = "0.14.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd" dependencies = [ "bytes", "futures-channel", @@ -1447,7 +1455,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 0.4.8", + "itoa 1.0.1", "pin-project-lite", "tokio", "tower-service", @@ -1492,9 +1500,9 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", @@ -1514,7 +1522,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", ] [[package]] @@ -1543,7 +1551,7 @@ dependencies = [ "num-traits", "parity-scale-codec", "paste", - "rand 0.8.4", + "rand 0.8.5", "rlibc", "scale-info", "secp256k1", @@ -1739,9 +1747,9 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" [[package]] name = "js-sys" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" dependencies = [ "wasm-bindgen", ] @@ -1902,9 +1910,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.112" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" +checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" [[package]] name = "libsecp256k1" @@ -1919,9 +1927,9 @@ dependencies = [ "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand 0.8.4", + "rand 0.8.5", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "typenum", ] @@ -1956,9 +1964,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" dependencies = [ "scopeguard", ] @@ -2010,7 +2018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de006e09d04fc301a5f7e817b75aa49801c4479a8af753764416b085337ddcc5" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.11.2", "parity-util-mem", ] @@ -2021,7 +2029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d505169b746dacf02f7d14d8c80b34edfd8212159c63d23c977739a0d960c626" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.11.2", "parity-util-mem", ] @@ -2061,9 +2069,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.14" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" dependencies = [ "libc", "log", @@ -2119,9 +2127,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "ntapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi", ] @@ -2213,15 +2221,15 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl-probe" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "output_vt100" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] @@ -2262,7 +2270,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" dependencies = [ - "proc-macro-crate 1.1.0", + "proc-macro-crate 1.1.2", "proc-macro2", "quote", "syn", @@ -2275,7 +2283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f4cb4e169446179cbc6b8b6320cc9fca49bd2e94e8db25f25f200a8ea774770" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.11.2", "impl-trait-for-tuples", "parity-util-mem-derive", "parking_lot", @@ -2363,18 +2371,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" dependencies = [ "proc-macro2", "quote", @@ -2383,9 +2391,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" [[package]] name = "pin-utils" @@ -2448,9 +2456,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "predicates" @@ -2468,15 +2476,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" +checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" [[package]] name = "predicates-tree" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" dependencies = [ "predicates-core", "termtree", @@ -2518,9 +2526,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" +checksum = "9dada8c9981fcf32929c3c0f0cd796a9284aca335565227ed88c83babb1d43dc" dependencies = [ "thiserror", "toml", @@ -2552,18 +2560,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.34" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.10" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ "proc-macro2", ] @@ -2584,20 +2592,19 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", + "rand_hc", "rand_pcg", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.3", - "rand_hc 0.3.1", ] [[package]] @@ -2635,7 +2642,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.4", ] [[package]] @@ -2647,15 +2654,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.3", -] - [[package]] name = "rand_pcg" version = "0.2.1" @@ -2804,9 +2802,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" +checksum = "b323592e3164322f5b193dc4302e4e36cd8d37158a712d664efae1a5c2791700" dependencies = [ "log", "ring", @@ -2876,7 +2874,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baeb2780690380592f86205aa4ee49815feb2acad8c2f59e6dd207148c3f1fcd" dependencies = [ - "proc-macro-crate 1.1.0", + "proc-macro-crate 1.1.2", "proc-macro2", "quote", "syn", @@ -2955,9 +2953,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.4.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags", "core-foundation", @@ -2968,9 +2966,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.4.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", @@ -3053,9 +3051,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", @@ -3072,7 +3070,7 @@ checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.1", + "digest 0.10.3", ] [[package]] @@ -3081,7 +3079,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f935e31cf406e8c0e96c2815a5516181b7004ae8c5f296293221e9b1e356bd" dependencies = [ - "digest 0.10.1", + "digest 0.10.3", "keccak", ] @@ -3096,9 +3094,9 @@ dependencies = [ [[package]] name = "signature" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" +checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" [[package]] name = "slab" @@ -3108,15 +3106,15 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" [[package]] name = "smallvec" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "socket2" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" dependencies = [ "libc", "winapi", @@ -3133,7 +3131,7 @@ dependencies = [ "futures", "httparse", "log", - "rand 0.8.4", + "rand 0.8.5", "sha-1", ] @@ -3213,7 +3211,7 @@ dependencies = [ "schnorrkel", "secrecy", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "sp-core-hashing", "sp-debug-derive", "sp-externalities 0.10.0", @@ -3287,7 +3285,7 @@ checksum = "ec864a6a67249f0c8dd3d5acab43623a61677e85ff4f2f9b04b802d2fe780e83" dependencies = [ "blake2-rfc", "byteorder", - "sha2 0.9.8", + "sha2 0.9.9", "sp-std", "tiny-keccak", "twox-hash", @@ -3523,7 +3521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b58cc6060b2d2f35061db5b4172f4a47353c3f01a89f281699a6c3f05d1267a" dependencies = [ "Inflector", - "proc-macro-crate 1.1.0", + "proc-macro-crate 1.1.2", "proc-macro2", "quote", "syn", @@ -3573,7 +3571,7 @@ dependencies = [ "sp-trie 5.0.0", "thiserror", "tracing", - "trie-db 0.23.0", + "trie-db 0.23.1", "trie-root 0.17.0", ] @@ -3652,7 +3650,7 @@ dependencies = [ "scale-info", "sp-core 5.0.0", "sp-std", - "trie-db 0.23.0", + "trie-db 0.23.1", "trie-root 0.17.0", ] @@ -3718,9 +3716,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "ss58-registry" -version = "1.12.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8319f44e20b42e5c11b88b1ad4130c35fe2974665a007b08b02322070177136a" +checksum = "d8cb4b9ce18beb6cb16ecad62d936245cef5212ddc8e094d7417a75e8d0e85f5" dependencies = [ "Inflector", "proc-macro2", @@ -3781,7 +3779,7 @@ dependencies = [ "hmac 0.11.0", "pbkdf2 0.8.0", "schnorrkel", - "sha2 0.9.8", + "sha2 0.9.9", "zeroize", ] @@ -3868,9 +3866,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ "proc-macro2", "quote", @@ -3920,9 +3918,9 @@ dependencies = [ [[package]] name = "termtree" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" [[package]] name = "textwrap" @@ -3955,9 +3953,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" dependencies = [ "once_cell", ] @@ -3974,7 +3972,7 @@ dependencies = [ "pbkdf2 0.4.0", "rand 0.7.3", "rustc-hash", - "sha2 0.9.8", + "sha2 0.9.9", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -4017,14 +4015,15 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" dependencies = [ "libc", "mio", "num_cpus", "pin-project-lite", + "socket2", "tokio-macros", "winapi", ] @@ -4083,9 +4082,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" dependencies = [ "cfg-if", "pin-project-lite", @@ -4095,9 +4094,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" dependencies = [ "proc-macro2", "quote", @@ -4106,11 +4105,12 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" dependencies = [ "lazy_static", + "valuable", ] [[package]] @@ -4126,9 +4126,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" dependencies = [ "serde", "tracing-core", @@ -4163,7 +4163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eac131e334e81b6b3be07399482042838adcd7957aa0010231d0813e39e02fa" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.11.2", "log", "rustc-hex", "smallvec", @@ -4171,12 +4171,12 @@ dependencies = [ [[package]] name = "trie-db" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ddae50680c12ef75bfbf58416ca6622fa43d879553f6cb2ed1a817346e1ffe" +checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.0", "log", "rustc-hex", "smallvec", @@ -4213,21 +4213,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0" dependencies = [ "cfg-if", - "rand 0.8.4", + "rand 0.8.5", "static_assertions", ] [[package]] name = "typenum" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "uint" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f" +checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" dependencies = [ "byteorder", "crunchy", @@ -4252,9 +4252,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-width" @@ -4287,6 +4287,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "value-bag" version = "1.0.0-alpha.8" @@ -4305,9 +4311,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wabt" @@ -4382,9 +4388,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4392,9 +4398,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" dependencies = [ "bumpalo", "lazy_static", @@ -4407,9 +4413,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" dependencies = [ "cfg-if", "js-sys", @@ -4419,9 +4425,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4429,9 +4435,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" dependencies = [ "proc-macro2", "quote", @@ -4442,9 +4448,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.78" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" [[package]] name = "wasmi" @@ -4472,9 +4478,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" dependencies = [ "js-sys", "wasm-bindgen", @@ -4492,9 +4498,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c475786c6f47219345717a043a37ec04cb4bc185e28853adcc4fa0a947eba630" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" dependencies = [ "webpki", ] @@ -4558,18 +4564,18 @@ checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" [[package]] name = "zeroize" -version = "1.4.3" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" +checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.2.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" +checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 23ac25649..1894b6916 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ More relevant links: There's only an old version in your distributions package manager? Just use a [binary release](https://github.com/WebAssembly/binaryen/releases). +* Step 4: `cargo install cargo-dylint dylint-link`. + * Step 3: `cargo install --force cargo-contract`. You can always update the `cargo-contract` binary to the latest version by running the Step 3. diff --git a/build.rs b/build.rs index dc79aa3f4..351a54eab 100644 --- a/build.rs +++ b/build.rs @@ -42,36 +42,147 @@ fn main() { .expect("OUT_DIR should be set by cargo") .into(); - let template_dir = manifest_dir.join("templates").join("new"); - let dst_file = out_dir.join("template.zip"); + generate_cargo_keys(); + rerun_if_git_head_changed(); + let template_dir = manifest_dir.join("templates").join("new"); + let template_dst_file = out_dir.join("template.zip"); println!( "Creating template zip: template_dir '{}', destination archive '{}'", template_dir.display(), - dst_file.display() + template_dst_file.display() ); - - generate_cargo_keys(); - rerun_if_git_head_changed(); - - std::process::exit( - match zip_dir(&template_dir, &dst_file, CompressionMethod::Stored) { + let zipped_template = + match zip_dir(&template_dir, &template_dst_file, CompressionMethod::Stored) { Ok(_) => { println!( "done: {} written to {}", template_dir.display(), - dst_file.display() + template_dst_file.display() ); - 0 + true } Err(e) => { eprintln!("Error: {:?}", e); - 1 + false } - }, + }; + + // This zip file will contain the `dylint` driver, this is one file named in the form of + // `libink_linting@nightly-2021-11-04-x86_64-unknown-linux-gnu.so`. This file is obtained + // by building the crate in `ink_linting/`. + let dylint_driver_dst_file = out_dir.join("ink-dylint-driver.zip"); + + #[cfg(feature = "cargo-clippy")] + { + // For `clippy` runs it is not necessary to build the `dylint` driver. + // Furthermore the fixed Rust nightly specified in `ink_linting/rust-toolchain` + // contains a bug that results in an `error[E0786]: found invalid metadata files` ICE. + // + // We still have to create an empty file though, due to the `include_bytes!` macro. + File::create(dylint_driver_dst_file).unwrap_or_else(|err| { + panic!( + "Failed creating an empty ink-dylint-driver.zip file: {:?}", + err + ); + }); + std::process::exit(bool_to_exit_code(zipped_template)); + } + + #[cfg(not(feature = "cargo-clippy"))] + { + let zipped_lints = build_dylint_driver(manifest_dir, out_dir, dylint_driver_dst_file) + .map(|_| true) + .unwrap_or_else(|err| { + panic!("Failed building dylint driver: {:?}", err); + }); + std::process::exit(bool_to_exit_code(zipped_template && zipped_lints)); + } +} + +/// Returns the process exit code which corresponds to a boolean. +fn bool_to_exit_code(val: bool) -> i32 { + match val { + true => 0, + false => 1, + } +} + +/// Builds the crate in `ink_linting/`. This crate contains the `dylint` driver with ink! specific +/// linting rules. +#[cfg(not(feature = "cargo-clippy"))] +fn build_dylint_driver( + manifest_dir: PathBuf, + out_dir: PathBuf, + dylint_driver_dst_file: PathBuf, +) -> Result<()> { + let ink_dylint_driver_dir = manifest_dir.join("ink_linting"); + + let mut cmd = Command::new("cargo"); + + let manifest_arg = format!( + "--manifest-path={}", + ink_dylint_driver_dir.join("Cargo.toml").display() ); + let target_dir = format!("--target-dir={}", out_dir.display()); + cmd.args(vec!["build", "--release", &target_dir, &manifest_arg]); + + // We need to remove those environment variables because `dylint` uses a + // fixed Rust toolchain via the `ink_linting/rust-toolchain` file. By removing + // these env variables we avoid issues with different Rust toolchains + // interfering with each other. + cmd.env_remove("RUSTUP_TOOLCHAIN"); + cmd.env_remove("CARGO_TARGET_DIR"); + + println!( + "Setting cargo working dir to '{}'", + ink_dylint_driver_dir.display() + ); + cmd.current_dir(ink_dylint_driver_dir.clone()); + + println!("Invoking cargo: {:?}", cmd); + + let child = cmd + // capture the stdout to return from this function as bytes + .stdout(std::process::Stdio::piped()) + .spawn() + .unwrap_or_else(|_| panic!("Error executing `{:?}`", cmd)); + let output = child.wait_with_output()?; + + if !output.status.success() { + anyhow::bail!( + "`{:?}` failed with exit code: {:?}", + cmd, + output.status.code() + ); + } + + println!( + "Creating ink-dylint-driver.zip: destination archive '{}'", + dylint_driver_dst_file.display() + ); + + match zip_dylint_driver( + &out_dir.join("release"), + &dylint_driver_dst_file, + CompressionMethod::Stored, + ) { + Ok(_) => { + println!( + "done: {} written to {}", + ink_dylint_driver_dir.display(), + dylint_driver_dst_file.display() + ); + Ok(()) + } + Err(e) => { + eprintln!("Error: {:?}", e); + Err(e) + } + } } +/// Creates a zip archive at `dst_file` with the content of the `src_dir`. fn zip_dir(src_dir: &Path, dst_file: &Path, method: CompressionMethod) -> Result<()> { if !src_dir.exists() { anyhow::bail!("src_dir '{}' does not exist", src_dir.display()); @@ -118,6 +229,50 @@ fn zip_dir(src_dir: &Path, dst_file: &Path, method: CompressionMethod) -> Result Ok(()) } +/// Creates a zip archive at `dst_file` with the `dylint` driver found in `src_dir`. +/// +/// `dylint` drivers have a file name of the form `libink_linting@toolchain.[so,dll]`. +#[cfg(not(feature = "cargo-clippy"))] +fn zip_dylint_driver(src_dir: &Path, dst_file: &Path, method: CompressionMethod) -> Result<()> { + if !src_dir.exists() { + anyhow::bail!("src_dir '{}' does not exist", src_dir.display()); + } + if !src_dir.is_dir() { + anyhow::bail!("src_dir '{}' is not a directory", src_dir.display()); + } + + let file = File::create(dst_file)?; + + let mut zip = ZipWriter::new(file); + let options = FileOptions::default() + .compression_method(method) + .unix_permissions(DEFAULT_UNIX_PERMISSIONS); + + let mut buffer = Vec::new(); + + let walkdir = WalkDir::new(src_dir); + let it = walkdir.into_iter().filter_map(|e| e.ok()); + + for entry in it { + let path = entry.path(); + let name = path.strip_prefix(&src_dir)?.to_path_buf(); + let file_path = name.as_os_str().to_string_lossy(); + + if path.is_file() && path.display().to_string().contains("libink_linting@") { + zip.start_file(file_path, options)?; + let mut f = File::open(path)?; + + f.read_to_end(&mut buffer)?; + zip.write_all(&*buffer)?; + buffer.clear(); + + zip.finish()?; + break; + } + } + Ok(()) +} + /// Generate the `cargo:` key output fn generate_cargo_keys() { let output = Command::new("git") diff --git a/ink_linting/.cargo/config.toml b/ink_linting/.cargo/config.toml new file mode 100644 index 000000000..e8488c83f --- /dev/null +++ b/ink_linting/.cargo/config.toml @@ -0,0 +1,11 @@ +# This file corresponds to the `.cargo/config.toml` file used for the `dylint` examples here: +# https://github.com/trailofbits/dylint/tree/master/examples. + +[target.x86_64-apple-darwin] +linker = "dylint-link" + +[target.x86_64-unknown-linux-gnu] +linker = "dylint-link" + +[target.x86_64-pc-windows-msvc] +linker = "dylint-link" diff --git a/ink_linting/.rustfmt.toml b/ink_linting/.rustfmt.toml new file mode 100644 index 000000000..8ebf443f8 --- /dev/null +++ b/ink_linting/.rustfmt.toml @@ -0,0 +1,3 @@ +license_template_path = "../FILE_HEADER" # changed +report_todo = "Always" +report_fixme = "Always" diff --git a/ink_linting/Cargo.toml b/ink_linting/Cargo.toml new file mode 100644 index 000000000..cd85dd40e --- /dev/null +++ b/ink_linting/Cargo.toml @@ -0,0 +1,91 @@ +[package] +name = "ink_linting" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2021" +publish = false + +license = "Apache-2.0" +readme = "README.md" +repository = "https://github.com/paritytech/cargo-contract" +documentation = "https://docs.rs/ink_linting" +homepage = "https://github.com/paritytech/cargo-contract" +description = "Linting tool for ink! smart contracts." +keywords = ["parity", "blockchain", "ink", "smart contracts", "substrate"] +include = ["Cargo.toml", "*.rs", "LICENSE"] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", tag = "rust-1.58.0" } +dylint_linting = "1.0.13" +if_chain = "1.0.2" +log = "0.4.14" +regex = "1.5.4" + +[dev-dependencies] +dylint_testing = "1.0.13" + +# The following are ink! dependencies, they are only required for the `ui` tests. +ink_primitives = { git = "https://github.com/paritytech/ink", default-features = false } +ink_metadata = { git = "https://github.com/paritytech/ink", default-features = false, features = ["derive"] } +ink_env = { git = "https://github.com/paritytech/ink", default-features = false } +ink_storage = { git = "https://github.com/paritytech/ink", default-features = false } +ink_lang = { git = "https://github.com/paritytech/ink", default-features = false } +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2", default-features = false, features = ["derive"] } + +# For the moment we have to include the tests as examples and +# then use `dylint_testing::ui_test_examples`. +# +# The reason is that the `dylint_testing` API currently does not +# provide any other option to run the tests on those files +# *while giving us the option to specify the dependencies*. +# +# Those files require the ink! dependencies though, by having +# them as examples here, they inherit the `dev-dependencies`. +[[example]] +name = "fail_mapping_one_constructor" +path = "ui/fail/mapping-one-constructor.rs" + +[[example]] +name = "fail_mapping_two_constructors_01" +path = "ui/fail/mapping-two-constructors-01.rs" + +[[example]] +name = "fail_mapping_two_constructors_02" +path = "ui/fail/mapping-two-constructors-02.rs" + +[[example]] +name = "fail_mapping_nested_initialize_call" +path = "ui/fail/mapping-nested-initialize-call.rs" + +[[example]] +name = "pass_mapping_one_constructor" +path = "ui/pass/mapping-one-constructor.rs" + +[[example]] +name = "pass_mapping_two_constructors" +path = "ui/pass/mapping-two-constructors.rs" + +[[example]] +name = "pass_mapping_03" +path = "ui/pass/mapping-additional-logic-constructor.rs" + +[package.metadata.rust-analyzer] +rustc_private = true + +[workspace] + +[features] +default = ["std"] +std = [ + "ink_metadata/std", + "ink_env/std", + "ink_storage/std", + "ink_primitives/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] diff --git a/ink_linting/LICENSE b/ink_linting/LICENSE new file mode 100644 index 000000000..6b0b1270f --- /dev/null +++ b/ink_linting/LICENSE @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + http://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/ink_linting/README.md b/ink_linting/README.md new file mode 100644 index 000000000..80279a661 --- /dev/null +++ b/ink_linting/README.md @@ -0,0 +1,17 @@ +# ink! linting rules + +This crate uses [`dylint`](https://github.com/trailofbits/dylint) to define custom +linting rules for [ink!](https://github.com/paritytech/ink); + +You can use it this way: + +```bash +# Install all prerequisites. +cargo install cargo-dylint dylint-link + +cargo build --release + +# Run the linting on a contract. +DYLINT_LIBRARY_PATH=$PWD/target/release cargo dylint contract_instantiated + --manifest-path ../ink/examples/erc20/Cargo.toml +``` \ No newline at end of file diff --git a/ink_linting/rust-toolchain b/ink_linting/rust-toolchain new file mode 100644 index 000000000..480368fe1 --- /dev/null +++ b/ink_linting/rust-toolchain @@ -0,0 +1,6 @@ +# This file corresponds to the `rust-toolchain` file used for the `dylint` examples here: +# https://github.com/trailofbits/dylint/tree/master/examples. + +[toolchain] +channel = "nightly-2021-11-04" +components = ["llvm-tools-preview", "rustc-dev"] diff --git a/ink_linting/src/lib.rs b/ink_linting/src/lib.rs new file mode 100644 index 000000000..7253469e5 --- /dev/null +++ b/ink_linting/src/lib.rs @@ -0,0 +1,40 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![feature(rustc_private)] + +dylint_linting::dylint_library!(); + +extern crate rustc_errors; +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_session; +extern crate rustc_span; + +mod mapping_initialized; + +#[doc(hidden)] +#[no_mangle] +pub fn register_lints(_sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) { + lint_store.register_lints(&[mapping_initialized::MAPPING_INITIALIZED]); + lint_store.register_late_pass(|| Box::new(mapping_initialized::MappingInitialized)); +} + +#[test] +fn ui() { + dylint_testing::ui_test_examples(env!("CARGO_PKG_NAME")); +} diff --git a/ink_linting/src/mapping_initialized.rs b/ink_linting/src/mapping_initialized.rs new file mode 100644 index 000000000..a684ccc59 --- /dev/null +++ b/ink_linting/src/mapping_initialized.rs @@ -0,0 +1,345 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use if_chain::if_chain; +use regex::Regex; +use rustc_errors::Applicability; +use rustc_hir::{ + def_id::DefId, + intravisit::{walk_fn, walk_item, walk_qpath, FnKind, NestedVisitorMap, Visitor}, + BodyId, FnDecl, HirId, Item, ItemKind, +}; +use rustc_hir::{QPath, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::{hir::map::Map, ty::Attributes}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::source_map::Span; + +declare_lint! { + /// **What it does:** Checks for ink! contracts that use + /// [`ink_storage::Mapping`](https://paritytech.github.io/ink/ink_storage/struct.Mapping.html) + /// in their storage without initializing it properly in the ink! constructor. + /// + /// **Why is this bad?** If `Mapping` is not properly initialized corruption of storage + /// might occur. + /// + /// **Known problems:** The lint currently requires for the + /// `ink_lang::utils::initialize_contract(…)` function call to be made on the top + /// level of the constructor *and* it has be made explicitly in this form. + /// + /// The lint can currently not detect if `initialize_contract` would be called in a + /// nested function within the constructor. + /// + /// **Example:** + /// + /// ```rust + /// // Good + /// use ink_storage::{traits::SpreadAllocate, Mapping}; + /// + /// #[ink(storage)] + /// #[derive(SpreadAllocate)] + /// pub struct MyContract { + /// balances: Mapping, + /// } + /// + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// ink_lang::utils::initialize_contract(Self::new_init) + /// } + /// + /// /// Default initializes the contract. + /// fn new_init(&mut self) { + /// let caller = Self::env().caller(); + /// let value: Balance = Default::default(); + /// self.balances.insert(&caller, &value); + /// } + /// ``` + /// + /// ```rust + /// // Bad + /// use ink_storage::{traits::SpreadAllocate, Mapping}; + /// + /// #[ink(storage)] + /// #[derive(SpreadAllocate)] + /// pub struct MyContract { + /// balances: Mapping, + /// } + /// + /// #[ink(constructor)] + /// pub fn new() -> Self { + /// Self { + /// balances: Default::default(), + /// } + /// } + /// ``` + pub MAPPING_INITIALIZED, + Deny, + "Error on ink! contracts that use `ink_storage::Mapping` without initializing it." +} + +declare_lint_pass!(MappingInitialized => [MAPPING_INITIALIZED]); + +/// An ink! attribute. +#[derive(PartialEq)] +enum InkAttribute { + // #[ink(storage)] + Storage, + // #[ink(constructor)] + Constructor, +} + +/// Returns `Some(InkAttribute)` if an ink! attribute is among `attributes`. +fn get_ink_attribute(attributes: Attributes) -> Option { + const INK_STORAGE: &str = "__ink_dylint_Storage"; + const INK_CONSTRUCTOR: &str = "__ink_dylint_Constructor"; + + let attrs = format!("symbol: \"{:?}\"", attributes); + if attrs.contains(INK_STORAGE) { + Some(InkAttribute::Storage) + } else if attrs.contains(INK_CONSTRUCTOR) { + Some(InkAttribute::Constructor) + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for MappingInitialized { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let ink_attrs = get_ink_attribute(attrs); + if ink_attrs.is_none() { + return; + } + + if let ItemKind::Struct(variant_data, _) = &item.kind { + if let VariantData::Unit(..) = variant_data { + return; + } + check_struct(cx, item, variant_data); + } + } +} + +/// Examines a `struct`. If the struct is annotated as an ink! storage struct +/// we examine if there is a `ink_storage::Mapping` in it; in case there +/// is we continue checking all ink! constructors for correct usage of the +/// `ink_lang::utils::initialize_contract` API. +/// +/// This function is backwards compatible, in case no annotated ink! struct is +/// found nothing happens. +fn check_struct<'a>(cx: &LateContext<'a>, item: &'a Item, data: &VariantData) { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + match get_ink_attribute(attrs) { + Some(InkAttribute::Storage) => {} + _ => return, + } + + let mut marker = None; + let fields = data.fields(); + let storage_contains_mapping = fields.iter().any(|field| { + let re = Regex::new(r"ink_storage\[.*\]::lazy::mapping::Mapping") + .expect("failed creating regex"); + if re.is_match(&format!("{:?}", field.ty.kind)) { + marker = Some(field); + return true; + } + false + }); + + if !storage_contains_mapping { + log::debug!("Found `#[ink(storage)]` struct without `Mapping`"); + return; + } + log::debug!("Found `#[ink(storage)]` struct with `Mapping`"); + + let inherent_impls = cx.tcx.inherent_impls(item.def_id); + let constructors_without_initialize: Vec = inherent_impls + .iter() + .map(|imp_did| item_from_def_id(cx, *imp_did)) + .filter_map(|item| constructor_but_no_initialize(cx, item)) + .collect(); + log::debug!( + "Result of searching for constructors without initialize: {:?}", + constructors_without_initialize + ); + + if_chain! { + if storage_contains_mapping; + if !constructors_without_initialize.is_empty(); + then { + let constructor_span = constructors_without_initialize.get(0) + .expect("at least one faulty constructor must exist"); + let snippet = snippet_opt(cx, *constructor_span) + .expect("snippet must exist"); + span_lint_and_then( + cx, + MAPPING_INITIALIZED, + item.span, + &format!( + "you have declared an `#[ink(storage)]` on `{}` without initializing it in the contract constructor.", + item.ident + ), + |diag| { + diag.span_suggestion( + *constructor_span, + "add an `ink_lang::utils::initialize_contract(…)` function in this constructor", + snippet, + Applicability::Unspecified, + ); + diag.span_help(marker.expect("marker must exist").span, "this field uses a `Mapping`"); + }); + } + } +} + +/// Returns `Some(span)` if a constructor without a call of +/// `ink_lang::utils::initialize_contract(…)` was found. +fn constructor_but_no_initialize<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx Item<'_>, +) -> Option { + let mut visitor = InkAttributeVisitor { + cx, + ink_attribute: None, + constructor_info: None, + }; + + walk_item(&mut visitor, item); + + match visitor.constructor_info { + Some(info) if !info.uses_initialize_contract => Some(info.span), + _ => None, + } +} + +/// Visitor for ink! attributes. +struct InkAttributeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + ink_attribute: Option, + constructor_info: Option, +} + +// Information about an ink! constructor. +struct InkConstructor { + // The constructor has a call to `ink_lang::utils::initialize_contract(…)` + // in its function. + uses_initialize_contract: bool, + // The span for the constructor function. + span: Span, +} + +impl<'tcx> Visitor<'tcx> for InkAttributeVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body_id: BodyId, + span: Span, + id: HirId, + ) { + // We can return immediately if an incorrect constructor was already found + if let Some(constructor) = &self.constructor_info { + if !constructor.uses_initialize_contract { + return; + } + } + + let attrs = self.cx.tcx.get_attrs(id.owner.to_def_id()); + self.ink_attribute = get_ink_attribute(attrs); + + if self.ink_attribute == Some(InkAttribute::Storage) { + return; + } else if self.ink_attribute == Some(InkAttribute::Constructor) { + log::debug!("Found constructor, starting to search for `initialize_contract`"); + let mut visitor = InitializeContractVisitor { + cx: self.cx, + uses_initialize_contract: false, + }; + walk_fn(&mut visitor, kind, decl, body_id, span, id); + + log::debug!( + "Has `initialize_contract`? {:?}", + visitor.uses_initialize_contract + ); + self.constructor_info = Some(InkConstructor { + uses_initialize_contract: visitor.uses_initialize_contract, + span, + }); + + return; + } + + walk_fn(self, kind, decl, body_id, span, id); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} + +/// Visitor to determine if a `fn` contains a call to `ink_lang::utils::initialize_contract(…)`. +/// +/// # Known Limitation +/// +/// This function currently only finds call to `initialize_contract` which happen +/// on the first level of the `fn` ‒ no nested calls are found! So if you would +/// call `initialize_contract` within a sub-function of the ink! constructor +/// this is not recognized! +/// +/// Also the function call must be explicitly called `ink_lang::utils::initialize_contract(…)`, +/// the linter currently does not detected indirections. +struct InitializeContractVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + uses_initialize_contract: bool, +} + +impl<'tcx> Visitor<'tcx> for InitializeContractVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, id: HirId, span: Span) { + log::debug!("Visiting path {:?}", qpath); + if self.uses_initialize_contract { + return; + } + + if let QPath::Resolved(_, path) = qpath { + log::debug!("QPath: {:?}", path.res); + let re = + Regex::new(r"ink_lang\[.*\]::codegen::dispatch::execution::initialize_contract") + .expect("failed creating regex"); + if re.is_match(&format!("{:?}", path.res)) { + self.uses_initialize_contract = true; + return; + } + } + + walk_qpath(self, qpath, id, span); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} + +/// Returns the `rustc_hir::Item` for a `rustc_hir::def_id::DefId`. +fn item_from_def_id<'tcx>(cx: &LateContext<'tcx>, def_id: DefId) -> &'tcx Item<'tcx> { + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + cx.tcx.hir().expect_item(hir_id) +} diff --git a/ink_linting/ui/fail/mapping-nested-initialize-call.rs b/ink_linting/ui/fail/mapping-nested-initialize-call.rs new file mode 100644 index 000000000..a8064b724 --- /dev/null +++ b/ink_linting/ui/fail/mapping-nested-initialize-call.rs @@ -0,0 +1,58 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod my_contract { + use ink_storage::{traits::SpreadAllocate, Mapping}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: Mapping, + } + + impl MyContract { + /// The linter currently does not detect if `initialize_contract` is + /// called in a nested function. + #[ink(constructor)] + pub fn new1() -> Self { + Self::foo() + } + + fn foo() -> Self { + ink_lang::utils::initialize_contract(Self::new_init) + } + + /// Default initializes the contract. + fn new_init(&mut self) { + let caller = Self::env().caller(); + let value: Balance = Default::default(); + self.balances.insert(&caller, &value); + } + + /// Returns something. + #[ink(message)] + pub fn get(&self) { + // ... + } + } +} + +fn main() {} diff --git a/ink_linting/ui/fail/mapping-nested-initialize-call.stderr b/ink_linting/ui/fail/mapping-nested-initialize-call.stderr new file mode 100644 index 000000000..cd11d830a --- /dev/null +++ b/ink_linting/ui/fail/mapping-nested-initialize-call.stderr @@ -0,0 +1,27 @@ +error: you have declared an `#[ink(storage)]` on `MyContract` without initializing it in the contract constructor. + --> $DIR/mapping-nested-initialize-call.rs:26:5 + | +LL | / #[derive(SpreadAllocate)] +LL | | pub struct MyContract { +LL | | balances: Mapping, +LL | | } + | |_____^ + | + = note: `#[deny(mapping_initialized)]` on by default +help: this field uses a `Mapping` + --> $DIR/mapping-nested-initialize-call.rs:28:9 + | +LL | balances: Mapping, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor + | +LL ~ /// The linter currently does not detect if `initialize_contract` is +LL + /// called in a nested function. +LL + #[ink(constructor)] +LL + pub fn new1() -> Self { +LL + Self::foo() +LL + } + | + +error: aborting due to previous error + diff --git a/ink_linting/ui/fail/mapping-one-constructor.rs b/ink_linting/ui/fail/mapping-one-constructor.rs new file mode 100644 index 000000000..18fbe1b78 --- /dev/null +++ b/ink_linting/ui/fail/mapping-one-constructor.rs @@ -0,0 +1,47 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod my_contract { + use ink_storage::{traits::SpreadAllocate, Mapping}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: Mapping, + } + + impl MyContract { + #[ink(constructor)] + pub fn new() -> Self { + Self { + balances: Default::default(), + } + } + + /// Returns the total token supply. + #[ink(message)] + pub fn get(&self) { + // ... + } + } +} + +fn main() {} diff --git a/ink_linting/ui/fail/mapping-one-constructor.stderr b/ink_linting/ui/fail/mapping-one-constructor.stderr new file mode 100644 index 000000000..8e0a4d8af --- /dev/null +++ b/ink_linting/ui/fail/mapping-one-constructor.stderr @@ -0,0 +1,26 @@ +error: you have declared an `#[ink(storage)]` on `MyContract` without initializing it in the contract constructor. + --> $DIR/mapping-one-constructor.rs:26:5 + | +LL | / #[derive(SpreadAllocate)] +LL | | pub struct MyContract { +LL | | balances: Mapping, +LL | | } + | |_____^ + | + = note: `#[deny(mapping_initialized)]` on by default +help: this field uses a `Mapping` + --> $DIR/mapping-one-constructor.rs:28:9 + | +LL | balances: Mapping, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor + | +LL ~ pub fn new() -> Self { +LL + Self { +LL + balances: Default::default(), +LL + } +LL + } + | + +error: aborting due to previous error + diff --git a/ink_linting/ui/fail/mapping-two-constructors-01.rs b/ink_linting/ui/fail/mapping-two-constructors-01.rs new file mode 100644 index 000000000..645ce9a50 --- /dev/null +++ b/ink_linting/ui/fail/mapping-two-constructors-01.rs @@ -0,0 +1,62 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod my_contract { + use ink_storage::{traits::SpreadAllocate, Mapping}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: Mapping, + } + + impl MyContract { + /// This constructor is correct. + /// It comes before the incorrect constructor. + #[ink(constructor)] + pub fn new1() -> Self { + ink_lang::utils::initialize_contract(Self::new_init) + } + + /// Default initializes the contract. + fn new_init(&mut self) { + let caller = Self::env().caller(); + let value: Balance = Default::default(); + self.balances.insert(&caller, &value); + } + + /// This constructor is **not** correct. + #[ink(constructor)] + pub fn new2() -> Self { + Self { + balances: Default::default(), + } + } + + /// Returns something. + #[ink(message)] + pub fn get(&self) { + // ... + } + } +} + +fn main() {} diff --git a/ink_linting/ui/fail/mapping-two-constructors-01.stderr b/ink_linting/ui/fail/mapping-two-constructors-01.stderr new file mode 100644 index 000000000..d174f423b --- /dev/null +++ b/ink_linting/ui/fail/mapping-two-constructors-01.stderr @@ -0,0 +1,27 @@ +error: you have declared an `#[ink(storage)]` on `MyContract` without initializing it in the contract constructor. + --> $DIR/mapping-two-constructors-01.rs:26:5 + | +LL | / #[derive(SpreadAllocate)] +LL | | pub struct MyContract { +LL | | balances: Mapping, +LL | | } + | |_____^ + | + = note: `#[deny(mapping_initialized)]` on by default +help: this field uses a `Mapping` + --> $DIR/mapping-two-constructors-01.rs:28:9 + | +LL | balances: Mapping, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor + | +LL ~ /// This constructor is **not** correct. +LL + #[ink(constructor)] +LL + pub fn new2() -> Self { +LL + Self { +LL + balances: Default::default(), +LL + } + ... + +error: aborting due to previous error + diff --git a/ink_linting/ui/fail/mapping-two-constructors-02.rs b/ink_linting/ui/fail/mapping-two-constructors-02.rs new file mode 100644 index 000000000..253ac1a5b --- /dev/null +++ b/ink_linting/ui/fail/mapping-two-constructors-02.rs @@ -0,0 +1,62 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod my_contract { + use ink_storage::{traits::SpreadAllocate, Mapping}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: Mapping, + } + + impl MyContract { + /// This constructor is **not** correct. + /// It comes before the correct constructor. + #[ink(constructor)] + pub fn new2() -> Self { + Self { + balances: Default::default(), + } + } + + /// This constructor is correct. + #[ink(constructor)] + pub fn new1() -> Self { + ink_lang::utils::initialize_contract(Self::new_init) + } + + /// Default initializes the contract. + fn new_init(&mut self) { + let caller = Self::env().caller(); + let value: Balance = Default::default(); + self.balances.insert(&caller, &value); + } + + /// Returns something. + #[ink(message)] + pub fn get(&self) { + // ... + } + } +} + +fn main() {} diff --git a/ink_linting/ui/fail/mapping-two-constructors-02.stderr b/ink_linting/ui/fail/mapping-two-constructors-02.stderr new file mode 100644 index 000000000..ff42f1f51 --- /dev/null +++ b/ink_linting/ui/fail/mapping-two-constructors-02.stderr @@ -0,0 +1,27 @@ +error: you have declared an `#[ink(storage)]` on `MyContract` without initializing it in the contract constructor. + --> $DIR/mapping-two-constructors-02.rs:26:5 + | +LL | / #[derive(SpreadAllocate)] +LL | | pub struct MyContract { +LL | | balances: Mapping, +LL | | } + | |_____^ + | + = note: `#[deny(mapping_initialized)]` on by default +help: this field uses a `Mapping` + --> $DIR/mapping-two-constructors-02.rs:28:9 + | +LL | balances: Mapping, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor + | +LL ~ /// This constructor is **not** correct. +LL + /// It comes before the correct constructor. +LL + #[ink(constructor)] +LL + pub fn new2() -> Self { +LL + Self { +LL + balances: Default::default(), + ... + +error: aborting due to previous error + diff --git a/ink_linting/ui/pass/mapping-additional-logic-constructor.rs b/ink_linting/ui/pass/mapping-additional-logic-constructor.rs new file mode 100644 index 000000000..964d8a91c --- /dev/null +++ b/ink_linting/ui/pass/mapping-additional-logic-constructor.rs @@ -0,0 +1,61 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod my_contract { + /// The `Mapping` must be recognized, even if it is imported + /// under a different name. + use ink_storage::{traits::SpreadAllocate, Mapping as SomeOtherName}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: SomeOtherName, + } + + impl MyContract { + #[ink(constructor)] + pub fn new() -> Self { + let a = 1; + let b = 2; + assert_eq!(add(a, b), 3); + ink_lang::utils::initialize_contract(Self::new_init) + } + + /// Default initializes the contract. + fn new_init(&mut self) { + let caller = Self::env().caller(); + let value: Balance = Default::default(); + self.balances.insert(&caller, &value); + } + + /// Returns something. + #[ink(message)] + pub fn get(&self) { + // ... + } + } + + fn add(a: u32, b: u32) -> u32 { + a + b + } +} + +fn main() {} diff --git a/ink_linting/ui/pass/mapping-one-constructor.rs b/ink_linting/ui/pass/mapping-one-constructor.rs new file mode 100644 index 000000000..48544660d --- /dev/null +++ b/ink_linting/ui/pass/mapping-one-constructor.rs @@ -0,0 +1,52 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod my_contract { + use ink_storage::{traits::SpreadAllocate, Mapping}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: Mapping, + } + + impl MyContract { + #[ink(constructor)] + pub fn new() -> Self { + ink_lang::utils::initialize_contract(Self::new_init) + } + + /// Default initializes the contract. + fn new_init(&mut self) { + let caller = Self::env().caller(); + let value: Balance = Default::default(); + self.balances.insert(&caller, &value); + } + + /// Returns something. + #[ink(message)] + pub fn get(&self) { + // ... + } + } +} + +fn main() {} diff --git a/ink_linting/ui/pass/mapping-two-constructors.rs b/ink_linting/ui/pass/mapping-two-constructors.rs new file mode 100644 index 000000000..558d7e0a4 --- /dev/null +++ b/ink_linting/ui/pass/mapping-two-constructors.rs @@ -0,0 +1,60 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// This file is part of cargo-contract. +// +// cargo-contract is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// cargo-contract is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with cargo-contract. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use ink_lang as ink; + +#[ink::contract] +mod my_contract { + use ink_storage::{traits::SpreadAllocate, Mapping}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: Mapping, + } + + impl MyContract { + /// The `initialize_contract` must be found, even if there is logic before it. + #[ink(constructor)] + pub fn new() -> Self { + let a = 1; + let b = 2; + assert_eq!(add(a, b), 3); + ink_lang::utils::initialize_contract(Self::new_init) + } + + /// Default initializes the contract. + fn new_init(&mut self) { + let caller = Self::env().caller(); + let value: Balance = Default::default(); + self.balances.insert(&caller, &value); + } + + /// Returns something. + #[ink(message)] + pub fn get(&self) { + // ... + } + } + + fn add(a: u32, b: u32) -> u32 { + a + b + } +} + +fn main() {} diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index d2507a42b..0846e17f8 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -2,8 +2,8 @@ name = "contract-metadata" version = "0.5.1" authors = ["Parity Technologies "] -edition = "2021" rust-version = "1.56.1" +edition = "2021" license = "Apache-2.0" readme = "README.md" diff --git a/src/cmd/build.rs b/src/cmd/build.rs index 808822659..5fd307dbd 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -247,13 +247,6 @@ fn exec_cargo_for_wasm_target( ) -> Result<()> { util::assert_channel()?; - // set linker args via RUSTFLAGS. - // Currently will override user defined RUSTFLAGS from .cargo/config. See https://github.com/paritytech/cargo-contract/issues/98. - std::env::set_var( - "RUSTFLAGS", - "-C link-arg=-zstack-size=65536 -C link-arg=--import-memory -Clinker-plugin-lto", - ); - let cargo_build = |manifest_path: &ManifestPath| { let target_dir = &crate_metadata.target_directory; let target_dir = format!("--target-dir={}", target_dir.to_string_lossy()); @@ -272,7 +265,11 @@ fn exec_cargo_for_wasm_target( } else { args.push("-Zbuild-std-features=panic_immediate_abort"); } - util::invoke_cargo(command, &args, manifest_path.directory(), verbosity)?; + let env = Some(vec![( + "RUSTFLAGS", + "-C link-arg=-zstack-size=65536 -C link-arg=--import-memory -Clinker-plugin-lto", + )]); + util::invoke_cargo(command, &args, manifest_path.directory(), verbosity, env)?; Ok(()) }; @@ -298,8 +295,36 @@ fn exec_cargo_for_wasm_target( .using_temp(cargo_build)?; } - // clear RUSTFLAGS - std::env::remove_var("RUSTFLAGS"); + Ok(()) +} + +/// Executes `cargo dylint` with the ink! linting driver that is built during +/// the `build.rs`. +/// +/// We create a temporary folder, extract the linting driver there and run +/// `cargo dylint` with it. +fn exec_cargo_dylint(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Result<()> { + let tmp_dir = tempfile::Builder::new() + .prefix("cargo-contract-dylint_") + .tempdir()?; + log::debug!("Using temp workspace at '{}'", tmp_dir.path().display()); + + let template = include_bytes!(concat!(env!("OUT_DIR"), "/ink-dylint-driver.zip")); + crate::util::unzip(template, tmp_dir.path().to_path_buf(), None)?; + + let manifest_path = crate_metadata.manifest_path.cargo_arg(); + let args = vec!["ink_linting", &manifest_path]; + let tmp_dir_path = tmp_dir.path().as_os_str().to_string_lossy(); + let env = vec![ + ("DYLINT_LIBRARY_PATH", tmp_dir_path.as_ref()), + ("DYLINT_DRIVER_PATH", tmp_dir_path.as_ref()), + ]; + let working_dir = crate_metadata + .manifest_path + .directory() + .unwrap_or_else(|| Path::new(".")) + .canonicalize()?; + util::invoke_cargo("dylint", &args, Some(working_dir), verbosity, Some(env))?; Ok(()) } @@ -615,16 +640,15 @@ fn assert_compatible_ink_dependencies( ) -> Result<()> { for dependency in ["parity-scale-codec", "scale-info"].iter() { let args = ["-i", dependency, "--duplicates"]; - let _ = util::invoke_cargo("tree", &args, manifest_path.directory(), verbosity).map_err( - |_| { + let _ = util::invoke_cargo("tree", &args, manifest_path.directory(), verbosity, None) + .map_err(|_| { anyhow::anyhow!( "Mismatching versions of `{}` were found!\n\ Please ensure that your contract and your ink! dependencies use a compatible \ version of this package.", dependency ) - }, - )?; + })?; } Ok(()) } @@ -671,6 +695,14 @@ pub(crate) fn execute(args: ExecuteArgs) -> Result { verbosity, " {} {}", format!("[1/{}]", build_artifact.steps()).bold(), + "Checking ink! linting rules".bright_green().bold() + ); + exec_cargo_dylint(&crate_metadata, verbosity)?; + + maybe_println!( + verbosity, + " {} {}", + format!("[2/{}]", build_artifact.steps()).bold(), "Building cargo project".bright_green().bold() ); exec_cargo_for_wasm_target( @@ -685,7 +717,7 @@ pub(crate) fn execute(args: ExecuteArgs) -> Result { maybe_println!( verbosity, " {} {}", - format!("[2/{}]", build_artifact.steps()).bold(), + format!("[3/{}]", build_artifact.steps()).bold(), "Post processing wasm file".bright_green().bold() ); post_process_wasm(&crate_metadata)?; @@ -693,7 +725,7 @@ pub(crate) fn execute(args: ExecuteArgs) -> Result { maybe_println!( verbosity, " {} {}", - format!("[3/{}]", build_artifact.steps()).bold(), + format!("[4/{}]", build_artifact.steps()).bold(), "Optimizing wasm file".bright_green().bold() ); let optimization_result = @@ -704,6 +736,20 @@ pub(crate) fn execute(args: ExecuteArgs) -> Result { let (opt_result, metadata_result) = match build_artifact { BuildArtifacts::CheckOnly => { + maybe_println!( + verbosity, + " {} {}", + format!("[1/{}]", build_artifact.steps()).bold(), + "Checking ink! linting rules".bright_green().bold() + ); + exec_cargo_dylint(&crate_metadata, verbosity)?; + + maybe_println!( + verbosity, + " {} {}", + format!("[2/{}]", build_artifact.steps()).bold(), + "Executing `cargo check`".bright_green().bold() + ); exec_cargo_for_wasm_target( &crate_metadata, "check", @@ -1273,4 +1319,69 @@ mod tests_ci_only { Ok(()) }) } + + // This test has to be ignored until the next ink! rc. + // Before that we don't have the `__ink_dylint_…` markers available + // to actually run `dylint`. + #[test] + #[ignore] + fn dylint_must_find_issue() { + with_new_contract_project(|manifest_path| { + // given + let contract = r#" + #![cfg_attr(not(feature = "std"), no_std)] + + use ink_lang as ink; + + #[ink::contract] + mod fail_mapping_01 { + use ink_storage::{traits::SpreadAllocate, Mapping}; + + #[ink(storage)] + #[derive(SpreadAllocate)] + pub struct MyContract { + balances: Mapping, + } + + impl MyContract { + #[ink(constructor)] + pub fn new() -> Self { + Self { + balances: Default::default(), + } + } + + /// Returns the total token supply. + #[ink(message)] + pub fn get(&self) { + // ... + } + } + }"#; + let project_dir = manifest_path.directory().expect("directory must exist"); + let lib = project_dir.join("lib.rs"); + std::fs::write(&lib, contract)?; + + let args = crate::cmd::build::ExecuteArgs { + manifest_path, + build_artifact: BuildArtifacts::CheckOnly, + ..Default::default() + }; + + // when + let res = super::execute(args); + + // then + match res { + Err(err) => { + eprintln!("err: {:?}", err); + assert!(err.to_string().contains( + "help: add an `initialize_contract` function in this constructor" + )); + } + _ => panic!("build succeeded, but must fail!"), + }; + Ok(()) + }) + } } diff --git a/src/cmd/metadata.rs b/src/cmd/metadata.rs index 539077afb..421beba76 100644 --- a/src/cmd/metadata.rs +++ b/src/cmd/metadata.rs @@ -100,6 +100,7 @@ pub(crate) fn execute( ], crate_metadata.manifest_path.directory(), verbosity, + None, )?; let ink_meta: serde_json::Map = serde_json::from_slice(&stdout)?; diff --git a/src/cmd/new.rs b/src/cmd/new.rs index 75482bb05..624223e9a 100644 --- a/src/cmd/new.rs +++ b/src/cmd/new.rs @@ -14,14 +14,9 @@ // You should have received a copy of the GNU General Public License // along with cargo-contract. If not, see . -use std::{ - env, fs, - io::{Cursor, Read, Seek, SeekFrom, Write}, - path::Path, -}; +use std::{env, fs, path::Path}; use anyhow::Result; -use heck::ToUpperCamelCase as _; pub(crate) fn execute

(name: &str, dir: Option

) -> Result<()> where @@ -51,56 +46,8 @@ where } let template = include_bytes!(concat!(env!("OUT_DIR"), "/template.zip")); - let mut cursor = Cursor::new(Vec::new()); - cursor.write_all(template)?; - cursor.seek(SeekFrom::Start(0))?; - - let mut archive = zip::ZipArchive::new(cursor)?; - - for i in 0..archive.len() { - let mut file = archive.by_index(i)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - // replace template placeholders - let contents = contents.replace("{{name}}", name); - let contents = contents.replace("{{camel_name}}", &name.to_upper_camel_case()); - - let outpath = out_dir.join(file.name()); - - if (*file.name()).ends_with('/') { - fs::create_dir_all(&outpath)?; - } else { - if let Some(p) = outpath.parent() { - if !p.exists() { - fs::create_dir_all(&p)?; - } - } - let mut outfile = fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(outpath.clone()) - .map_err(|e| { - if e.kind() == std::io::ErrorKind::AlreadyExists { - anyhow::anyhow!("New contract file {} already exists", file.name()) - } else { - anyhow::anyhow!(e) - } - })?; - - outfile.write_all(contents.as_bytes())?; - } - - // Get and set permissions - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - - if let Some(mode) = file.unix_mode() { - fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?; - } - } - } + + crate::util::unzip(template, out_dir, Some(name))?; Ok(()) } @@ -177,7 +124,7 @@ mod tests { assert!(result.is_err(), "Should fail"); assert_eq!( result.err().unwrap().to_string(), - "New contract file .gitignore already exists" + "File .gitignore already exists" ); Ok(()) }) diff --git a/src/cmd/test.rs b/src/cmd/test.rs index 98b9dae13..49272723e 100644 --- a/src/cmd/test.rs +++ b/src/cmd/test.rs @@ -65,7 +65,7 @@ pub(crate) fn execute(manifest_path: &ManifestPath, verbosity: Verbosity) -> Res "Running tests".bright_green().bold() ); - let stdout = util::invoke_cargo("test", &[""], manifest_path.directory(), verbosity)?; + let stdout = util::invoke_cargo("test", &[""], manifest_path.directory(), verbosity, None)?; Ok(TestResult { stdout, verbosity }) } diff --git a/src/util.rs b/src/util.rs index d373db1cd..a696dcfca 100644 --- a/src/util.rs +++ b/src/util.rs @@ -16,8 +16,15 @@ use crate::Verbosity; use anyhow::{Context, Result}; +use heck::ToUpperCamelCase as _; use rustc_version::Channel; -use std::{ffi::OsStr, path::Path, process::Command}; +use std::{ + ffi::OsStr, + fs, + io::{Cursor, Read, Seek, SeekFrom, Write}, + path::{Path, PathBuf}, + process::Command, +}; /// Check whether the current rust channel is valid: `nightly` is recommended. pub fn assert_channel() -> Result<()> { @@ -43,6 +50,7 @@ pub(crate) fn invoke_cargo( args: I, working_dir: Option

, verbosity: Verbosity, + env: Option>, ) -> Result> where I: IntoIterator + std::fmt::Debug, @@ -51,6 +59,22 @@ where { let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); let mut cmd = Command::new(cargo); + + if command == "dylint" { + // We need to remove those environment in case `cargo dylint` is invoked. + // This is because the ink! dylint driver crate found in `dylint` uses a + // fixed Rust toolchain via the `ink_linting/rust-toolchain` file. By + // removing this env variable we avoid issues with different Rust toolchains + // interfering with each other. + cmd.env_remove("CARGO_TARGET_DIR"); + } + + env.unwrap_or_default() + .iter() + .for_each(|(env_var, env_key)| { + cmd.env(env_var, env_key); + }); + if let Some(path) = working_dir { log::debug!("Setting cargo working dir to '{}'", path.as_ref().display()); cmd.current_dir(path); @@ -60,7 +84,14 @@ where cmd.args(args); match verbosity { Verbosity::Quiet => cmd.arg("--quiet"), - Verbosity::Verbose => cmd.arg("--verbose"), + Verbosity::Verbose => { + // `dylint` is verbose by default, it doesn't have a `--verbose` argument. + if command != "dylint" { + cmd.arg("--verbose") + } else { + &mut cmd + } + } Verbosity::Default => &mut cmd, }; @@ -189,3 +220,66 @@ pub mod tests { }) } } + +// Unzips the file at `template` to `out_dir`. +// +// In case `name` is set the zip file is treated as if it were a template for a new +// contract. Replacements in `Cargo.toml` for `name`-placeholders are attempted in +// that case. +pub fn unzip(template: &[u8], out_dir: PathBuf, name: Option<&str>) -> Result<()> { + let mut cursor = Cursor::new(Vec::new()); + cursor.write_all(template)?; + cursor.seek(SeekFrom::Start(0))?; + + let mut archive = zip::ZipArchive::new(cursor)?; + + for i in 0..archive.len() { + let mut file = archive.by_index(i)?; + let outpath = out_dir.join(file.name()); + + if (*file.name()).ends_with('/') { + fs::create_dir_all(&outpath)?; + } else { + if let Some(p) = outpath.parent() { + if !p.exists() { + fs::create_dir_all(&p)?; + } + } + let mut outfile = fs::OpenOptions::new() + .write(true) + .create_new(true) + .open(outpath.clone()) + .map_err(|e| { + if e.kind() == std::io::ErrorKind::AlreadyExists { + anyhow::anyhow!("File {} already exists", file.name(),) + } else { + anyhow::anyhow!(e) + } + })?; + + if let Some(name) = name { + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + let contents = contents.replace("{{name}}", name); + let contents = contents.replace("{{camel_name}}", &name.to_upper_camel_case()); + outfile.write_all(contents.as_bytes())?; + } else { + let mut v = Vec::new(); + file.read_to_end(&mut v)?; + outfile.write_all(v.as_slice())?; + } + } + + // Get and set permissions + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + + if let Some(mode) = file.unix_mode() { + fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?; + } + } + } + + Ok(()) +} diff --git a/src/workspace/manifest.rs b/src/workspace/manifest.rs index ad0f02ca9..8a45a2b9f 100644 --- a/src/workspace/manifest.rs +++ b/src/workspace/manifest.rs @@ -53,7 +53,15 @@ impl ManifestPath { /// Create an arg `--manifest-path=` for `cargo` command pub fn cargo_arg(&self) -> String { - format!("--manifest-path={}", self.path.to_string_lossy()) + format!( + "--manifest-path={}", + self.path + .canonicalize() + .unwrap_or_else(|err| { + panic!("Failed to canonicalize {:?}: {:?}", self.path, err); + }) + .to_string_lossy() + ) } /// The directory path of the manifest path.