diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8009b6d..c8bdb29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,7 @@ on: push: branches: - master + - cairo-comp-1 concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/Cargo.lock b/Cargo.lock index 6013eb3..d0415f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "ascii-canvas" @@ -81,17 +87,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -125,6 +120,18 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -144,36 +151,45 @@ dependencies = [ "serde", ] +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "cairo-felt" -version = "0.3.0-rc1" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93dedd19b8edf685798f1f12e4e0ac21ac196ea5262c300783f69f3fa0cb28b" +checksum = "ed22664386f178bf9ca7b9ae7235727d92fa37b731a9063b5122488a1f699834" dependencies = [ "lazy_static", "num-bigint", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", "serde", ] [[package]] name = "cairo-lang-casm" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-utils", "indoc", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.16", + "parity-scale-codec", + "parity-scale-codec-derive", + "schemars", "serde", "thiserror", ] [[package]] name = "cairo-lang-compiler" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "anyhow", "cairo-lang-defs", @@ -188,7 +204,6 @@ dependencies = [ "cairo-lang-sierra-generator", "cairo-lang-syntax", "cairo-lang-utils", - "clap", "log", "salsa", "smol_str", @@ -197,13 +212,16 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" +dependencies = [ + "cairo-lang-utils", +] [[package]] name = "cairo-lang-defs" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -211,38 +229,39 @@ dependencies = [ "cairo-lang-parser", "cairo-lang-syntax", "cairo-lang-utils", - "indexmap", - "itertools", + "indexmap 2.0.0", + "itertools 0.11.0", "salsa", "smol_str", ] [[package]] name = "cairo-lang-diagnostics" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ + "cairo-lang-debug", "cairo-lang-filesystem", "cairo-lang-utils", - "itertools", + "itertools 0.11.0", "salsa", ] [[package]] name = "cairo-lang-eq-solver" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-utils", "good_lp", - "indexmap", - "itertools", + "indexmap 2.0.0", + "itertools 0.11.0", ] [[package]] name = "cairo-lang-filesystem" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -254,8 +273,8 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -267,19 +286,19 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "id-arena", - "indexmap", - "itertools", + "indexmap 2.0.0", + "itertools 0.11.0", "log", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.16", "salsa", "smol_str", ] [[package]] name = "cairo-lang-parser" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -287,10 +306,10 @@ dependencies = [ "cairo-lang-syntax-codegen", "cairo-lang-utils", "colored", - "itertools", + "itertools 0.11.0", "log", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.16", "salsa", "smol_str", "unescaper", @@ -298,8 +317,8 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-defs", "cairo-lang-diagnostics", @@ -309,27 +328,29 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "indoc", - "itertools", + "itertools 0.11.0", + "num-bigint", "salsa", "smol_str", ] [[package]] name = "cairo-lang-proc-macros" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-debug", "quote", - "syn 1.0.109", + "syn 2.0.28", ] [[package]] name = "cairo-lang-project" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-filesystem", + "cairo-lang-utils", "serde", "smol_str", "thiserror", @@ -338,8 +359,8 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -350,28 +371,28 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "id-arena", - "itertools", + "itertools 0.11.0", "log", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.16", "salsa", "smol_str", ] [[package]] name = "cairo-lang-sierra" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-utils", "const-fnv1a-hash", "convert_case", "derivative", - "itertools", + "itertools 0.11.0", "lalrpop", "lalrpop-util", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.16", "regex", "salsa", "serde", @@ -382,32 +403,34 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", + "cairo-lang-sierra-type-size", "cairo-lang-utils", - "itertools", + "itertools 0.11.0", "thiserror", ] [[package]] name = "cairo-lang-sierra-gas" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", + "cairo-lang-sierra-type-size", "cairo-lang-utils", - "itertools", + "itertools 0.11.0", "thiserror", ] [[package]] name = "cairo-lang-sierra-generator" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -422,8 +445,8 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "id-arena", - "indexmap", - "itertools", + "indexmap 2.0.0", + "itertools 0.11.0", "num-bigint", "salsa", "smol_str", @@ -431,30 +454,38 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ - "anyhow", "assert_matches", "cairo-felt", "cairo-lang-casm", "cairo-lang-sierra", "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", + "cairo-lang-sierra-type-size", "cairo-lang-utils", - "clap", "indoc", - "itertools", + "itertools 0.11.0", "log", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.16", "thiserror", ] +[[package]] +name = "cairo-lang-sierra-type-size" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" +dependencies = [ + "cairo-lang-sierra", + "cairo-lang-utils", +] + [[package]] name = "cairo-lang-starknet" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "anyhow", "cairo-felt", @@ -474,15 +505,15 @@ dependencies = [ "cairo-lang-sierra-to-casm", "cairo-lang-syntax", "cairo-lang-utils", - "clap", "convert_case", "genco", + "indent", "indoc", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", "once_cell", "serde", "serde_json", @@ -493,14 +524,14 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", "cairo-lang-utils", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.16", "salsa", "smol_str", "thiserror", @@ -509,34 +540,31 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ - "cairo-lang-utils", "genco", - "log", "xshell", ] [[package]] name = "cairo-lang-utils" -version = "1.1.1" -source = "git+https://github.com/starkware-libs/cairo.git?tag=v1.1.1#c6b003cc425907ac5fc846375a48737d5125f5b5" +version = "2.1.0" +source = "git+https://github.com/starkware-libs/cairo.git?tag=v2.1.0#0f77760aa2e7750b2dda7e708403584e3040b6ad" dependencies = [ - "env_logger", - "indexmap", - "itertools", - "log", + "indexmap 2.0.0", + "itertools 0.11.0", "num-bigint", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", + "parity-scale-codec", + "schemars", "serde", - "time", ] [[package]] name = "caracal" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -555,7 +583,7 @@ dependencies = [ "insta", "num-bigint", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", "once_cell", "serde_json", "smol_str", @@ -576,9 +604,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.3.11" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", "clap_derive", @@ -587,9 +615,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", @@ -599,14 +627,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] @@ -752,15 +780,21 @@ dependencies = [ [[package]] name = "dot-structures" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675e35c02a51bb4d4618cb4885b3839ce6d1787c97b664474d9208d074742e20" + +[[package]] +name = "dyn-clone" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545da7d6df7f8fd0de7106669a7d0bfa3dbcfa24d81da46906ad658188b2ff7c" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "ena" @@ -778,23 +812,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] -name = "env_logger" -version = "0.9.3" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -813,12 +840,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "fixedbitset" @@ -832,6 +856,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "genco" version = "0.17.5" @@ -877,9 +907,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" +checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006" dependencies = [ "aho-corasick", "bstr", @@ -890,9 +920,9 @@ dependencies = [ [[package]] name = "good_lp" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed4d07599e3cdb52477f1d36bef936c89ce854c452e7026b2ba327b93c86f61" +checksum = "c0833c2bc3cee9906df9969ade12b0b3b8759faa7bc2682b428be767033cdcd4" dependencies = [ "fnv", "minilp", @@ -900,9 +930,9 @@ dependencies = [ [[package]] name = "graphviz-rust" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6ed437c021ff97f2517bfd9c629b5bbbb71b123ba5556e01df359e3f6bc18" +checksum = "ce46b84ee931ac0aa6588e025fd616413eb09774522496257347e738e57ec851" dependencies = [ "dot-generator", "dot-structures", @@ -920,6 +950,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.3.3" @@ -937,30 +973,32 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] -name = "hermit-abi" -version = "0.3.2" +name = "id-arena" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] -name = "humantime" -version = "2.1.0" +name = "impl-trait-for-tuples" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "id-arena" -version = "2.2.1" +name = "indent" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +checksum = "d9f1a0777d972970f204fdf8ef319f1f4f8459131636d7e3c96c5d59570d0fa6" [[package]] name = "indexmap" @@ -969,21 +1007,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", "serde", ] [[package]] name = "indoc" -version = "2.0.2" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "761cde40c27e2a9877f8c928fd248b7eec9dd48623dd514b256858ca593fbba7" +checksum = "2c785eefb63ebd0e33416dfcb8d6da0bf27ce752843a45632a67bf10d4d4b5c4" [[package]] name = "insta" -version = "1.30.0" +version = "1.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28491f7753051e5704d4d0ae7860d45fae3238d7d235bc4289dcd45c48d3cec3" +checksum = "a0770b0a3d4c70567f0d58331f3088b0e4c4f56c9b8d764efe654b4a5d46de3a" dependencies = [ "console", "globset", @@ -1005,18 +1054,18 @@ dependencies = [ [[package]] name = "into-attr" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ec0dd848c05d2695dd73818984fb156ac8cbcbfdf4e474243590bfcc2468e9" +checksum = "18b48c537e49a709e678caec3753a7dba6854661a1eaa27675024283b3f8b376" dependencies = [ "dot-structures", ] [[package]] name = "into-attr-derive" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fbf3ed04003b36d48d9acb5ee7d66eaf1efa8318bc26571c6b86db7b05f12a" +checksum = "ecac7c1ae6cd2c6a3a64d1061a8bdc7f52ff62c26a831a2301e54c1b5d70d5b1" dependencies = [ "dot-generator", "dot-structures", @@ -1025,25 +1074,14 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.2", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.2", - "rustix 0.38.4", + "hermit-abi", + "rustix", "windows-sys 0.48.0", ] @@ -1056,11 +1094,20 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "keccak" @@ -1073,20 +1120,21 @@ dependencies = [ [[package]] name = "lalrpop" -version = "0.19.12" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8" dependencies = [ "ascii-canvas", "bit-set", "diff", "ena", "is-terminal", - "itertools", + "itertools 0.10.5", "lalrpop-util", "petgraph", + "pico-args", "regex", - "regex-syntax 0.6.29", + "regex-syntax", "string_cache", "term", "tiny-keccak", @@ -1095,9 +1143,9 @@ dependencies = [ [[package]] name = "lalrpop-util" -version = "0.19.12" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" +checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d" dependencies = [ "regex", ] @@ -1125,15 +1173,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "lock_api" @@ -1185,7 +1227,7 @@ dependencies = [ "matrixmultiply", "num-complex", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", "rawpointer", ] @@ -1203,7 +1245,7 @@ checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", "serde", ] @@ -1214,7 +1256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ "autocfg", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -1224,7 +1266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -1233,27 +1275,18 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "once_cell" version = "1.18.0" @@ -1266,6 +1299,31 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "parity-scale-codec" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -1316,15 +1374,15 @@ dependencies = [ [[package]] name = "path-clean" -version = "0.1.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" +checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" [[package]] name = "pest" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -1332,9 +1390,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -1342,22 +1400,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", @@ -1371,7 +1429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -1383,6 +1441,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1395,24 +1459,40 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1487,26 +1567,20 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.7.4", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.7.4" @@ -1525,20 +1599,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustix" -version = "0.37.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - [[package]] name = "rustix" version = "0.38.4" @@ -1548,21 +1608,21 @@ dependencies = [ "bitflags 2.3.3", "errno", "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys", "windows-sys 0.48.0", ] [[package]] name = "rustversion" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salsa" @@ -1571,7 +1631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b84d9f96071f3f3be0dc818eae3327625d8ebc95b58da37d6850724f31d3403" dependencies = [ "crossbeam-utils", - "indexmap", + "indexmap 1.9.3", "lock_api", "log", "oorandom", @@ -1602,43 +1662,88 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schemars" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.10.7" @@ -1736,26 +1841,31 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ - "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.23", + "rustix", "windows-sys 0.48.0", ] @@ -1781,69 +1891,65 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.28", ] [[package]] -name = "time" -version = "0.3.23" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ - "itoa", - "libc", - "num_threads", - "serde", - "time-core", - "time-macros", + "crunchy", ] [[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.2.10" +name = "toml" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ - "time-core", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "tiny-keccak" -version = "2.0.2" +name = "toml_datetime" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ - "crunchy", + "serde", ] [[package]] -name = "toml" -version = "0.4.10" +name = "toml_edit" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ + "indexmap 2.0.0", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -1869,9 +1975,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-segmentation" @@ -2076,6 +2182,24 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "xshell" version = "0.2.5" diff --git a/Cargo.toml b/Cargo.toml index e5adb52..eb423db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "caracal" description = "Starknet smart contract static analysis tool" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0" -clap = "4.1" +clap = { version = "4.1", features = ["derive"] } serde_json = "1.0" num-bigint = "0.4" num-traits = "0.2" @@ -18,17 +18,17 @@ num-integer = "0.1" termcolor = "1.2" graphviz-rust = "0.6.2" -cairo-lang-compiler = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-defs = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-plugins = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-starknet = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-filesystem = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-parser = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-syntax = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-semantic = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-utils = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-sierra-generator = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } -cairo-lang-sierra = { git = "https://github.com/starkware-libs/cairo.git", tag = "v1.1.1" } +cairo-lang-compiler = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-defs = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-plugins = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-starknet = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-filesystem = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-parser = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-syntax = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-semantic = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-utils = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-sierra-generator = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } +cairo-lang-sierra = { git = "https://github.com/starkware-libs/cairo.git", tag = "v2.1.0" } [dev-dependencies] insta = { version = "1.30", features = ["glob"] } diff --git a/README.md b/README.md index a421784..a07441a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Caracal is a static analyzer tool over the SIERRA representation for Starknet sm ## Installation ### Precompiled binaries -Precompiled binaries are available on our [releases page](https://github.com/crytic/caracal/releases). +Precompiled binaries are available on our [releases page](https://github.com/crytic/caracal/releases). If you are using Cairo compiler 1.x.x uses the binary v0.1.x otherwise if you are using the Cairo compiler 2.x.x uses v0.2.x. ### Building from source You need the Rust compiler and Cargo. @@ -66,18 +66,21 @@ caracal print path/to/dir --printer printer_to_use ``` ## Detectors -Num | Detector | What it Detects | Impact | Confidence ---- | --- | --- | --- | --- -1 | `controlled-library-call` | Library calls with a user controlled class hash | High | Medium -2 | `unchecked-l1-handler-from` | Detect L1 handlers without from address check | High | Medium -3 | `reentrancy` | Detect when a storage variable is read before an external call and written after | Medium | Medium -4 | `unused-events` | Events defined but not emitted | Medium | Medium -5 | `unused-return` | Unused return values | Medium | Medium -6 | `unenforced-view` | Function has view decorator but modifies state | Medium | Medium -7 | `unused-arguments` | Unused arguments | Low | Medium -8 | `reentrancy-benign` | Detect when a storage variable is written after an external call but not read before | Low | Medium -9 | `reentrancy-events` | Detect when an event is emitted after an external call leading to out-of-order events | Low | Medium -10 | `dead-code` | Private functions never used | Low | Medium +Num | Detector | What it Detects | Impact | Confidence | Cairo +--- | --- | --- | --- | --- | --- +1 | `controlled-library-call` | Library calls with a user controlled class hash | High | Medium | 1 & 2 +2 | `unchecked-l1-handler-from` | Detect L1 handlers without from address check | High | Medium | 1 & 2 +3 | `reentrancy` | Detect when a storage variable is read before an external call and written after | Medium | Medium | 1 & 2 +4 | `read-only-reentrancy` | Detect when a view function read a storage variable written after an external call | Medium | Medium | 1 & 2 +5 | `unused-events` | Events defined but not emitted | Medium | Medium | 1 & 2 +6 | `unused-return` | Unused return values | Medium | Medium | 1 & 2 +7 | `unenforced-view` | Function has view decorator but modifies state | Medium | Medium | 1 +8 | `unused-arguments` | Unused arguments | Low | Medium | 1 & 2 +9 | `reentrancy-benign` | Detect when a storage variable is written after an external call but not read before | Low | Medium | 1 & 2 +10 | `reentrancy-events` | Detect when an event is emitted after an external call leading to out-of-order events | Low | Medium | 1 & 2 +11 | `dead-code` | Private functions never used | Low | Medium | 1 & 2 + +The Cairo column represent the compiler version for which the detector is valid. ## Printers - `cfg`: Export the CFG of each function in a .dot file @@ -89,6 +92,5 @@ Check the wiki on the following topics: * [How to write a printer](https://github.com/crytic/caracal/wiki/How-to-write-a-printer) ## Limitations -- At the moment only Cairo 1 is supported (compiler version up to 1.1.1). - Inlined functions are not handled correctly. - Since it's working over the SIERRA representation it's not possible to report where an error is in the source code but we can only report SIERRA instructions/what's available in a SIERRA program. diff --git a/corelib/Scarb.toml b/corelib/Scarb.toml index 7df253b..b7383e0 100644 --- a/corelib/Scarb.toml +++ b/corelib/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "core" -version = "1.1.1" +version = "2.1.0-rc4" # NOTE: This is non-public, unstable Scarb's field, which instructs resolver that this package does not # depend on `core`, which is only true for this particular package. Nobody else should use it. diff --git a/corelib/src/array.cairo b/corelib/src/array.cairo index 71b9794..98b10d7 100644 --- a/corelib/src/array.cairo +++ b/corelib/src/array.cairo @@ -3,11 +3,13 @@ use traits::IndexView; use box::BoxTrait; use gas::withdraw_gas; use option::OptionTrait; +use serde::Serde; extern type Array; extern fn array_new() -> Array nopanic; extern fn array_append(ref arr: Array, value: T) nopanic; extern fn array_pop_front(ref arr: Array) -> Option> nopanic; +extern fn array_pop_front_consume(arr: Array) -> Option<(Array, Box)> nopanic; extern fn array_snapshot_pop_front(ref arr: @Array) -> Option> nopanic; extern fn array_snapshot_pop_back(ref arr: @Array) -> Option> nopanic; #[panic_with('Index out of bounds', array_at)] @@ -19,16 +21,7 @@ extern fn array_slice( ) -> Option<@Array> implicits(RangeCheck) nopanic; extern fn array_len(arr: @Array) -> usize nopanic; -trait ArrayTrait { - fn new() -> Array; - fn append(ref self: Array, value: T); - fn pop_front(ref self: Array) -> Option nopanic; - fn get(self: @Array, index: usize) -> Option>; - fn at(self: @Array, index: usize) -> @T; - fn len(self: @Array) -> usize; - fn is_empty(self: @Array) -> bool; - fn span(self: @Array) -> Span; -} +#[generate_trait] impl ArrayImpl of ArrayTrait { #[inline(always)] fn new() -> Array { @@ -42,7 +35,14 @@ impl ArrayImpl of ArrayTrait { fn pop_front(ref self: Array) -> Option nopanic { match array_pop_front(ref self) { Option::Some(x) => Option::Some(x.unbox()), - Option::None(_) => Option::None(()), + Option::None => Option::None, + } + } + #[inline(always)] + fn pop_front_consume(self: Array) -> Option<(Array, T)> nopanic { + match array_pop_front_consume(self) { + Option::Some((arr, x)) => Option::Some((arr, x.unbox())), + Option::None => Option::None, } } #[inline(always)] @@ -66,12 +66,53 @@ impl ArrayImpl of ArrayTrait { } } +impl ArrayDefault of Default> { + #[inline(always)] + fn default() -> Array { + ArrayTrait::new() + } +} + impl ArrayIndex of IndexView, usize, @T> { fn index(self: @Array, index: usize) -> @T { array_at(self, index).unbox() } } +impl ArraySerde, impl TDrop: Drop> of Serde> { + fn serialize(self: @Array, ref output: Array) { + self.len().serialize(ref output); + serialize_array_helper(self.span(), ref output); + } + fn deserialize(ref serialized: Span) -> Option> { + let length = *serialized.pop_front()?; + let mut arr = Default::default(); + deserialize_array_helper(ref serialized, arr, length) + } +} + +fn serialize_array_helper, impl TDrop: Drop>( + mut input: Span, ref output: Array +) { + match input.pop_front() { + Option::Some(value) => { + value.serialize(ref output); + serialize_array_helper(input, ref output); + }, + Option::None => {}, + } +} + +fn deserialize_array_helper, impl TDrop: Drop>( + ref serialized: Span, mut curr_output: Array, remaining: felt252 +) -> Option> { + if remaining == 0 { + return Option::Some(curr_output); + } + curr_output.append(TSerde::deserialize(ref serialized)?); + deserialize_array_helper(ref serialized, curr_output, remaining - 1) +} + // Impls for common generic types impl ArrayDrop> of Drop>; @@ -83,15 +124,20 @@ struct Span { impl SpanCopy of Copy>; impl SpanDrop of Drop>; -trait SpanTrait { - fn pop_front(ref self: Span) -> Option<@T>; - fn pop_back(ref self: Span) -> Option<@T>; - fn get(self: Span, index: usize) -> Option>; - fn at(self: Span, index: usize) -> @T; - fn slice(self: Span, start: usize, length: usize) -> Span; - fn len(self: Span) -> usize; - fn is_empty(self: Span) -> bool; +impl SpanSerde, impl TDrop: Drop> of Serde> { + fn serialize(self: @Span, ref output: Array) { + (*self).len().serialize(ref output); + serialize_array_helper(*self, ref output) + } + + fn deserialize(ref serialized: Span) -> Option> { + let length = *serialized.pop_front()?; + let mut arr = array_new(); + Option::Some(deserialize_array_helper(ref serialized, arr, length)?.span()) + } } + +#[generate_trait] impl SpanImpl of SpanTrait { #[inline(always)] fn pop_front(ref self: Span) -> Option<@T> { @@ -100,7 +146,7 @@ impl SpanImpl of SpanTrait { self = Span { snapshot }; match item { Option::Some(x) => Option::Some(x.unbox()), - Option::None(_) => Option::None(()), + Option::None => Option::None, } } #[inline(always)] @@ -110,7 +156,7 @@ impl SpanImpl of SpanTrait { self = Span { snapshot }; match item { Option::Some(x) => Option::Some(x.unbox()), - Option::None(_) => Option::None(()), + Option::None => Option::None, } } #[inline(always)] @@ -152,7 +198,7 @@ impl ArrayTCloneImpl, impl TDrop: Drop> of Clone { response.append(TClone::clone(v)); }, - Option::None(_) => { + Option::None => { break (); }, }; diff --git a/corelib/src/box.cairo b/corelib/src/box.cairo index 7887fb3..f3eeeae 100644 --- a/corelib/src/box.cairo +++ b/corelib/src/box.cairo @@ -8,11 +8,7 @@ impl BoxTDrop> of Drop>; extern fn into_box(value: T) -> Box nopanic; extern fn unbox(box: Box) -> T nopanic; -trait BoxTrait { - fn new(value: T) -> Box nopanic; - fn unbox(self: Box) -> T nopanic; -} - +#[generate_trait] impl BoxImpl of BoxTrait { #[inline(always)] fn new(value: T) -> Box nopanic { diff --git a/corelib/src/bytes_31.cairo b/corelib/src/bytes_31.cairo new file mode 100644 index 0000000..ae5f655 --- /dev/null +++ b/corelib/src/bytes_31.cairo @@ -0,0 +1,53 @@ +use traits::{Into, TryInto}; +use option::OptionTrait; + +#[derive(Copy, Drop)] +extern type bytes31; + +extern fn bytes31_const() -> bytes31 nopanic; +extern fn bytes31_try_from_felt252(value: felt252) -> Option implicits(RangeCheck) nopanic; +extern fn bytes31_to_felt252(value: bytes31) -> felt252 nopanic; + +impl Bytes31IntoFelt252 of Into { + fn into(self: bytes31) -> felt252 { + bytes31_to_felt252(self) + } +} + +impl Felt252TryIntoBytes31 of TryInto { + fn try_into(self: felt252) -> Option { + bytes31_try_from_felt252(self) + } +} + +// TODO(yuval): implement all `into`s using `integer::upcast(self)`. +impl U8IntoBytes31 of Into { + fn into(self: u8) -> bytes31 { + let as_felt: felt252 = self.into(); + as_felt.try_into().unwrap() + } +} +impl U16IntoBytes31 of Into { + fn into(self: u16) -> bytes31 { + let as_felt: felt252 = self.into(); + as_felt.try_into().unwrap() + } +} +impl U32IntoBytes31 of Into { + fn into(self: u32) -> bytes31 { + let as_felt: felt252 = self.into(); + as_felt.try_into().unwrap() + } +} +impl U64IntoBytes31 of Into { + fn into(self: u64) -> bytes31 { + let as_felt: felt252 = self.into(); + as_felt.try_into().unwrap() + } +} +impl U128IntoBytes31 of Into { + fn into(self: u128) -> bytes31 { + let as_felt: felt252 = self.into(); + as_felt.try_into().unwrap() + } +} diff --git a/corelib/src/debug.cairo b/corelib/src/debug.cairo index 8ef0b3e..4d3ce5a 100644 --- a/corelib/src/debug.cairo +++ b/corelib/src/debug.cairo @@ -12,7 +12,7 @@ use option::Option; // // get_caller_address().print(); // -// let mut arr = ArrayTrait::new(); +// let mut arr = Default::default(); // arr.append('1234567890123456789012345678901'); // arr.append('Sca'); // arr.append('SomeVeryLongMessage'); @@ -21,9 +21,7 @@ use option::Option; extern fn print(message: Array) nopanic; fn print_felt252(message: felt252) { - let mut arr = ArrayTrait::new(); - arr.append(message); - print(arr); + print(array![message]); } trait PrintTrait { diff --git a/corelib/src/dict.cairo b/corelib/src/dict.cairo index 588d2a6..ba6f294 100644 --- a/corelib/src/dict.cairo +++ b/corelib/src/dict.cairo @@ -25,7 +25,6 @@ extern fn felt252_dict_squash( ) -> SquashedFelt252Dict implicits(RangeCheck, GasBuiltin, SegmentArena) nopanic; trait Felt252DictTrait { - fn new() -> Felt252Dict; /// Inserts the given value for the given key. /// /// Requires the `Destruct` trait, as the previous value is dropped. @@ -38,11 +37,6 @@ trait Felt252DictTrait { fn entry(self: Felt252Dict, key: felt252) -> (Felt252DictEntry, T) nopanic; } impl Felt252DictImpl> of Felt252DictTrait { - #[inline(always)] - fn new() -> Felt252Dict { - felt252_dict_new() - } - #[inline] fn insert>(ref self: Felt252Dict, key: felt252, value: T) { let (entry, _prev_value) = felt252_dict_entry_get(self, key); @@ -79,6 +73,13 @@ impl Felt252DictEntryImpl> of Felt252DictE } } +impl Felt252DictDefault of Default> { + #[inline(always)] + fn default() -> Felt252Dict { + felt252_dict_new() + } +} + impl Felt252DictDestruct< T, impl TDrop: Drop, impl TDefault: Felt252DictValue > of Destruct> { diff --git a/corelib/src/ec.cairo b/corelib/src/ec.cairo index 8362f1f..9b5f2d7 100644 --- a/corelib/src/ec.cairo +++ b/corelib/src/ec.cairo @@ -5,7 +5,7 @@ use array::ArrayTrait; use zeroable::IsZeroResult; use traits::Into; -mod StarkCurve { +mod stark_curve { /// The STARK Curve is defined by the equation `y^2 = x^3 + ALPHA*x + BETA`. const ALPHA: felt252 = 1; /// The STARK Curve is defined by the equation `y^2 = x^3 + ALPHA*x + BETA`. @@ -36,7 +36,7 @@ extern fn ec_point_try_new_nz(x: felt252, y: felt252) -> Option fn ec_point_try_new(x: felt252, y: felt252) -> Option { match ec_point_try_new_nz(:x, :y) { Option::Some(pt) => Option::Some(pt.into()), - Option::None(()) => Option::None(()), + Option::None => Option::None, } } @@ -50,7 +50,7 @@ extern fn ec_point_from_x_nz(x: felt252) -> Option implicits(Ran fn ec_point_from_x(x: felt252) -> Option { match ec_point_from_x_nz(:x) { Option::Some(pt) => Option::Some(pt.into()), - Option::None(()) => Option::None(()), + Option::None => Option::None, } } @@ -63,7 +63,7 @@ extern fn ec_point_is_zero(p: EcPoint) -> IsZeroResult nopanic; /// Converts `p` to `NonZeroEcPoint`. Panics if `p` is the zero point. fn ec_point_non_zero(p: EcPoint) -> NonZeroEcPoint { match ec_point_is_zero(p) { - IsZeroResult::Zero(()) => panic_with_felt252('Zero point'), + IsZeroResult::Zero => panic_with_felt252('Zero point'), IsZeroResult::NonZero(p_nz) => p_nz, } } @@ -89,14 +89,14 @@ extern fn ec_state_add_mul(ref s: EcState, m: felt252, p: NonZeroEcPoint) implic fn ec_state_finalize(s: EcState) -> EcPoint { match ec_state_try_finalize_nz(s) { Option::Some(pt) => pt.into(), - Option::None(()) => ec_point_zero(), + Option::None => ec_point_zero(), } } /// Computes the product of an EC point `p` by the given scalar `m`. fn ec_mul(p: EcPoint, m: felt252) -> EcPoint { match ec_point_is_zero(p) { - IsZeroResult::Zero(()) => p, + IsZeroResult::Zero => p, IsZeroResult::NonZero(p_nz) => { let mut state = ec_state_init(); ec_state_add_mul(ref state, m, p_nz); @@ -110,13 +110,13 @@ impl EcPointAdd of Add { // TODO(lior): Implement using a libfunc to make it more efficient. fn add(lhs: EcPoint, rhs: EcPoint) -> EcPoint { let lhs_nz = match ec_point_is_zero(lhs) { - IsZeroResult::Zero(()) => { + IsZeroResult::Zero => { return rhs; }, IsZeroResult::NonZero(pt) => pt, }; let rhs_nz = match ec_point_is_zero(rhs) { - IsZeroResult::Zero(()) => { + IsZeroResult::Zero => { return lhs; }, IsZeroResult::NonZero(pt) => pt, @@ -139,7 +139,7 @@ impl EcPointSub of Sub { /// Computes the difference between two points on the curve. fn sub(lhs: EcPoint, rhs: EcPoint) -> EcPoint { match ec_point_is_zero(rhs) { - IsZeroResult::Zero(()) => { + IsZeroResult::Zero => { // lhs - 0 = lhs. return lhs; }, diff --git a/corelib/src/ecdsa.cairo b/corelib/src/ecdsa.cairo index cf87d56..c6ece79 100644 --- a/corelib/src/ecdsa.cairo +++ b/corelib/src/ecdsa.cairo @@ -13,7 +13,7 @@ use ec::{ // standard ECDSA. // While this does not allow to create valid signatures if one does not possess the private key, // it means that the signature algorithm used should be modified accordingly. -// Namely, it should check that `r, s < StarkCurve::ORDER`. +// Namely, it should check that `r, s < stark_curve::ORDER`. // // Arguments: // * `message_hash` - the signed message. @@ -29,21 +29,21 @@ fn check_ecdsa_signature( ) -> bool { // TODO(lior): Change to || once short circuiting is supported. - // Check that s != 0 (mod StarkCurve.ORDER). + // Check that s != 0 (mod stark_curve::ORDER). if (signature_s == 0) { return false; } - if (signature_s == ec::StarkCurve::ORDER) { + if (signature_s == ec::stark_curve::ORDER) { return false; } - if (signature_r == ec::StarkCurve::ORDER) { + if (signature_r == ec::stark_curve::ORDER) { return false; } // Check that the public key is the x coordinate of a point on the curve and get such a point. let public_key_point = match ec::ec_point_from_x(public_key) { Option::Some(point) => point, - Option::None(()) => { + Option::None => { return false; }, }; @@ -52,15 +52,15 @@ fn check_ecdsa_signature( // Note that this ensures that `r != 0`. let signature_r_point = match ec::ec_point_from_x(signature_r) { Option::Some(point) => point, - Option::None(()) => { + Option::None => { return false; }, }; // Retrieve the generator point. - let gen_point = match ec_point_try_new(ec::StarkCurve::GEN_X, ec::StarkCurve::GEN_Y) { + let gen_point = match ec_point_try_new(ec::stark_curve::GEN_X, ec::stark_curve::GEN_Y) { Option::Some(point) => point, - Option::None(()) => { + Option::None => { return false; }, }; @@ -75,7 +75,7 @@ fn check_ecdsa_signature( let sR: EcPoint = ec_mul(signature_r_point, signature_s); let sR_x = match ec_point_is_zero(sR) { - IsZeroResult::Zero(()) => { + IsZeroResult::Zero => { return false; }, IsZeroResult::NonZero(pt) => { @@ -87,7 +87,7 @@ fn check_ecdsa_signature( let zG: EcPoint = ec_mul(gen_point, message_hash); let rQ: EcPoint = ec_mul(public_key_point, signature_r); match ec_point_is_zero(zG + rQ) { - IsZeroResult::Zero(()) => {}, + IsZeroResult::Zero => {}, IsZeroResult::NonZero(pt) => { let (x, y) = ec_point_unwrap(pt); if (x == sR_x) { @@ -97,7 +97,7 @@ fn check_ecdsa_signature( }; match ec_point_is_zero(zG - rQ) { - IsZeroResult::Zero(()) => {}, + IsZeroResult::Zero => {}, IsZeroResult::NonZero(pt) => { let (x, y) = ec_point_unwrap(pt); if (x == sR_x) { diff --git a/corelib/src/hash.cairo b/corelib/src/hash.cairo index 0bde091..4947295 100644 --- a/corelib/src/hash.cairo +++ b/corelib/src/hash.cairo @@ -137,7 +137,3 @@ impl TupleSize4LegacyHash< E3LegacyHash::hash(state, e3) } } - -fn foo(input: Span) -> starknet::SyscallResult { - starknet::syscalls::keccak_syscall(input) -} diff --git a/corelib/src/integer.cairo b/corelib/src/integer.cairo index 9172c69..d17e778 100644 --- a/corelib/src/integer.cairo +++ b/corelib/src/integer.cairo @@ -1,7 +1,10 @@ -use zeroable::IsZeroResult; use option::OptionTrait; use result::ResultTrait; use traits::{Into, TryInto, Default, Felt252DictValue}; +use zeroable::{IsZeroResult, NonZeroIntoImpl, Zeroable}; +use serde::Serde; +use array::ArrayTrait; +use array::SpanTrait; // TODO(spapini): Add method for const creation from Integer. trait NumericLiteral; @@ -12,6 +15,15 @@ extern type u128; impl NumericLiteralu128 of NumericLiteral; extern fn u128_const() -> u128 nopanic; +impl U128Serde of Serde { + fn serialize(self: @u128, ref output: Array) { + Into::::into(*self).serialize(ref output); + } + fn deserialize(ref serialized: Span) -> Option { + Option::Some(((*serialized.pop_front()?).try_into())?) + } +} + enum U128sFromFelt252Result { Narrow: u128, Wide: (u128, u128), @@ -22,7 +34,7 @@ extern fn u128s_from_felt252(a: felt252) -> U128sFromFelt252Result implicits(Ran fn u128_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) nopanic { match u128s_from_felt252(a) { U128sFromFelt252Result::Narrow(x) => Option::Some(x), - U128sFromFelt252Result::Wide(x) => Option::None(()), + U128sFromFelt252Result::Wide(x) => Option::None, } } @@ -88,7 +100,7 @@ fn u128_overflowing_mul(lhs: u128, rhs: u128) -> (u128, bool) implicits(RangeChe fn u128_checked_add(lhs: u128, rhs: u128) -> Option implicits(RangeCheck) nopanic { match u128_overflowing_add(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -108,7 +120,7 @@ impl U128AddEq of AddEq { fn u128_checked_sub(lhs: u128, rhs: u128) -> Option implicits(RangeCheck) nopanic { match u128_overflowing_sub(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -128,7 +140,7 @@ fn u128_checked_mul(lhs: u128, rhs: u128) -> Option implicits(RangeCheck) let (top_word, bottom_word) = u128_wide_mul(lhs, rhs); match u128_to_felt252(top_word) { 0 => Option::Some(bottom_word), - _ => Option::None(()), + _ => Option::None, } } @@ -145,16 +157,22 @@ impl U128MulEq of MulEq { } #[panic_with('u128 is 0', u128_as_non_zero)] -fn u128_try_as_non_zero(a: u128) -> Option> implicits() nopanic { +fn u128_try_as_non_zero(a: u128) -> Option> nopanic { match u128_is_zero(a) { - IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::Zero => Option::None, IsZeroResult::NonZero(x) => Option::Some(x), } } +impl U128TryIntoNonZero of TryInto> { + fn try_into(self: u128) -> Option> { + Option::Some(u128_as_non_zero(self)) + } +} + impl U128Div of Div { fn div(lhs: u128, rhs: u128) -> u128 { - let (q, r) = u128_safe_divmod(lhs, u128_as_non_zero(rhs)); + let (q, r) = u128_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); q } } @@ -167,7 +185,7 @@ impl U128DivEq of DivEq { impl U128Rem of Rem { fn rem(lhs: u128, rhs: u128) -> u128 { - let (q, r) = u128_safe_divmod(lhs, u128_as_non_zero(rhs)); + let (q, r) = u128_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); r } } @@ -192,12 +210,12 @@ extern fn u128_eq(lhs: u128, rhs: u128) -> bool implicits() nopanic; impl U128PartialEq of PartialEq { #[inline(always)] - fn eq(lhs: u128, rhs: u128) -> bool { - u128_eq(lhs, rhs) + fn eq(lhs: @u128, rhs: @u128) -> bool { + u128_eq(*lhs, *rhs) } #[inline(always)] - fn ne(lhs: u128, rhs: u128) -> bool { - !(lhs == rhs) + fn ne(lhs: @u128, rhs: @u128) -> bool { + !(*lhs == *rhs) } } @@ -264,14 +282,23 @@ extern fn u8_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) no extern fn u8_eq(lhs: u8, rhs: u8) -> bool implicits() nopanic; +impl U8Serde of Serde { + fn serialize(self: @u8, ref output: Array) { + Into::::into(*self).serialize(ref output); + } + fn deserialize(ref serialized: Span) -> Option { + Option::Some(((*serialized.pop_front()?).try_into())?) + } +} + impl U8PartialEq of PartialEq { #[inline(always)] - fn eq(lhs: u8, rhs: u8) -> bool { - u8_eq(lhs, rhs) + fn eq(lhs: @u8, rhs: @u8) -> bool { + u8_eq(*lhs, *rhs) } #[inline(always)] - fn ne(lhs: u8, rhs: u8) -> bool { - !(lhs == rhs) + fn ne(lhs: @u8, rhs: @u8) -> bool { + !(*lhs == *rhs) } } @@ -314,7 +341,7 @@ fn u8_wrapping_sub(lhs: u8, rhs: u8) -> u8 implicits(RangeCheck) nopanic { fn u8_checked_add(lhs: u8, rhs: u8) -> Option implicits(RangeCheck) nopanic { match u8_overflowing_add(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -333,7 +360,7 @@ impl U8AddEq of AddEq { fn u8_checked_sub(lhs: u8, rhs: u8) -> Option implicits(RangeCheck) nopanic { match u8_overflowing_sub(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -354,7 +381,7 @@ extern fn u8_sqrt(value: u8) -> u8 implicits(RangeCheck) nopanic; impl U8Mul of Mul { fn mul(lhs: u8, rhs: u8) -> u8 { - u8_try_from_felt252(u16_to_felt252(u8_wide_mul(lhs, rhs))).expect('u8_mul Overflow') + u8_wide_mul(lhs, rhs).try_into().expect('u8_mul Overflow') } } impl U8MulEq of MulEq { @@ -368,16 +395,22 @@ extern fn u8_is_zero(a: u8) -> IsZeroResult implicits() nopanic; extern fn u8_safe_divmod(lhs: u8, rhs: NonZero) -> (u8, u8) implicits(RangeCheck) nopanic; #[panic_with('u8 is 0', u8_as_non_zero)] -fn u8_try_as_non_zero(a: u8) -> Option> implicits() nopanic { +fn u8_try_as_non_zero(a: u8) -> Option> nopanic { match u8_is_zero(a) { - IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::Zero => Option::None, IsZeroResult::NonZero(x) => Option::Some(x), } } +impl U8TryIntoNonZero of TryInto> { + fn try_into(self: u8) -> Option> { + Option::Some(u8_as_non_zero(self)) + } +} + impl U8Div of Div { fn div(lhs: u8, rhs: u8) -> u8 { - let (q, r) = u8_safe_divmod(lhs, u8_as_non_zero(rhs)); + let (q, r) = u8_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); q } } @@ -390,7 +423,7 @@ impl U8DivEq of DivEq { impl U8Rem of Rem { fn rem(lhs: u8, rhs: u8) -> u8 { - let (q, r) = u8_safe_divmod(lhs, u8_as_non_zero(rhs)); + let (q, r) = u8_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); r } } @@ -412,6 +445,28 @@ impl U8BitNot of BitNot { BoundedInt::max() - a } } +extern fn u8_bitwise(lhs: u8, rhs: u8) -> (u8, u8, u8) implicits(Bitwise) nopanic; +impl U8BitAnd of BitAnd { + #[inline(always)] + fn bitand(lhs: u8, rhs: u8) -> u8 { + let (v, _, _) = u8_bitwise(lhs, rhs); + v + } +} +impl U8BitXor of BitXor { + #[inline(always)] + fn bitxor(lhs: u8, rhs: u8) -> u8 { + let (_, v, _) = u8_bitwise(lhs, rhs); + v + } +} +impl U8BitOr of BitOr { + #[inline(always)] + fn bitor(lhs: u8, rhs: u8) -> u8 { + let (_, _, v) = u8_bitwise(lhs, rhs); + v + } +} #[derive(Copy, Drop)] extern type u16; @@ -424,14 +479,23 @@ extern fn u16_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) extern fn u16_eq(lhs: u16, rhs: u16) -> bool implicits() nopanic; +impl U16Serde of Serde { + fn serialize(self: @u16, ref output: Array) { + Into::::into(*self).serialize(ref output); + } + fn deserialize(ref serialized: Span) -> Option { + Option::Some(((*serialized.pop_front()?).try_into())?) + } +} + impl U16PartialEq of PartialEq { #[inline(always)] - fn eq(lhs: u16, rhs: u16) -> bool { - u16_eq(lhs, rhs) + fn eq(lhs: @u16, rhs: @u16) -> bool { + u16_eq(*lhs, *rhs) } #[inline(always)] - fn ne(lhs: u16, rhs: u16) -> bool { - !(lhs == rhs) + fn ne(lhs: @u16, rhs: @u16) -> bool { + !(*lhs == *rhs) } } @@ -474,7 +538,7 @@ fn u16_wrapping_sub(lhs: u16, rhs: u16) -> u16 implicits(RangeCheck) nopanic { fn u16_checked_add(lhs: u16, rhs: u16) -> Option implicits(RangeCheck) nopanic { match u16_overflowing_add(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -493,7 +557,7 @@ impl U16AddEq of AddEq { fn u16_checked_sub(lhs: u16, rhs: u16) -> Option implicits(RangeCheck) nopanic { match u16_overflowing_sub(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -514,8 +578,7 @@ extern fn u16_sqrt(value: u16) -> u8 implicits(RangeCheck) nopanic; impl U16Mul of Mul { fn mul(lhs: u16, rhs: u16) -> u16 { - // TODO(orizi): Use direct conversion, instead of going through felt252. - u16_try_from_felt252(u32_to_felt252(u16_wide_mul(lhs, rhs))).expect('u16_mul Overflow') + u16_wide_mul(lhs, rhs).try_into().expect('u16_mul Overflow') } } impl U16MulEq of MulEq { @@ -529,16 +592,22 @@ extern fn u16_is_zero(a: u16) -> IsZeroResult implicits() nopanic; extern fn u16_safe_divmod(lhs: u16, rhs: NonZero) -> (u16, u16) implicits(RangeCheck) nopanic; #[panic_with('u16 is 0', u16_as_non_zero)] -fn u16_try_as_non_zero(a: u16) -> Option> implicits() nopanic { +fn u16_try_as_non_zero(a: u16) -> Option> nopanic { match u16_is_zero(a) { - IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::Zero => Option::None, IsZeroResult::NonZero(x) => Option::Some(x), } } +impl U16TryIntoNonZero of TryInto> { + fn try_into(self: u16) -> Option> { + Option::Some(u16_as_non_zero(self)) + } +} + impl U16Div of Div { fn div(lhs: u16, rhs: u16) -> u16 { - let (q, r) = u16_safe_divmod(lhs, u16_as_non_zero(rhs)); + let (q, r) = u16_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); q } } @@ -551,7 +620,7 @@ impl U16DivEq of DivEq { impl U16Rem of Rem { fn rem(lhs: u16, rhs: u16) -> u16 { - let (q, r) = u16_safe_divmod(lhs, u16_as_non_zero(rhs)); + let (q, r) = u16_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); r } } @@ -573,6 +642,28 @@ impl U16BitNot of BitNot { BoundedInt::max() - a } } +extern fn u16_bitwise(lhs: u16, rhs: u16) -> (u16, u16, u16) implicits(Bitwise) nopanic; +impl U16BitAnd of BitAnd { + #[inline(always)] + fn bitand(lhs: u16, rhs: u16) -> u16 { + let (v, _, _) = u16_bitwise(lhs, rhs); + v + } +} +impl U16BitXor of BitXor { + #[inline(always)] + fn bitxor(lhs: u16, rhs: u16) -> u16 { + let (_, v, _) = u16_bitwise(lhs, rhs); + v + } +} +impl U16BitOr of BitOr { + #[inline(always)] + fn bitor(lhs: u16, rhs: u16) -> u16 { + let (_, _, v) = u16_bitwise(lhs, rhs); + v + } +} #[derive(Copy, Drop)] extern type u32; @@ -585,14 +676,23 @@ extern fn u32_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) extern fn u32_eq(lhs: u32, rhs: u32) -> bool implicits() nopanic; +impl U32Serde of Serde { + fn serialize(self: @u32, ref output: Array) { + Into::::into(*self).serialize(ref output); + } + fn deserialize(ref serialized: Span) -> Option { + Option::Some(((*serialized.pop_front()?).try_into())?) + } +} + impl U32PartialEq of PartialEq { #[inline(always)] - fn eq(lhs: u32, rhs: u32) -> bool { - u32_eq(lhs, rhs) + fn eq(lhs: @u32, rhs: @u32) -> bool { + u32_eq(*lhs, *rhs) } #[inline(always)] - fn ne(lhs: u32, rhs: u32) -> bool { - !(lhs == rhs) + fn ne(lhs: @u32, rhs: @u32) -> bool { + !(*lhs == *rhs) } } @@ -635,7 +735,7 @@ fn u32_wrapping_sub(lhs: u32, rhs: u32) -> u32 implicits(RangeCheck) nopanic { fn u32_checked_add(lhs: u32, rhs: u32) -> Option implicits(RangeCheck) nopanic { match u32_overflowing_add(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -654,7 +754,7 @@ impl U32AddEq of AddEq { fn u32_checked_sub(lhs: u32, rhs: u32) -> Option implicits(RangeCheck) nopanic { match u32_overflowing_sub(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -675,8 +775,7 @@ extern fn u32_sqrt(value: u32) -> u16 implicits(RangeCheck) nopanic; impl U32Mul of Mul { fn mul(lhs: u32, rhs: u32) -> u32 { - // TODO(orizi): Use direct conversion, instead of going through felt252. - u32_try_from_felt252(u64_to_felt252(u32_wide_mul(lhs, rhs))).expect('u32_mul Overflow') + u32_wide_mul(lhs, rhs).try_into().expect('u32_mul Overflow') } } impl U32MulEq of MulEq { @@ -690,16 +789,22 @@ extern fn u32_is_zero(a: u32) -> IsZeroResult implicits() nopanic; extern fn u32_safe_divmod(lhs: u32, rhs: NonZero) -> (u32, u32) implicits(RangeCheck) nopanic; #[panic_with('u32 is 0', u32_as_non_zero)] -fn u32_try_as_non_zero(a: u32) -> Option> implicits() nopanic { +fn u32_try_as_non_zero(a: u32) -> Option> nopanic { match u32_is_zero(a) { - IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::Zero => Option::None, IsZeroResult::NonZero(x) => Option::Some(x), } } +impl U32TryIntoNonZero of TryInto> { + fn try_into(self: u32) -> Option> { + Option::Some(u32_as_non_zero(self)) + } +} + impl U32Div of Div { fn div(lhs: u32, rhs: u32) -> u32 { - let (q, r) = u32_safe_divmod(lhs, u32_as_non_zero(rhs)); + let (q, r) = u32_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); q } } @@ -712,7 +817,7 @@ impl U32DivEq of DivEq { impl U32Rem of Rem { fn rem(lhs: u32, rhs: u32) -> u32 { - let (q, r) = u32_safe_divmod(lhs, u32_as_non_zero(rhs)); + let (q, r) = u32_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); r } } @@ -734,6 +839,28 @@ impl U32BitNot of BitNot { BoundedInt::max() - a } } +extern fn u32_bitwise(lhs: u32, rhs: u32) -> (u32, u32, u32) implicits(Bitwise) nopanic; +impl U32BitAnd of BitAnd { + #[inline(always)] + fn bitand(lhs: u32, rhs: u32) -> u32 { + let (v, _, _) = u32_bitwise(lhs, rhs); + v + } +} +impl U32BitXor of BitXor { + #[inline(always)] + fn bitxor(lhs: u32, rhs: u32) -> u32 { + let (_, v, _) = u32_bitwise(lhs, rhs); + v + } +} +impl U32BitOr of BitOr { + #[inline(always)] + fn bitor(lhs: u32, rhs: u32) -> u32 { + let (_, _, v) = u32_bitwise(lhs, rhs); + v + } +} #[derive(Copy, Drop)] extern type u64; @@ -746,14 +873,23 @@ extern fn u64_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) extern fn u64_eq(lhs: u64, rhs: u64) -> bool implicits() nopanic; +impl U64Serde of Serde { + fn serialize(self: @u64, ref output: Array) { + Into::::into(*self).serialize(ref output); + } + fn deserialize(ref serialized: Span) -> Option { + Option::Some(((*serialized.pop_front()?).try_into())?) + } +} + impl U64PartialEq of PartialEq { #[inline(always)] - fn eq(lhs: u64, rhs: u64) -> bool { - u64_eq(lhs, rhs) + fn eq(lhs: @u64, rhs: @u64) -> bool { + u64_eq(*lhs, *rhs) } #[inline(always)] - fn ne(lhs: u64, rhs: u64) -> bool { - !(lhs == rhs) + fn ne(lhs: @u64, rhs: @u64) -> bool { + !(*lhs == *rhs) } } @@ -796,7 +932,7 @@ fn u64_wrapping_sub(lhs: u64, rhs: u64) -> u64 implicits(RangeCheck) nopanic { fn u64_checked_add(lhs: u64, rhs: u64) -> Option implicits(RangeCheck) nopanic { match u64_overflowing_add(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -815,7 +951,7 @@ impl U64AddEq of AddEq { fn u64_checked_sub(lhs: u64, rhs: u64) -> Option implicits(RangeCheck) nopanic { match u64_overflowing_sub(lhs, rhs) { Result::Ok(r) => Option::Some(r), - Result::Err(r) => Option::None(()), + Result::Err(r) => Option::None, } } @@ -836,8 +972,7 @@ extern fn u64_sqrt(value: u64) -> u32 implicits(RangeCheck) nopanic; impl U64Mul of Mul { fn mul(lhs: u64, rhs: u64) -> u64 { - // TODO(orizi): Use direct conversion, instead of going through felt252. - u64_try_from_felt252(u128_to_felt252(u64_wide_mul(lhs, rhs))).expect('u64_mul Overflow') + u64_wide_mul(lhs, rhs).try_into().expect('u64_mul Overflow') } } impl U64MulEq of MulEq { @@ -851,16 +986,22 @@ extern fn u64_is_zero(a: u64) -> IsZeroResult implicits() nopanic; extern fn u64_safe_divmod(lhs: u64, rhs: NonZero) -> (u64, u64) implicits(RangeCheck) nopanic; #[panic_with('u64 is 0', u64_as_non_zero)] -fn u64_try_as_non_zero(a: u64) -> Option> implicits() nopanic { +fn u64_try_as_non_zero(a: u64) -> Option> nopanic { match u64_is_zero(a) { - IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::Zero => Option::None, IsZeroResult::NonZero(x) => Option::Some(x), } } +impl U64TryIntoNonZero of TryInto> { + fn try_into(self: u64) -> Option> { + Option::Some(u64_as_non_zero(self)) + } +} + impl U64Div of Div { fn div(lhs: u64, rhs: u64) -> u64 { - let (q, r) = u64_safe_divmod(lhs, u64_as_non_zero(rhs)); + let (q, r) = u64_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); q } } @@ -873,7 +1014,7 @@ impl U64DivEq of DivEq { impl U64Rem of Rem { fn rem(lhs: u64, rhs: u64) -> u64 { - let (q, r) = u64_safe_divmod(lhs, u64_as_non_zero(rhs)); + let (q, r) = u64_safe_divmod(lhs, rhs.try_into().expect('Division by 0')); r } } @@ -895,8 +1036,30 @@ impl U64BitNot of BitNot { BoundedInt::max() - a } } +extern fn u64_bitwise(lhs: u64, rhs: u64) -> (u64, u64, u64) implicits(Bitwise) nopanic; +impl U64BitAnd of BitAnd { + #[inline(always)] + fn bitand(lhs: u64, rhs: u64) -> u64 { + let (v, _, _) = u64_bitwise(lhs, rhs); + v + } +} +impl U64BitXor of BitXor { + #[inline(always)] + fn bitxor(lhs: u64, rhs: u64) -> u64 { + let (_, v, _) = u64_bitwise(lhs, rhs); + v + } +} +impl U64BitOr of BitOr { + #[inline(always)] + fn bitor(lhs: u64, rhs: u64) -> u64 { + let (_, _, v) = u64_bitwise(lhs, rhs); + v + } +} -#[derive(Copy, Drop, PartialEq, Serde)] +#[derive(Copy, Drop, PartialEq, Serde, starknet::Store)] struct u256 { low: u128, high: u128, @@ -943,8 +1106,8 @@ fn u256_overflow_mul(lhs: u256, rhs: u256) -> (u256, bool) { Result::Ok(high) => ( high, overflow_value1 != 0_u128 - | overflow_value2 != 0_u128 - | (lhs.high > 0_u128 & rhs.high > 0_u128) + || overflow_value2 != 0_u128 + || (lhs.high > 0_u128 && rhs.high > 0_u128) ), Result::Err(high) => (high, true), }; @@ -958,7 +1121,7 @@ fn u256_overflow_mul(lhs: u256, rhs: u256) -> (u256, bool) { fn u256_checked_add(lhs: u256, rhs: u256) -> Option implicits(RangeCheck) nopanic { let (r, overflow) = u256_overflowing_add(lhs, rhs); if overflow { - Option::None(()) + Option::None } else { Option::Some(r) } @@ -980,7 +1143,7 @@ impl U256AddEq of AddEq { fn u256_checked_sub(lhs: u256, rhs: u256) -> Option implicits(RangeCheck) nopanic { let (r, overflow) = u256_overflow_sub(lhs, rhs); if overflow { - Option::None(()) + Option::None } else { Option::Some(r) } @@ -1001,7 +1164,7 @@ impl U256SubEq of SubEq { fn u256_checked_mul(lhs: u256, rhs: u256) -> Option implicits(RangeCheck) { let (r, overflow) = u256_overflow_mul(lhs, rhs); if overflow { - Option::None(()) + Option::None } else { Option::Some(r) } @@ -1070,22 +1233,38 @@ fn u256_from_felt252(lhs: felt252) -> u256 implicits(RangeCheck) nopanic { } extern fn u256_is_zero(a: u256) -> IsZeroResult implicits() nopanic; + +/// Calculates division with remainder of a u256 by a non-zero u256. +/// Additionally returns a `U128MulGuarantee` that is required for validating the calculation. extern fn u256_safe_divmod( lhs: u256, rhs: NonZero -) -> (u256, u256) implicits(RangeCheck) nopanic; +) -> (u256, u256, U128MulGuarantee) implicits(RangeCheck) nopanic; + +/// Calculates division with remainder of a u256 by a non-zero u256. +#[inline(always)] +fn u256_safe_div_rem(lhs: u256, rhs: NonZero) -> (u256, u256) implicits(RangeCheck) nopanic { + let (q, r, _) = u256_safe_divmod(lhs, rhs); + (q, r) +} extern fn u256_sqrt(a: u256) -> u128 implicits(RangeCheck) nopanic; #[panic_with('u256 is 0', u256_as_non_zero)] -fn u256_try_as_non_zero(a: u256) -> Option> implicits() nopanic { +fn u256_try_as_non_zero(a: u256) -> Option> nopanic { match u256_is_zero(a) { - IsZeroResult::Zero(()) => Option::None(()), + IsZeroResult::Zero => Option::None, IsZeroResult::NonZero(x) => Option::Some(x), } } +impl U256TryIntoNonZero of TryInto> { + fn try_into(self: u256) -> Option> { + Option::Some(u256_as_non_zero(self)) + } +} + impl U256Div of Div { fn div(lhs: u256, rhs: u256) -> u256 { - let (q, r) = u256_safe_divmod(lhs, u256_as_non_zero(rhs)); + let (q, r) = u256_safe_div_rem(lhs, rhs.try_into().expect('Division by 0')); q } } @@ -1098,7 +1277,7 @@ impl U256DivEq of DivEq { impl U256Rem of Rem { fn rem(lhs: u256, rhs: u256) -> u256 { - let (q, r) = u256_safe_divmod(lhs, u256_as_non_zero(rhs)); + let (q, r) = u256_safe_div_rem(lhs, rhs.try_into().expect('Division by 0')); r } } @@ -1111,7 +1290,7 @@ impl U256RemEq of RemEq { impl U256DivRem of DivRem { fn div_rem(lhs: u256, rhs: NonZero) -> (u256, u256) { - u256_safe_divmod(lhs, rhs) + u256_safe_div_rem(lhs, rhs) } } @@ -1313,12 +1492,12 @@ impl U256TryIntoFelt252 of TryInto { fn try_into(self: u256) -> Option { let FELT252_PRIME_HIGH = 0x8000000000000110000000000000000_u128; if self.high > FELT252_PRIME_HIGH { - return Option::None(()); + return Option::None; } if self.high == FELT252_PRIME_HIGH { // since FELT252_PRIME_LOW is 1. if self.low != 0 { - return Option::None(()); + return Option::None; } } Option::Some( @@ -1326,6 +1505,31 @@ impl U256TryIntoFelt252 of TryInto { ) } } +impl I8IntoFelt252 of Into { + fn into(self: i8) -> felt252 { + i8_to_felt252(self) + } +} +impl I16IntoFelt252 of Into { + fn into(self: i16) -> felt252 { + i16_to_felt252(self) + } +} +impl I32IntoFelt252 of Into { + fn into(self: i32) -> felt252 { + i32_to_felt252(self) + } +} +impl I64IntoFelt252 of Into { + fn into(self: i64) -> felt252 { + i64_to_felt252(self) + } +} +impl I128IntoFelt252 of Into { + fn into(self: i128) -> felt252 { + i128_to_felt252(self) + } +} // TODO(lior): Restrict the function (using traits) in the high-level compiler so that wrong types // will not lead to Sierra errors. @@ -1335,6 +1539,36 @@ extern fn upcast(x: FromType) -> ToType nopanic; // will not lead to Sierra errors. extern fn downcast(x: FromType) -> Option implicits(RangeCheck) nopanic; +// Marks `FromType` as upcastable to `ToType`. +// Do not add user code implementing this trait. +trait Upcastable; +impl UpcastableU8U16 of Upcastable {} +impl UpcastableU8U32 of Upcastable {} +impl UpcastableU8U64 of Upcastable {} +impl UpcastableU8U128 of Upcastable {} +impl UpcastableU16U32 of Upcastable {} +impl UpcastableU16U64 of Upcastable {} +impl UpcastableU16U128 of Upcastable {} +impl UpcastableU32U64 of Upcastable {} +impl UpcastableU32U128 of Upcastable {} +impl UpcastableU64U128 of Upcastable {} +// Marks `FromType` as downcastable to `ToType`. +// Do not add user code implementing this trait. +trait Downcastable; +impl DowncastableU128U64 of Downcastable {} +impl DowncastableU128U32 of Downcastable {} +impl DowncastableU128U16 of Downcastable {} +impl DowncastableU128U8 of Downcastable {} + +impl DowncastableU64U32 of Downcastable {} +impl DowncastableU64U16 of Downcastable {} +impl DowncastableU64U8 of Downcastable {} + +impl DowncastableU32U16 of Downcastable {} +impl DowncastableU32U8 of Downcastable {} + +impl DowncastableU16U8 of Downcastable {} + /// Default values impl U8Default of Default { #[inline(always)] @@ -1414,122 +1648,293 @@ impl U128Felt252DictValue of Felt252DictValue { 0 } } -impl U8IntoU16 of Into { - fn into(self: u8) -> u16 { + +impl UpcastableInto> of Into { + fn into(self: From) -> To { upcast(self) } } -impl U16TryIntoU8 of TryInto { - fn try_into(self: u16) -> Option { +impl DowncastableTryInto< + From, To, impl FromToDowncastable: Downcastable +> of TryInto { + fn try_into(self: From) -> Option { downcast(self) } } -impl U8IntoU32 of Into { - fn into(self: u8) -> u32 { - upcast(self) +impl U8IntoU256 of Into { + fn into(self: u8) -> u256 { + u256 { low: upcast(self), high: 0_u128 } } } -impl U32TryIntoU8 of TryInto { - fn try_into(self: u32) -> Option { - downcast(self) +impl U256TryIntoU8 of TryInto { + fn try_into(self: u256) -> Option { + let u256{low: low, high: high } = self; + + if high != 0 { + return Option::None; + } + + low.try_into() } } -impl U8IntoU64 of Into { - fn into(self: u8) -> u64 { - upcast(self) +impl U16IntoU256 of Into { + fn into(self: u16) -> u256 { + u256 { low: upcast(self), high: 0_u128 } } } -impl U64TryIntoU8 of TryInto { - fn try_into(self: u64) -> Option { - downcast(self) +impl U256TryIntoU16 of TryInto { + fn try_into(self: u256) -> Option { + let u256{low: low, high: high } = self; + + if high != 0 { + return Option::None; + } + + low.try_into() } } -impl U8IntoU128 of Into { - fn into(self: u8) -> u128 { - upcast(self) +impl U32IntoU256 of Into { + fn into(self: u32) -> u256 { + u256 { low: upcast(self), high: 0_u128 } } } -impl U128TryIntoU8 of TryInto { - fn try_into(self: u128) -> Option { - downcast(self) +impl U256TryIntoU32 of TryInto { + fn try_into(self: u256) -> Option { + let u256{low: low, high: high } = self; + + if high != 0 { + return Option::None; + } + + low.try_into() } } -impl U16IntoU32 of Into { - fn into(self: u16) -> u32 { - upcast(self) +impl U64IntoU256 of Into { + fn into(self: u64) -> u256 { + u256 { low: upcast(self), high: 0_u128 } } } -impl U32TryIntoU16 of TryInto { - fn try_into(self: u32) -> Option { - downcast(self) +impl U256TryIntoU64 of TryInto { + fn try_into(self: u256) -> Option { + let u256{low: low, high: high } = self; + + if high != 0 { + return Option::None; + } + + low.try_into() } } -impl U16IntoU64 of Into { - fn into(self: u16) -> u64 { - upcast(self) +impl U128IntoU256 of Into { + fn into(self: u128) -> u256 { + u256 { low: self, high: 0_u128 } } } -impl U64TryIntoU16 of TryInto { - fn try_into(self: u64) -> Option { - downcast(self) +impl U256TryIntoU128 of TryInto { + fn try_into(self: u256) -> Option { + let u256{low: low, high: high } = self; + + if high != 0 { + return Option::None; + } + + Option::Some(low) } } -impl U16IntoU128 of Into { - fn into(self: u16) -> u128 { - upcast(self) +// === Zeroable === + +impl U8Zeroable of Zeroable { + fn zero() -> u8 { + 0 + } + #[inline(always)] + fn is_zero(self: u8) -> bool { + self == U8Zeroable::zero() + } + #[inline(always)] + fn is_non_zero(self: u8) -> bool { + self != U8Zeroable::zero() } } -impl U128TryIntoU16 of TryInto { - fn try_into(self: u128) -> Option { - downcast(self) +impl U16Zeroable of Zeroable { + fn zero() -> u16 { + 0 + } + #[inline(always)] + fn is_zero(self: u16) -> bool { + self == U16Zeroable::zero() + } + #[inline(always)] + fn is_non_zero(self: u16) -> bool { + self != U16Zeroable::zero() } } -impl U32IntoU64 of Into { - fn into(self: u32) -> u64 { - upcast(self) +impl U32Zeroable of Zeroable { + fn zero() -> u32 { + 0 + } + #[inline(always)] + fn is_zero(self: u32) -> bool { + self == U32Zeroable::zero() + } + #[inline(always)] + fn is_non_zero(self: u32) -> bool { + self != U32Zeroable::zero() } } -impl U64TryIntoU32 of TryInto { - fn try_into(self: u64) -> Option { - downcast(self) +impl U64Zeroable of Zeroable { + fn zero() -> u64 { + 0 + } + #[inline(always)] + fn is_zero(self: u64) -> bool { + self == U64Zeroable::zero() + } + #[inline(always)] + fn is_non_zero(self: u64) -> bool { + self != U64Zeroable::zero() } } -impl U32IntoU128 of Into { - fn into(self: u32) -> u128 { - upcast(self) +impl U128Zeroable of Zeroable { + fn zero() -> u128 { + 0 + } + #[inline(always)] + fn is_zero(self: u128) -> bool { + self == U128Zeroable::zero() + } + #[inline(always)] + fn is_non_zero(self: u128) -> bool { + self != U128Zeroable::zero() } } -impl U128TryIntoU32 of TryInto { - fn try_into(self: u128) -> Option { - downcast(self) +impl U256Zeroable of Zeroable { + fn zero() -> u256 { + 0 + } + #[inline(always)] + fn is_zero(self: u256) -> bool { + self == U256Zeroable::zero() + } + #[inline(always)] + fn is_non_zero(self: u256) -> bool { + self != U256Zeroable::zero() } } -impl U64IntoU128 of Into { - fn into(self: u64) -> u128 { - upcast(self) +#[derive(Copy, Drop)] +extern type i8; +impl NumericLiterali8 of NumericLiteral; +extern fn i8_const() -> i8 nopanic; +extern fn i8_to_felt252(a: i8) -> felt252 nopanic; + +extern fn i8_is_zero(a: i8) -> IsZeroResult implicits() nopanic; +extern fn i8_eq(lhs: i8, rhs: i8) -> bool implicits() nopanic; + +impl I8PartialEq of PartialEq { + #[inline(always)] + fn eq(lhs: @i8, rhs: @i8) -> bool { + i8_eq(*lhs, *rhs) + } + #[inline(always)] + fn ne(lhs: @i8, rhs: @i8) -> bool { + !(*lhs == *rhs) } } -impl U128TryIntoU64 of TryInto { - fn try_into(self: u128) -> Option { - downcast(self) +#[derive(Copy, Drop)] +extern type i16; +impl NumericLiterali16 of NumericLiteral; +extern fn i16_const() -> i16 nopanic; +extern fn i16_to_felt252(a: i16) -> felt252 nopanic; + +extern fn i16_is_zero(a: i16) -> IsZeroResult implicits() nopanic; +extern fn i16_eq(lhs: i16, rhs: i16) -> bool implicits() nopanic; + +impl I16PartialEq of PartialEq { + #[inline(always)] + fn eq(lhs: @i16, rhs: @i16) -> bool { + i16_eq(*lhs, *rhs) + } + #[inline(always)] + fn ne(lhs: @i16, rhs: @i16) -> bool { + !(*lhs == *rhs) + } +} + +#[derive(Copy, Drop)] +extern type i32; +impl NumericLiterali32 of NumericLiteral; +extern fn i32_const() -> i32 nopanic; +extern fn i32_to_felt252(a: i32) -> felt252 nopanic; + +extern fn i32_is_zero(a: i32) -> IsZeroResult implicits() nopanic; +extern fn i32_eq(lhs: i32, rhs: i32) -> bool implicits() nopanic; + +impl I32PartialEq of PartialEq { + #[inline(always)] + fn eq(lhs: @i32, rhs: @i32) -> bool { + i32_eq(*lhs, *rhs) + } + #[inline(always)] + fn ne(lhs: @i32, rhs: @i32) -> bool { + !(*lhs == *rhs) + } +} + +#[derive(Copy, Drop)] +extern type i64; +impl NumericLiterali64 of NumericLiteral; +extern fn i64_const() -> i64 nopanic; +extern fn i64_to_felt252(a: i64) -> felt252 nopanic; + +extern fn i64_is_zero(a: i64) -> IsZeroResult implicits() nopanic; +extern fn i64_eq(lhs: i64, rhs: i64) -> bool implicits() nopanic; + +impl I64PartialEq of PartialEq { + #[inline(always)] + fn eq(lhs: @i64, rhs: @i64) -> bool { + i64_eq(*lhs, *rhs) + } + #[inline(always)] + fn ne(lhs: @i64, rhs: @i64) -> bool { + !(*lhs == *rhs) + } +} + +#[derive(Copy, Drop)] +extern type i128; +impl NumericLiterali128 of NumericLiteral; +extern fn i128_const() -> i128 nopanic; +extern fn i128_to_felt252(a: i128) -> felt252 nopanic; + +extern fn i128_is_zero(a: i128) -> IsZeroResult implicits() nopanic; +extern fn i128_eq(lhs: i128, rhs: i128) -> bool implicits() nopanic; + +impl I128PartialEq of PartialEq { + #[inline(always)] + fn eq(lhs: @i128, rhs: @i128) -> bool { + i128_eq(*lhs, *rhs) + } + #[inline(always)] + fn ne(lhs: @i128, rhs: @i128) -> bool { + !(*lhs == *rhs) } } diff --git a/corelib/src/keccak.cairo b/corelib/src/keccak.cairo index 598bbce..0da072b 100644 --- a/corelib/src/keccak.cairo +++ b/corelib/src/keccak.cairo @@ -3,7 +3,9 @@ use integer::TryInto; use option::OptionTrait; use starknet::SyscallResultTrait; +const KECCAK_FULL_RATE_IN_BYTES: usize = 136; const KECCAK_FULL_RATE_IN_U64S: usize = 17; +const BYTES_IN_U64_WORD: usize = 8; fn u128_to_u64(input: u128) -> u64 { @@ -12,13 +14,13 @@ fn u128_to_u64(input: u128) -> u64 { fn u128_split(input: u128) -> (u64, u64) { let (high, low) = integer::u128_safe_divmod( - input, integer::u128_try_as_non_zero(0x10000000000000000).unwrap() + input, 0x10000000000000000_u128.try_into().unwrap() ); (u128_to_u64(high), u128_to_u64(low)) } -fn keccak_add_uint256_le(ref keccak_input: Array::, v: u256) { +fn keccak_add_u256_le(ref keccak_input: Array::, v: u256) { let (high, low) = u128_split(v.low); keccak_input.append(low); keccak_input.append(high); @@ -28,27 +30,28 @@ fn keccak_add_uint256_le(ref keccak_input: Array::, v: u256) { } -// Computes the keccak256 of multiple uint256 values. -// The values are interpreted as little-endian. -fn keccak_uint256s_le(mut input: Span) -> u256 { - let mut keccak_input: Array:: = ArrayTrait::new(); +// Computes the keccak256 of multiple u256 values. +// The input values are interpreted as little-endian. +// The 32-byte result is represented as a little-endian u256. +fn keccak_u256s_le_inputs(mut input: Span) -> u256 { + let mut keccak_input: Array:: = Default::default(); loop { match input.pop_front() { Option::Some(v) => { - keccak_add_uint256_le(ref keccak_input, *v); + keccak_add_u256_le(ref keccak_input, *v); }, - Option::None(_) => { + Option::None => { break (); }, }; }; - add_padding(ref keccak_input); + add_padding(ref keccak_input, 0, 0); starknet::syscalls::keccak_syscall(keccak_input.span()).unwrap_syscall() } -fn keccak_add_uint256_be(ref keccak_input: Array::, v: u256) { +fn keccak_add_u256_be(ref keccak_input: Array::, v: u256) { let (high, low) = u128_split(integer::u128_byte_reverse(v.high)); keccak_input.append(low); keccak_input.append(high); @@ -57,51 +60,99 @@ fn keccak_add_uint256_be(ref keccak_input: Array::, v: u256) { keccak_input.append(high); } -// Computes the keccak256 of multiple uint256 values. -// The values are interpreted as big-endian. -fn keccak_uint256s_be(mut input: Span) -> u256 { - let mut keccak_input: Array:: = ArrayTrait::new(); +// Computes the keccak256 of multiple u256 values. +// The input values are interpreted as big-endian. +// The 32-byte result is represented as a little-endian u256. +fn keccak_u256s_be_inputs(mut input: Span) -> u256 { + let mut keccak_input: Array:: = Default::default(); loop { match input.pop_front() { Option::Some(v) => { - keccak_add_uint256_be(ref keccak_input, *v); + keccak_add_u256_be(ref keccak_input, *v); }, - Option::None(_) => { + Option::None => { break (); }, }; }; - add_padding(ref keccak_input); + add_padding(ref keccak_input, 0, 0); starknet::syscalls::keccak_syscall(keccak_input.span()).unwrap_syscall() } +// Computes the keccak of `input` + `last_input_num_bytes` LSB bytes of `last_input_word`. +// To use this function, split the input into words of 64 bits (little endian). +// For example, to compute keccak('Hello world!'), use: +// inputs = [8031924123371070792, 560229490] +// where: +// 8031924123371070792 == int.from_bytes(b'Hello wo', 'little') +// 560229490 == int.from_bytes(b'rld!', 'little') +// +// Returns the hash as a little endian u256. +fn cairo_keccak(ref input: Array, last_input_word: u64, last_input_num_bytes: usize) -> u256 { + add_padding(ref input, last_input_word, last_input_num_bytes); + starknet::syscalls::keccak_syscall(input.span()).unwrap_syscall() +} -// The padding in keccak256 is 10*1; -fn add_padding(ref input: Array) { - let divisor = integer::u32_try_as_non_zero(KECCAK_FULL_RATE_IN_U64S).unwrap(); - let (q, r) = integer::u32_safe_divmod(input.len(), divisor); - let padding_len = KECCAK_FULL_RATE_IN_U64S - r; - // padding_len is in the range [1, KECCAK_FULL_RATE_IN_U64S]. +// The padding in keccak256 is "1 0* 1". +// `last_input_num_bytes` (0-7) is the number of bytes in the last u64 input - `last_input_word`. +fn add_padding(ref input: Array, last_input_word: u64, last_input_num_bytes: usize) { + let words_divisor = KECCAK_FULL_RATE_IN_U64S.try_into().unwrap(); + // `last_block_num_full_words` is in range [0, KECCAK_FULL_RATE_IN_U64S - 1] + let (_, last_block_num_full_words) = integer::u32_safe_divmod(input.len(), words_divisor); + // `last_block_num_bytes` is in range [0, KECCAK_FULL_RATE_IN_BYTES - 1] + let last_block_num_bytes = last_block_num_full_words * BYTES_IN_U64_WORD + last_input_num_bytes; + + // The first word to append would be of the form + // 0x1<`last_input_num_bytes` LSB bytes of `last_input_word`>. + // For example, for `last_input_num_bytes == 4`: + // 0x1000000 + (last_input_word & 0xffffff) + let first_word_to_append = if last_input_num_bytes == 0 { + // This case is handled separately to avoid unnecessary computations. + 1 + } else { + let first_padding_byte_part = if last_input_num_bytes == 1 { + 0x100 + } else if last_input_num_bytes == 2 { + 0x10000 + } else if last_input_num_bytes == 3 { + 0x1000000 + } else if last_input_num_bytes == 4 { + 0x100000000 + } else if last_input_num_bytes == 5 { + 0x10000000000 + } else if last_input_num_bytes == 6 { + 0x1000000000000 + } else if last_input_num_bytes == 7 { + 0x100000000000000 + } else { + panic_with_felt252('Keccak last input word >7b') + }; + let (_, r) = integer::u64_safe_divmod( + last_input_word, first_padding_byte_part.try_into().unwrap() + ); + first_padding_byte_part + r + }; - if padding_len == 1 { - input.append(0x8000000000000001); - return (); + if last_block_num_full_words == KECCAK_FULL_RATE_IN_U64S - 1 { + input.append(0x8000000000000000 + first_word_to_append); + return; } - // padding_len >= 2; - input.append(1); - finalize_padding(ref input, padding_len - 1); + // last_block_num_full_words < KECCAK_FULL_RATE_IN_U64S - 1 + input.append(first_word_to_append); + finalize_padding(ref input, KECCAK_FULL_RATE_IN_U64S - 1 - last_block_num_full_words); } -// Finalize the padding by appending 0*1. -fn finalize_padding(ref input: Array, padding_len: u32) { - if (padding_len == 1) { +// Finalize the padding by appending "0* 1". +fn finalize_padding(ref input: Array, num_padding_words: u32) { + if (num_padding_words == 1) { input.append(0x8000000000000000); - return (); + return; } input.append(0); - finalize_padding(ref input, padding_len - 1); + finalize_padding(ref input, num_padding_words - 1); } + diff --git a/corelib/src/lib.cairo b/corelib/src/lib.cairo index fb46dd1..01c04b9 100644 --- a/corelib/src/lib.cairo +++ b/corelib/src/lib.cairo @@ -4,13 +4,28 @@ use traits::{ PartialEq, PartialOrd, Rem, RemEq, Sub, SubEq, TupleSize0Copy, TupleSize0Drop, TupleSize0PartialEq, TupleSize1Copy, TupleSize1Drop, TupleSize1PartialEq, TupleSize2Copy, TupleSize2Drop, TupleSize3Copy, TupleSize3Drop, TupleSize4Copy, TupleSize4Drop, Not, Neg, Into, - TryInto, Index, IndexView, Destruct, Default, Felt252DictValue + TryInto, Index, IndexView, Destruct, Default, Felt252DictValue, PanicDestruct }; +use serde::Serde; +use array::SpanTrait; #[derive(Copy, Drop)] enum bool { - False: (), - True: (), + False, + True, +} + +impl BoolSerde of Serde { + fn serialize(self: @bool, ref output: Array) { + if *self { + 1 + } else { + 0 + }.serialize(ref output); + } + fn deserialize(ref serialized: Span) -> Option { + Option::Some(*serialized.pop_front()? != 0) + } } extern fn bool_and_impl(lhs: bool, rhs: bool) -> (bool, ) implicits() nopanic; @@ -50,15 +65,20 @@ impl BoolBitXor of BitXor { } } -extern fn bool_eq(lhs: bool, rhs: bool) -> bool implicits() nopanic; impl BoolPartialEq of PartialEq { #[inline(always)] - fn eq(lhs: bool, rhs: bool) -> bool { - bool_eq(lhs, rhs) + fn eq(lhs: @bool, rhs: @bool) -> bool { + match lhs { + bool::False => !*rhs, + bool::True => *rhs, + } } #[inline(always)] - fn ne(lhs: bool, rhs: bool) -> bool { - !(lhs == rhs) + fn ne(lhs: @bool, rhs: @bool) -> bool { + match lhs { + bool::False => *rhs, + bool::True => !*rhs, + } } } @@ -71,6 +91,12 @@ impl BoolFelt252DictValue of Felt252DictValue { } extern fn bool_to_felt252(a: bool) -> felt252 implicits() nopanic; +impl BoolIntoFelt252 of Into { + #[inline(always)] + fn into(self: bool) -> felt252 implicits() nopanic { + bool_to_felt252(self) + } +} // General purpose implicits. extern type RangeCheck; @@ -81,6 +107,15 @@ extern type SegmentArena; extern type felt252; extern fn felt252_const() -> felt252 nopanic; +impl Felt252Serde of Serde { + fn serialize(self: @felt252, ref output: Array) { + output.append(*self); + } + fn deserialize(ref serialized: Span) -> Option { + Option::Some(*serialized.pop_front()?) + } +} + impl Felt252Add of Add { #[inline(always)] fn add(lhs: felt252, rhs: felt252) -> felt252 { @@ -135,20 +170,29 @@ extern fn felt252_div(lhs: felt252, rhs: NonZero) -> felt252 nopanic; impl Felt252PartialEq of PartialEq { #[inline(always)] - fn eq(lhs: felt252, rhs: felt252) -> bool { - match lhs - rhs { + fn eq(lhs: @felt252, rhs: @felt252) -> bool { + match *lhs - *rhs { 0 => bool::True(()), _ => bool::False(()), } } #[inline(always)] - fn ne(lhs: felt252, rhs: felt252) -> bool { - !(lhs == rhs) + fn ne(lhs: @felt252, rhs: @felt252) -> bool { + !(*lhs == *rhs) } } extern fn felt252_is_zero(lhs: felt252) -> zeroable::IsZeroResult nopanic; +impl Felt252TryIntoNonZero of TryInto> { + fn try_into(self: felt252) -> Option> { + match felt252_is_zero(self) { + zeroable::IsZeroResult::Zero => Option::None, + zeroable::IsZeroResult::NonZero(x) => Option::Some(x), + } + } +} + impl Felt252Default of Default { #[inline(always)] fn default() -> felt252 nopanic { @@ -211,13 +255,18 @@ mod ecdsa; // Integer. mod integer; use integer::{ - NumericLiteral, u128, u128_const, u128_sqrt, u128_is_zero, u8, u8_const, u16, u16_const, u32, - u32_const, u64, u64_const, u256, u256_sqrt, Felt252TryIntoU8, U8IntoFelt252, Felt252TryIntoU16, - U16IntoFelt252, Felt252TryIntoU32, U32IntoFelt252, Felt252TryIntoU64, U64IntoFelt252, - Felt252TryIntoU128, U128IntoFelt252, U16TryIntoU8, U32TryIntoU16, U64TryIntoU32, U128TryIntoU64, - Felt252IntoU256, Bitwise + i8, i8_const, I8IntoFelt252, i16, i16_const, I16IntoFelt252, i32, i32_const, I32IntoFelt252, + i64, i64_const, I64IntoFelt252, i128, i128_const, I128IntoFelt252, NumericLiteral, u128, + u128_const, u128_sqrt, u128_is_zero, u8, u8_const, u16, u16_const, u32, u32_const, u64, + u64_const, u256, u256_sqrt, Felt252TryIntoU8, U8IntoFelt252, Felt252TryIntoU16, U16IntoFelt252, + Felt252TryIntoU32, U32IntoFelt252, Felt252TryIntoU64, U64IntoFelt252, Felt252TryIntoU128, + U128IntoFelt252, Felt252IntoU256, Bitwise }; +// Math. +mod math; + +// Cmp. mod cmp; // Gas. @@ -226,18 +275,14 @@ use gas::{BuiltinCosts, GasBuiltin, get_builtin_costs}; // Panics. -enum PanicResult { - Ok: T, - Err: Array, -} +mod panics; +use panics::{panic, Panic, PanicResult}; + enum never {} -extern fn panic(data: Array) -> never; #[inline(always)] fn panic_with_felt252(err_code: felt252) -> never { - let mut data = ArrayTrait::new(); - data.append(err_code); - panic(data) + panic(array![err_code]) } #[inline(always)] @@ -274,6 +319,10 @@ mod internal; mod zeroable; use zeroable::{Zeroable, NonZero}; +// bytes31. +mod bytes_31; +use bytes_31::{bytes31, bytes31_const, Bytes31IntoFelt252, Felt252TryIntoBytes31}; + #[cfg(test)] mod test; diff --git a/corelib/src/math.cairo b/corelib/src/math.cairo new file mode 100644 index 0000000..ea22092 --- /dev/null +++ b/corelib/src/math.cairo @@ -0,0 +1,181 @@ +use zeroable::{IsZeroResult, NonZeroIntoImpl, Zeroable}; +use traits::{Into, TryInto}; +use option::OptionTrait; +use integer::{u256_wide_mul, u512_safe_div_rem_by_u256}; + +// TODO(yuval): use signed integers once supported. +// TODO(yuval): use a single impl of a trait with associated impls, once associated impls are +// supported. +/// Extended GCD: finds (g, s, t, sub_direction) such that +/// `g = gcd(a, b) = s * a - t * b` if `sub_direction` is true, or +/// `g = gcd(a, b) = t * b - s * a` if `sub_direction` is false. +/// `(s, -t)` or `(-s, t)` are the Bezout coefficients (according to `sub_direction`). +/// +/// Uses the Extended Euclidean algorithm. +fn egcd< + T, + impl TCopyImpl: Copy, + impl TDropImpl: Drop, + impl TAddImpl: Add, + impl TMulImpl: Mul, + impl TDivRemImpl: DivRem, + impl TZeroableImpl: Zeroable, + impl TOneableImpl: Oneable, + impl TTryIntoNonZeroImpl: TryInto>, +>( + a: NonZero, b: NonZero +) -> (T, T, T, bool) { + let (q, r) = TDivRemImpl::div_rem(a.into(), b); + + if r.is_zero() { + return (b.into(), TZeroableImpl::zero(), TOneableImpl::one(), false); + } + + // `sign` (1 for true, -1 for false) is the sign of `g` in the current iteration. + // 0 is considered negative for this purpose. + let (g, s, t, sign) = egcd(b, r.try_into().unwrap()); + // We know that `a = q*b + r` and that `s*b - t*r = sign*g`. + // So `t*a - (s + q*t)*b = t*r - s*b = sign*g`. + // Thus we pick `new_s = t`, `new_t = s + q*t`, `new_sign = !sign`. + (g, t, s + q * t, !sign) +} + +// TODO(yuval): use signed integers once supported. +/// Returns the inverse of `a` modulo `n`, or None if `gcd(a, n) > 1`. +fn inv_mod< + T, + impl TCopyImpl: Copy, + impl TDropImpl: Drop, + impl TAddImpl: Add, + impl TSubImpl: Sub, + impl TMulImpl: Mul, + impl TDivRemImpl: DivRem, + impl TZeroableImpl: Zeroable, + impl TOneableImpl: Oneable, + impl TTryIntoNonZeroImpl: TryInto>, +>( + a: NonZero, n: NonZero +) -> Option { + if TOneableImpl::is_one(n.into()) { + return Option::Some(TZeroableImpl::zero()); + } + let (g, s, _, sub_direction) = egcd(a, n); + if g.is_one() { + // `1 = g = gcd(a, n) = +-(s*a - t*n) => s*a = +-1 (mod n)`. + // The absolute values of Bezout coefficients are guaranteed to be `< n`. + // With n > 1 and gcd = 1, `s` can't be 0. + if sub_direction { + // `s` is the Bezout coefficient, `0 < s < n`. + Option::Some(s) + } else { + // `-s` is the Bezout coefficient. + // `-n < -s < 0 => 0 < n - s < n`, and `n - s = -s (mod n)`. + Option::Some(n.into() - s) + } + } else { + Option::None + } +} + +/// Returns `a / b (mod n)`, or None if `b` is not invertible modulo `n`. +fn u256_div_mod_n(a: u256, b: NonZero, n: NonZero) -> Option { + let inv_b = inv_mod(b, n)?; + let quotient = u256_wide_mul(a, inv_b); + let (_, quotient_mod_n) = u512_safe_div_rem_by_u256(quotient, n); + Option::Some(quotient_mod_n) +} + +// === Oneable === + +trait Oneable { + /// Returns the multiplicative identity element of Self, 1. + fn one() -> T; + /// Returns whether self is equal to 1, the multiplicative identity element. + fn is_one(self: T) -> bool; + /// Returns whether self is not equal to 1, the multiplicative identity element. + fn is_non_one(self: T) -> bool; +} + +impl U8Oneable of Oneable { + fn one() -> u8 { + 1 + } + #[inline(always)] + fn is_one(self: u8) -> bool { + self == U8Oneable::one() + } + #[inline(always)] + fn is_non_one(self: u8) -> bool { + self != U8Oneable::one() + } +} + +impl U16Oneable of Oneable { + fn one() -> u16 { + 1 + } + #[inline(always)] + fn is_one(self: u16) -> bool { + self == U16Oneable::one() + } + #[inline(always)] + fn is_non_one(self: u16) -> bool { + self != U16Oneable::one() + } +} + +impl U32Oneable of Oneable { + fn one() -> u32 { + 1 + } + #[inline(always)] + fn is_one(self: u32) -> bool { + self == U32Oneable::one() + } + #[inline(always)] + fn is_non_one(self: u32) -> bool { + self != U32Oneable::one() + } +} + +impl U64Oneable of Oneable { + fn one() -> u64 { + 1 + } + #[inline(always)] + fn is_one(self: u64) -> bool { + self == U64Oneable::one() + } + #[inline(always)] + fn is_non_one(self: u64) -> bool { + self != U64Oneable::one() + } +} + +impl U128Oneable of Oneable { + fn one() -> u128 { + 1 + } + #[inline(always)] + fn is_one(self: u128) -> bool { + self == U128Oneable::one() + } + #[inline(always)] + fn is_non_one(self: u128) -> bool { + self != U128Oneable::one() + } +} + +impl U256Oneable of Oneable { + fn one() -> u256 { + 1 + } + #[inline(always)] + fn is_one(self: u256) -> bool { + self == U256Oneable::one() + } + #[inline(always)] + fn is_non_one(self: u256) -> bool { + self != U256Oneable::one() + } +} diff --git a/corelib/src/nullable.cairo b/corelib/src/nullable.cairo index 4ed6d30..f945c3d 100644 --- a/corelib/src/nullable.cairo +++ b/corelib/src/nullable.cairo @@ -5,7 +5,7 @@ use traits::Felt252DictValue; extern type Nullable; enum FromNullableResult { - Null: (), + Null, NotNull: Box, } @@ -20,7 +20,7 @@ trait NullableTrait { impl NullableImpl of NullableTrait { fn deref(self: Nullable) -> T { match match_nullable(self) { - FromNullableResult::Null(()) => panic_with_felt252('Attempted to deref null value'), + FromNullableResult::Null => panic_with_felt252('Attempted to deref null value'), FromNullableResult::NotNull(value) => value.unbox(), } } diff --git a/corelib/src/option.cairo b/corelib/src/option.cairo index fc282f2..ba6125d 100644 --- a/corelib/src/option.cairo +++ b/corelib/src/option.cairo @@ -1,9 +1,34 @@ use array::ArrayTrait; +use serde::Serde; +use array::SpanTrait; enum Option { Some: T, - None: (), + None, } + +impl OptionSerde, impl TDrop: Drop> of Serde> { + fn serialize(self: @Option, ref output: Array) { + match self { + Option::Some(x) => { + 0.serialize(ref output); + x.serialize(ref output) + }, + Option::None => 1.serialize(ref output), + } + } + fn deserialize(ref serialized: Span) -> Option> { + let variant = *serialized.pop_front()?; + if variant == 0 { + Option::Some(Option::Some(Serde::::deserialize(ref serialized)?)) + } else if variant == 1 { + Option::Some(Option::None) + } else { + Option::None + } + } +} + trait OptionTrait { /// If `val` is `Option::Some(x)`, returns `x`. Otherwise, panics with `err`. fn expect(self: Option, err: felt252) -> T; @@ -19,7 +44,7 @@ impl OptionTraitImpl of OptionTrait { fn expect(self: Option, err: felt252) -> T { match self { Option::Some(x) => x, - Option::None(_) => panic_with_felt252(err), + Option::None => panic_with_felt252(err), } } #[inline(always)] @@ -30,14 +55,14 @@ impl OptionTraitImpl of OptionTrait { fn is_some(self: @Option) -> bool { match self { Option::Some(_) => true, - Option::None(_) => false, + Option::None => false, } } #[inline(always)] fn is_none(self: @Option) -> bool { match self { Option::Some(_) => false, - Option::None(_) => true, + Option::None => true, } } } diff --git a/corelib/src/panics.cairo b/corelib/src/panics.cairo new file mode 100644 index 0000000..1541a81 --- /dev/null +++ b/corelib/src/panics.cairo @@ -0,0 +1,10 @@ +use array::Array; + +struct Panic {} + +enum PanicResult { + Ok: T, + Err: (Panic, Array), +} + +extern fn panic(data: Array) -> never; diff --git a/corelib/src/poseidon.cairo b/corelib/src/poseidon.cairo index b1ab40d..42017ac 100644 --- a/corelib/src/poseidon.cairo +++ b/corelib/src/poseidon.cairo @@ -35,7 +35,7 @@ fn _poseidon_hash_span_inner( ) -> felt252 { let x = match span.pop_front() { Option::Some(x) => x, - Option::None(()) => { + Option::None => { // Pad input with [1, 0]. let (s0, s1, s2) = hades_permutation(state.s0 + 1, state.s1, state.s2); return s0; @@ -43,7 +43,7 @@ fn _poseidon_hash_span_inner( }; let y = match span.pop_front() { Option::Some(y) => y, - Option::None(()) => { + Option::None => { // Add x and pad with [0]. let (s0, s1, s2) = hades_permutation(state.s0 + *x, state.s1 + 1, state.s2); return s0; diff --git a/corelib/src/result.cairo b/corelib/src/result.cairo index 3f0c5a7..972134d 100644 --- a/corelib/src/result.cairo +++ b/corelib/src/result.cairo @@ -1,8 +1,39 @@ use array::ArrayTrait; +use serde::Serde; +use array::SpanTrait; + enum Result { Ok: T, Err: E, } + +impl ResultSerde< + R, E, impl RSerde: Serde, impl ESerde: Serde, impl RDrop: Drop, impl EDrop: Drop +> of Serde> { + fn serialize(self: @Result, ref output: Array) { + match self { + Result::Ok(x) => { + 0.serialize(ref output); + x.serialize(ref output) + }, + Result::Err(y) => { + 1.serialize(ref output); + y.serialize(ref output) + }, + } + } + fn deserialize(ref serialized: Span) -> Option> { + let variant = *serialized.pop_front()?; + if variant == 0 { + Option::Some(Result::Ok(Serde::::deserialize(ref serialized)?)) + } else if variant == 1 { + Option::Some(Result::Err(Serde::::deserialize(ref serialized)?)) + } else { + Option::None(()) + } + } +} + trait ResultTrait { /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics with `err`. fn expect>(self: Result, err: felt252) -> T; diff --git a/corelib/src/serde.cairo b/corelib/src/serde.cairo index f72a145..236f8d0 100644 --- a/corelib/src/serde.cairo +++ b/corelib/src/serde.cairo @@ -8,130 +8,6 @@ trait Serde { fn deserialize(ref serialized: Span) -> Option; } -impl Felt252Serde of Serde { - fn serialize(self: @felt252, ref output: Array) { - output.append(*self); - } - fn deserialize(ref serialized: Span) -> Option { - Option::Some(*serialized.pop_front()?) - } -} - -impl BoolSerde of Serde { - fn serialize(self: @bool, ref output: Array) { - if *self { - 1 - } else { - 0 - }.serialize(ref output); - } - fn deserialize(ref serialized: Span) -> Option { - Option::Some(*serialized.pop_front()? != 0) - } -} - -impl U8Serde of Serde { - fn serialize(self: @u8, ref output: Array) { - Into::::into(*self).serialize(ref output); - } - fn deserialize(ref serialized: Span) -> Option { - Option::Some(((*serialized.pop_front()?).try_into())?) - } -} - -impl U16Serde of Serde { - fn serialize(self: @u16, ref output: Array) { - Into::::into(*self).serialize(ref output); - } - fn deserialize(ref serialized: Span) -> Option { - Option::Some(((*serialized.pop_front()?).try_into())?) - } -} - -impl U32Serde of Serde { - fn serialize(self: @u32, ref output: Array) { - Into::::into(*self).serialize(ref output); - } - fn deserialize(ref serialized: Span) -> Option { - Option::Some(((*serialized.pop_front()?).try_into())?) - } -} - -impl U64Serde of Serde { - fn serialize(self: @u64, ref output: Array) { - Into::::into(*self).serialize(ref output); - } - fn deserialize(ref serialized: Span) -> Option { - Option::Some(((*serialized.pop_front()?).try_into())?) - } -} - -impl U128Serde of Serde { - fn serialize(self: @u128, ref output: Array) { - Into::::into(*self).serialize(ref output); - } - fn deserialize(ref serialized: Span) -> Option { - Option::Some(((*serialized.pop_front()?).try_into())?) - } -} - -impl OptionSerde, impl TDrop: Drop> of Serde> { - fn serialize(self: @Option, ref output: Array) { - match self { - Option::Some(x) => { - 0.serialize(ref output); - x.serialize(ref output) - }, - Option::None(()) => 1.serialize(ref output), - } - } - fn deserialize(ref serialized: Span) -> Option> { - let variant = *serialized.pop_front()?; - if variant == 0 { - Option::Some(Option::Some(Serde::::deserialize(ref serialized)?)) - } else if variant == 1 { - Option::Some(Option::None(())) - } else { - Option::None(()) - } - } -} - - -impl ArraySerde, impl TDrop: Drop> of Serde> { - fn serialize(self: @Array, ref output: Array) { - self.len().serialize(ref output); - serialize_array_helper(self.span(), ref output); - } - fn deserialize(ref serialized: Span) -> Option> { - let length = *serialized.pop_front()?; - let mut arr = ArrayTrait::new(); - deserialize_array_helper(ref serialized, arr, length) - } -} - -fn serialize_array_helper, impl TDrop: Drop>( - mut input: Span, ref output: Array -) { - match input.pop_front() { - Option::Some(value) => { - value.serialize(ref output); - serialize_array_helper(input, ref output); - }, - Option::None(_) => {}, - } -} - -fn deserialize_array_helper, impl TDrop: Drop>( - ref serialized: Span, mut curr_output: Array, remaining: felt252 -) -> Option> { - if remaining == 0 { - return Option::Some(curr_output); - } - curr_output.append(TSerde::deserialize(ref serialized)?); - deserialize_array_helper(ref serialized, curr_output, remaining - 1) -} - impl TupleSize0Serde of Serde<()> { fn serialize(self: @(), ref output: Array) {} fn deserialize(ref serialized: Span) -> Option<()> { diff --git a/corelib/src/starknet.cairo b/corelib/src/starknet.cairo index 3a339b5..21d3791 100644 --- a/corelib/src/starknet.cairo +++ b/corelib/src/starknet.cairo @@ -6,10 +6,10 @@ use traits::TryInto; use zeroable::Zeroable; // Re-imports -// StorageAccess +// Store mod storage_access; use storage_access::{ - StorageAccess, StorageAddress, StorageBaseAddress, storage_base_address_const, + Store, StorePacking, StorageAddress, StorageBaseAddress, storage_base_address_const, storage_base_address_from_felt252, storage_address_from_base, storage_address_from_base_and_offset, storage_address_to_felt252, storage_address_try_from_felt252 @@ -18,13 +18,15 @@ use storage_access::{ // Module containing all the extern declaration of the syscalls. mod syscalls; use syscalls::{ - call_contract_syscall, deploy_syscall, emit_event_syscall, get_execution_info_syscall, - library_call_syscall, send_message_to_l1_syscall, storage_read_syscall, storage_write_syscall, - replace_class_syscall, keccak_syscall + call_contract_syscall, deploy_syscall, emit_event_syscall, get_block_hash_syscall, + get_execution_info_syscall, library_call_syscall, send_message_to_l1_syscall, + storage_read_syscall, storage_write_syscall, replace_class_syscall, keccak_syscall }; -// secp256k1 +// secp256 +mod secp256_trait; mod secp256k1; +mod secp256r1; // ContractAddress mod contract_address; @@ -55,6 +57,9 @@ use info::{ mod event; use event::Event; +mod account; +use account::AccountContract; + extern type System; // An Helper function to force the inclusion of `System` in the list of implicits. diff --git a/corelib/src/starknet/account.cairo b/corelib/src/starknet/account.cairo new file mode 100644 index 0000000..67e965c --- /dev/null +++ b/corelib/src/starknet/account.cairo @@ -0,0 +1,14 @@ +use starknet::ContractAddress; + +#[derive(Drop, Serde)] +struct Call { + to: ContractAddress, + selector: felt252, + calldata: Array +} + +trait AccountContract { + fn __validate_declare__(self: @TContractState, class_hash: felt252) -> felt252; + fn __validate__(ref self: TContractState, calls: Array) -> felt252; + fn __execute__(ref self: TContractState, calls: Array) -> Array>; +} diff --git a/corelib/src/starknet/class_hash.cairo b/corelib/src/starknet/class_hash.cairo index d3628fa..5d52693 100644 --- a/corelib/src/starknet/class_hash.cairo +++ b/corelib/src/starknet/class_hash.cairo @@ -50,11 +50,11 @@ impl ClassHashSerde of serde::Serde { impl ClassHashPartialEq of PartialEq { #[inline(always)] - fn eq(lhs: ClassHash, rhs: ClassHash) -> bool { - class_hash_to_felt252(lhs) == class_hash_to_felt252(rhs) + fn eq(lhs: @ClassHash, rhs: @ClassHash) -> bool { + class_hash_to_felt252(*lhs) == class_hash_to_felt252(*rhs) } #[inline(always)] - fn ne(lhs: ClassHash, rhs: ClassHash) -> bool { + fn ne(lhs: @ClassHash, rhs: @ClassHash) -> bool { !(lhs == rhs) } } diff --git a/corelib/src/starknet/contract_address.cairo b/corelib/src/starknet/contract_address.cairo index f12ff7d..1e36eb1 100644 --- a/corelib/src/starknet/contract_address.cairo +++ b/corelib/src/starknet/contract_address.cairo @@ -52,11 +52,11 @@ impl ContractAddressSerde of serde::Serde { impl ContractAddressPartialEq of PartialEq { #[inline(always)] - fn eq(lhs: ContractAddress, rhs: ContractAddress) -> bool { - contract_address_to_felt252(lhs) == contract_address_to_felt252(rhs) + fn eq(lhs: @ContractAddress, rhs: @ContractAddress) -> bool { + contract_address_to_felt252(*lhs) == contract_address_to_felt252(*rhs) } #[inline(always)] - fn ne(lhs: ContractAddress, rhs: ContractAddress) -> bool { + fn ne(lhs: @ContractAddress, rhs: @ContractAddress) -> bool { !(lhs == rhs) } } diff --git a/corelib/src/starknet/eth_address.cairo b/corelib/src/starknet/eth_address.cairo index b80595c..53f2fe3 100644 --- a/corelib/src/starknet/eth_address.cairo +++ b/corelib/src/starknet/eth_address.cairo @@ -1,22 +1,23 @@ +use debug::PrintTrait; +use integer::{u128_safe_divmod, U128TryIntoNonZero, U256TryIntoFelt252}; +use option::{Option, OptionTrait}; use serde::Serde; use traits::{Into, TryInto}; use zeroable::Zeroable; -use option::{Option, OptionTrait}; // An Ethereum address (160 bits). -#[derive(Copy, Drop)] +#[derive(Copy, Drop, starknet::Store)] struct EthAddress { address: felt252, } impl Felt252TryIntoEthAddress of TryInto { fn try_into(self: felt252) -> Option { - // TODO(yuval): change to a constant once u256 literals are supported. - let ETH_ADDRESS_BOUND = u256 { high: 0x100000000_u128, low: 0_u128 }; // 2 ** 160 + let ETH_ADDRESS_BOUND = 0x10000000000000000000000000000000000000000_u256; // 2 ** 160 if self.into() < ETH_ADDRESS_BOUND { Option::Some(EthAddress { address: self }) } else { - Option::None(()) + Option::None } } } @@ -25,6 +26,16 @@ impl EthAddressIntoFelt252 of Into { self.address } } +impl U256IntoEthAddress of Into { + fn into(self: u256) -> EthAddress { + // The Ethereum address is the 20 least significant bytes (=160=128+32 bits) of the value. + let high_32_bits = self.high % 0x100000000_u128; + EthAddress { + address: high_32_bits.into() * 0x100000000000000000000000000000000_felt252 + + self.low.into() + } + } +} impl EthAddressSerde of Serde { fn serialize(self: @EthAddress, ref output: Array) { self.address.serialize(ref output); @@ -46,13 +57,19 @@ impl EthAddressZeroable of Zeroable { !self.is_zero() } } -impl ContractAddressPartialEq of PartialEq { +impl EthAddressPartialEq of PartialEq { #[inline(always)] - fn eq(lhs: EthAddress, rhs: EthAddress) -> bool { - lhs.address == rhs.address + fn eq(lhs: @EthAddress, rhs: @EthAddress) -> bool { + *lhs.address == *rhs.address } #[inline(always)] - fn ne(lhs: EthAddress, rhs: EthAddress) -> bool { + fn ne(lhs: @EthAddress, rhs: @EthAddress) -> bool { !(lhs == rhs) } } + +impl EthAddressPrintImpl of PrintTrait { + fn print(self: EthAddress) { + self.address.print(); + } +} diff --git a/corelib/src/starknet/event.cairo b/corelib/src/starknet/event.cairo index 4e6bdfd..9d3cbc0 100644 --- a/corelib/src/starknet/event.cairo +++ b/corelib/src/starknet/event.cairo @@ -1,6 +1,11 @@ use serde::Serde; +use traits::Into; trait Event { - fn append_keys_and_values(self: T, ref keys: Array, ref values: Array); - fn deserialize(ref keys: Span, ref values: Span) -> Option; + fn append_keys_and_data(self: @T, ref keys: Array, ref data: Array); + fn deserialize(ref keys: Span, ref data: Span) -> Option; +} + +trait EventEmitter { + fn emit>(ref self: T, event: S); } diff --git a/corelib/src/starknet/secp256_trait.cairo b/corelib/src/starknet/secp256_trait.cairo new file mode 100644 index 0000000..e56e2c1 --- /dev/null +++ b/corelib/src/starknet/secp256_trait.cairo @@ -0,0 +1,137 @@ +use array::ArrayTrait; +use keccak::keccak_u256s_be_inputs; +use math::u256_div_mod_n; +use option::OptionTrait; +use starknet::{eth_address::U256IntoEthAddress, EthAddress, SyscallResult, SyscallResultTrait}; +use traits::{Into, TryInto}; +use integer::U256TryIntoNonZero; + +/// Secp256{k/r}1 ECDSA signature. +#[derive(Copy, Drop, PartialEq, Serde, starknet::Store)] +struct Signature { + r: u256, + s: u256, + // The parity of the y coordinate of the ec point whose x coordinate is `r`. + // `y_parity` == true means that the y coordinate is odd. + // Some places use non boolean v instead of y_parity. + // In that case, `signature_from_vrs` should be used. + y_parity: bool, +} + + +/// Creates an ECDSA signature from the v, r and s values. +fn signature_from_vrs(v: u32, r: u256, s: u256) -> Signature { + Signature { r, s, y_parity: v % 2 == 1, } +} + +trait Secp256Trait { + fn get_curve_size() -> u256; + fn get_generator_point() -> Secp256Point; + + fn secp256_ec_new_syscall(x: u256, y: u256) -> SyscallResult>; + fn secp256_ec_get_point_from_x_syscall( + x: u256, y_parity: bool + ) -> SyscallResult>; +} + +trait Secp256PointTrait { + fn get_coordinates(self: Secp256Point) -> SyscallResult<(u256, u256)>; + fn add(self: Secp256Point, other: Secp256Point) -> SyscallResult; + fn mul(self: Secp256Point, scalar: u256) -> SyscallResult; +} + + +/// Receives a signature and the signed message hash. +/// Returns the public key associated with the signer, represented as a point on the curve. +fn recover_public_key< + Secp256Point, + impl Secp256PointDrop: Drop, + impl Secp256Impl: Secp256Trait, + impl Secp256PointImpl: Secp256PointTrait +>( + msg_hash: u256, signature: Signature +) -> Option { + let Signature{r, s, y_parity } = signature; + let r_point = Secp256Impl::secp256_ec_get_point_from_x_syscall(x: r, :y_parity) + .unwrap_syscall()?; + let generator_point = Secp256Impl::get_generator_point(); + + // The result is given by + // -(msg_hash / r) * gen + (s / r) * r_point + // where the divisions by `r` are modulo `N` (the size of the curve). + + let n_nz = Secp256Impl::get_curve_size().try_into().unwrap(); + let r_nz = r.try_into().unwrap(); + let u1 = u256_div_mod_n(msg_hash, r_nz, n_nz).unwrap(); + let minus_u1 = secp256_ec_negate_scalar::(u1); + let u2 = u256_div_mod_n(s, r_nz, n_nz).unwrap(); + + let minus_point1 = generator_point.mul(minus_u1).unwrap_syscall(); + + let point2 = r_point.mul(u2).unwrap_syscall(); + + Option::Some(minus_point1.add(point2).unwrap_syscall()) +} + +/// Computes the negation of a scalar modulo N (the size of the curve). +fn secp256_ec_negate_scalar< + Secp256Point, + impl Secp256PointDrop: Drop, + impl Secp256Impl: Secp256Trait +>( + c: u256 +) -> u256 { + Secp256Impl::get_curve_size() - c +} + + +/// Verifies a Secp256 ECDSA signature. +/// Also verifies that r and s components of the signature are in the range (0, N), +/// where N is the size of the curve. +fn verify_eth_signature< + Secp256Point, + impl Secp256PointDrop: Drop, + impl Secp256Impl: Secp256Trait, + impl Secp256PointImpl: Secp256PointTrait +>( + msg_hash: u256, signature: Signature, eth_address: EthAddress +) { + assert(is_signature_entry_valid::(signature.r), 'Signature out of range'); + assert(is_signature_entry_valid::(signature.s), 'Signature out of range'); + + let public_key_point = recover_public_key::(:msg_hash, :signature).unwrap(); + let calculated_eth_address = public_key_point_to_eth_address(:public_key_point); + assert(eth_address == calculated_eth_address, 'Invalid signature'); +} + +/// Checks whether `value` is in the range [1, N), where N is the size of the curve. +fn is_signature_entry_valid< + Secp256Point, + impl Secp256PointDrop: Drop, + impl Secp256Impl: Secp256Trait +>( + value: u256 +) -> bool { + value != 0_u256 && value < Secp256Impl::get_curve_size() +} + +/// Converts a public key point to the corresponding Ethereum address. +fn public_key_point_to_eth_address< + Secp256Point, + impl Secp256PointDrop: Drop, + impl Secp256Impl: Secp256Trait, + impl Secp256PointImpl: Secp256PointTrait +>( + public_key_point: Secp256Point +) -> EthAddress { + let (x, y) = public_key_point.get_coordinates().unwrap_syscall(); + + // Keccak output is little endian. + let point_hash_le = keccak_u256s_be_inputs(array![x, y].span()); + let point_hash = u256 { + low: integer::u128_byte_reverse(point_hash_le.high), + high: integer::u128_byte_reverse(point_hash_le.low) + }; + + point_hash.into() +} diff --git a/corelib/src/starknet/secp256k1.cairo b/corelib/src/starknet/secp256k1.cairo index 0aa62b5..7030844 100644 --- a/corelib/src/starknet/secp256k1.cairo +++ b/corelib/src/starknet/secp256k1.cairo @@ -1,40 +1,74 @@ //! This module contains functions and constructs related to elliptic curve operations on the //! secp256k1 curve. -use starknet::{SyscallResult, SyscallResultTrait}; use option::OptionTrait; +use starknet::{ + EthAddress, secp256_trait::{Secp256Trait, Secp256PointTrait}, SyscallResult, SyscallResultTrait +}; #[derive(Copy, Drop)] -extern type Secp256K1EcPoint; +extern type Secp256k1Point; + +impl Secp256k1Impl of Secp256Trait { + // TODO(yuval): change to constant once u256 constants are supported. + fn get_curve_size() -> u256 { + 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 + } + /// Creates the generator point of the secp256k1 curve. + fn get_generator_point() -> Secp256k1Point { + secp256k1_new_syscall( + 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, + 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 + ) + .unwrap_syscall() + .unwrap() + } + + fn secp256_ec_new_syscall(x: u256, y: u256) -> SyscallResult> { + secp256k1_new_syscall(x, y) + } + fn secp256_ec_get_point_from_x_syscall( + x: u256, y_parity: bool + ) -> SyscallResult> { + secp256k1_get_point_from_x_syscall(x, y_parity) + } +} + +impl Secp256k1PointImpl of Secp256PointTrait { + fn get_coordinates(self: Secp256k1Point) -> SyscallResult<(u256, u256)> { + secp256k1_get_xy_syscall(self) + } + fn add(self: Secp256k1Point, other: Secp256k1Point) -> SyscallResult { + secp256k1_add_syscall(self, other) + } + fn mul(self: Secp256k1Point, scalar: u256) -> SyscallResult { + secp256k1_mul_syscall(self, scalar) + } +} /// Creates a secp256k1 EC point from the given x and y coordinates. /// Returns None if the given coordinates do not correspond to a point on the curve. -extern fn secp256k1_ec_new_syscall( +extern fn secp256k1_new_syscall( x: u256, y: u256 -) -> SyscallResult> implicits(GasBuiltin, System) nopanic; +) -> SyscallResult> implicits(GasBuiltin, System) nopanic; /// Computes the addition of secp256k1 EC points `p0 + p1`. -extern fn secp256k1_ec_add_syscall( - p0: Secp256K1EcPoint, p1: Secp256K1EcPoint -) -> SyscallResult implicits(GasBuiltin, System) nopanic; - -/// Computes the product of a secp256k1 EC point `p` by the given scalar `m`. -extern fn secp256k1_ec_mul_syscall( - p: Secp256K1EcPoint, m: u256 -) -> SyscallResult implicits(GasBuiltin, System) nopanic; +extern fn secp256k1_add_syscall( + p0: Secp256k1Point, p1: Secp256k1Point +) -> SyscallResult implicits(GasBuiltin, System) nopanic; +/// Computes the product of a secp256k1 EC point `p` by the given scalar `scalar`. +extern fn secp256k1_mul_syscall( + p: Secp256k1Point, scalar: u256 +) -> SyscallResult implicits(GasBuiltin, System) nopanic; /// Computes the point on the secp256k1 curve that matches the given `x` coordinate, if such exists. /// Out of the two possible y's, chooses according to `y_parity`. -extern fn secp256k1_ec_get_point_from_x_syscall( +/// `y_parity` == true means that the y coordinate is odd. +extern fn secp256k1_get_point_from_x_syscall( x: u256, y_parity: bool -) -> SyscallResult> implicits(GasBuiltin, System) nopanic; - -/// Creates the generator point of the secp256k1 curve. -fn get_generator_point() -> Secp256K1EcPoint { - secp256k1_ec_new_syscall( - u256 { high: 0x79be667ef9dcbbac55a06295ce870b07, low: 0x029bfcdb2dce28d959f2815b16f81798 }, - u256 { high: 0x483ada7726a3c4655da4fbfc0e1108a8, low: 0xfd17b448a68554199c47d08ffb10d4b8 } - ) - .unwrap_syscall() - .unwrap() -} +) -> SyscallResult> implicits(GasBuiltin, System) nopanic; + +/// Returns the coordinates of a point on the secp256k1 curve. +extern fn secp256k1_get_xy_syscall( + p: Secp256k1Point +) -> SyscallResult<(u256, u256)> implicits(GasBuiltin, System) nopanic; diff --git a/corelib/src/starknet/secp256r1.cairo b/corelib/src/starknet/secp256r1.cairo new file mode 100644 index 0000000..65fdfcc --- /dev/null +++ b/corelib/src/starknet/secp256r1.cairo @@ -0,0 +1,74 @@ +//! This module contains functions and constructs related to elliptic curve operations on the +//! secp256r1 curve. + +use option::OptionTrait; +use starknet::{ + EthAddress, secp256_trait::{Secp256Trait, Secp256PointTrait}, SyscallResult, SyscallResultTrait +}; + +#[derive(Copy, Drop)] +extern type Secp256r1Point; + +impl Secp256r1Impl of Secp256Trait { + // TODO(yuval): change to constant once u256 constants are supported. + fn get_curve_size() -> u256 { + 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 + } + /// Creates the generator point of the secp256r1 curve. + fn get_generator_point() -> Secp256r1Point { + secp256r1_new_syscall( + 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, + 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5, + ) + .unwrap_syscall() + .unwrap() + } + + fn secp256_ec_new_syscall(x: u256, y: u256) -> SyscallResult> { + secp256r1_new_syscall(x, y) + } + fn secp256_ec_get_point_from_x_syscall( + x: u256, y_parity: bool + ) -> SyscallResult> { + secp256r1_get_point_from_x_syscall(x, y_parity) + } +} + +impl Secp256r1PointImpl of Secp256PointTrait { + fn get_coordinates(self: Secp256r1Point) -> SyscallResult<(u256, u256)> { + secp256r1_get_xy_syscall(self) + } + fn add(self: Secp256r1Point, other: Secp256r1Point) -> SyscallResult { + secp256r1_add_syscall(self, other) + } + fn mul(self: Secp256r1Point, scalar: u256) -> SyscallResult { + secp256r1_mul_syscall(self, scalar) + } +} + +/// Creates a secp256r1 EC point from the given x and y coordinates. +/// Returns None if the given coordinates do not correspond to a point on the curve. +extern fn secp256r1_new_syscall( + x: u256, y: u256 +) -> SyscallResult> implicits(GasBuiltin, System) nopanic; + +/// Computes the addition of secp256r1 EC points `p0 + p1`. +extern fn secp256r1_add_syscall( + p0: Secp256r1Point, p1: Secp256r1Point +) -> SyscallResult implicits(GasBuiltin, System) nopanic; +/// Computes the product of a secp256r1 EC point `p` by the given scalar `scalar`. +extern fn secp256r1_mul_syscall( + p: Secp256r1Point, scalar: u256 +) -> SyscallResult implicits(GasBuiltin, System) nopanic; + +/// Computes the point on the secp256r1 curve that matches the given `x` coordinate, if such exists. +/// Out of the two possible y's, chooses according to `y_parity`. +/// `y_parity` == true means that the y coordinate is odd. +extern fn secp256r1_get_point_from_x_syscall( + x: u256, y_parity: bool +) -> SyscallResult> implicits(GasBuiltin, System) nopanic; + +/// Returns the coordinates of a point on the secp256r1 curve. +extern fn secp256r1_get_xy_syscall( + p: Secp256r1Point +) -> SyscallResult<(u256, u256)> implicits(GasBuiltin, System) nopanic; diff --git a/corelib/src/starknet/storage_access.cairo b/corelib/src/starknet/storage_access.cairo index 70cc157..82f1545 100644 --- a/corelib/src/starknet/storage_access.cairo +++ b/corelib/src/starknet/storage_access.cairo @@ -52,12 +52,53 @@ impl StorageAddressSerde of serde::Serde { } } -trait StorageAccess { +trait Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult; fn write(address_domain: u32, base: StorageBaseAddress, value: T) -> SyscallResult<()>; + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult; + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: T + ) -> SyscallResult<()>; + fn size() -> u8; } -impl StorageAccessFelt252 of StorageAccess { +trait StorePacking { + fn pack(value: T) -> PackedT; + fn unpack(value: PackedT) -> T; +} + +impl StoreUsingPacking< + T, PackedT, impl TPacking: StorePacking, impl PackedTStore: Store +> of Store { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { + Result::Ok(TPacking::unpack(PackedTStore::read(address_domain, base)?)) + } + #[inline(always)] + fn write(address_domain: u32, base: StorageBaseAddress, value: T) -> SyscallResult<()> { + PackedTStore::write(address_domain, base, TPacking::pack(value)) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok(TPacking::unpack(PackedTStore::read_at_offset(address_domain, base, offset)?)) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: T + ) -> SyscallResult<()> { + PackedTStore::write_at_offset(address_domain, base, offset, TPacking::pack(value)) + } + #[inline(always)] + fn size() -> u8 { + PackedTStore::size() + } +} + +impl StoreFelt252 of Store { #[inline(always)] fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { storage_read_syscall(address_domain, storage_address_from_base(base)) @@ -66,153 +107,543 @@ impl StorageAccessFelt252 of StorageAccess { fn write(address_domain: u32, base: StorageBaseAddress, value: felt252) -> SyscallResult<()> { storage_write_syscall(address_domain, storage_address_from_base(base), value) } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + storage_read_syscall(address_domain, storage_address_from_base_and_offset(base, offset)) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: felt252 + ) -> SyscallResult<()> { + storage_write_syscall( + address_domain, storage_address_from_base_and_offset(base, offset), value + ) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 + } } -impl StorageAccessBool of StorageAccess { +impl StoreBool of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { - Result::Ok(StorageAccess::::read(address_domain, base)? != 0) + Result::Ok(Store::::read(address_domain, base)? != 0) } #[inline(always)] fn write(address_domain: u32, base: StorageBaseAddress, value: bool) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, if value { + Store::::write(address_domain, base, if value { 1 } else { 0 }) } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok(Store::::read_at_offset(address_domain, base, offset)? != 0) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: bool + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, if value { + 1 + } else { + 0 + }) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 + } } -impl StorageAccessU8 of StorageAccess { +impl StoreU8 of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)? - .try_into() - .expect('StorageAccessU8 - non u8') + Store::::read(address_domain, base)?.try_into().expect('StoreU8 - non u8') ) } #[inline(always)] fn write(address_domain: u32, base: StorageBaseAddress, value: u8) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + Store::::write(address_domain, base, value.into()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok( + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('StoreU8 - non u8') + ) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: u8 + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 } } -impl StorageAccessU16 of StorageAccess { +impl StoreU16 of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)? - .try_into() - .expect('StorageAccessU16 - non u16') + Store::::read(address_domain, base)?.try_into().expect('StoreU16 - non u16') ) } #[inline(always)] fn write(address_domain: u32, base: StorageBaseAddress, value: u16) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + Store::::write(address_domain, base, value.into()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok( + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('StoreU16 - non u16') + ) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: u16 + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 } } -impl StorageAccessU32 of StorageAccess { +impl StoreU32 of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)? - .try_into() - .expect('StorageAccessU32 - non u32') + Store::::read(address_domain, base)?.try_into().expect('StoreU32 - non u32') ) } #[inline(always)] fn write(address_domain: u32, base: StorageBaseAddress, value: u32) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + Store::::write(address_domain, base, value.into()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok( + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('StoreU32 - non u32') + ) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: u32 + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 } } -impl StorageAccessU64 of StorageAccess { +impl StoreU64 of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)? - .try_into() - .expect('StorageAccessU64 - non u64') + Store::::read(address_domain, base)?.try_into().expect('StoreU64 - non u64') ) } #[inline(always)] fn write(address_domain: u32, base: StorageBaseAddress, value: u64) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + Store::::write(address_domain, base, value.into()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok( + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('StoreU64 - non u64') + ) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: u64 + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 } } -impl StorageAccessU128 of StorageAccess { +impl StoreU128 of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)? - .try_into() - .expect('StorageAccessU128 - non u128') + Store::::read(address_domain, base)?.try_into().expect('StoreU128 - non u128') ) } #[inline(always)] fn write(address_domain: u32, base: StorageBaseAddress, value: u128) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + Store::::write(address_domain, base, value.into()) } -} - -impl StorageAccessU256 of StorageAccess { - fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { Result::Ok( - u256 { - low: StorageAccess::::read(address_domain, base)?, - high: storage_read_syscall( - address_domain, storage_address_from_base_and_offset(base, 1_u8) - )? - .try_into() - .expect('StorageAccessU256 - non u256') - } - ) - } - fn write(address_domain: u32, base: StorageBaseAddress, value: u256) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.low)?; - storage_write_syscall( - address_domain, storage_address_from_base_and_offset(base, 1_u8), value.high.into() + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('StoreU128 - non u128') ) } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: u128 + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 + } } -impl StorageAccessStorageAddress of StorageAccess { +impl StoreStorageAddress of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)? - .try_into() - .expect('Non StorageAddress') + Store::::read(address_domain, base)?.try_into().expect('Non StorageAddress') ) } #[inline(always)] fn write( address_domain: u32, base: StorageBaseAddress, value: StorageAddress ) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + Store::::write(address_domain, base, value.into()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok( + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('Non StorageAddress') + ) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: StorageAddress + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 } } -impl StorageAccessContractAddress of StorageAccess { +impl StoreContractAddress of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)? - .try_into() - .expect('Non ContractAddress') + Store::::read(address_domain, base)?.try_into().expect('Non ContractAddress') ) } #[inline(always)] fn write( address_domain: u32, base: StorageBaseAddress, value: ContractAddress ) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + Store::::write(address_domain, base, value.into()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { + Result::Ok( + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('Non ContractAddress') + ) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: ContractAddress + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 } } -impl StorageAccessClassHash of StorageAccess { +impl StoreClassHash of Store { fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { + Result::Ok(Store::::read(address_domain, base)?.try_into().expect('Non ClassHash')) + } + #[inline(always)] + fn write(address_domain: u32, base: StorageBaseAddress, value: ClassHash) -> SyscallResult<()> { + Store::::write(address_domain, base, value.into()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult { Result::Ok( - StorageAccess::::read(address_domain, base)?.try_into().expect('Non ClassHash') + Store::::read_at_offset(address_domain, base, offset)? + .try_into() + .expect('Non ClassHash') ) } #[inline(always)] - fn write(address_domain: u32, base: StorageBaseAddress, value: ClassHash) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: ClassHash + ) -> SyscallResult<()> { + Store::::write_at_offset(address_domain, base, offset, value.into()) + } + #[inline(always)] + fn size() -> u8 { + 1_u8 + } +} + +impl TupleSize0Store of Store<()> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult<()> { + Result::Ok(()) + } + #[inline(always)] + fn write(address_domain: u32, base: StorageBaseAddress, value: ()) -> SyscallResult<()> { + Result::Ok(()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult<()> { + Result::Ok(()) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: () + ) -> SyscallResult<()> { + Result::Ok(()) + } + #[inline(always)] + fn size() -> u8 { + 0 + } +} + +impl TupleSize1Store, impl E0Drop: Drop> of Store<(E0, )> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult<(E0, )> { + Result::Ok((E0Store::read(address_domain, base)?, )) + } + #[inline(always)] + fn write(address_domain: u32, base: StorageBaseAddress, value: (E0, )) -> SyscallResult<()> { + let (e0, ) = value; + E0Store::write(address_domain, base, e0) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult<(E0, )> { + Result::Ok((E0Store::read_at_offset(address_domain, base, offset)?, )) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: (E0, ) + ) -> SyscallResult<()> { + let (e0, ) = value; + E0Store::write_at_offset(address_domain, base, offset, e0) + } + #[inline(always)] + fn size() -> u8 { + E0Store::size() + } +} + +impl TupleSize2Store< + E0, + E1, + impl E0Store: Store, + impl E0Drop: Drop, + impl E1Store: Store, + impl E0Drop: Drop +> of Store<(E0, E1)> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult<(E0, E1)> { + let e0 = E0Store::read(address_domain, base)?; + let e1 = E1Store::read_at_offset(address_domain, base, E0Store::size())?; + Result::Ok((e0, e1)) + } + #[inline(always)] + fn write(address_domain: u32, base: StorageBaseAddress, value: (E0, E1)) -> SyscallResult<()> { + let (e0, e1) = value; + E0Store::write(address_domain, base, e0)?; + E1Store::write_at_offset(address_domain, base, E0Store::size(), e1) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8 + ) -> SyscallResult<(E0, E1)> { + let e0 = E0Store::read_at_offset(address_domain, base, offset)?; + offset += E0Store::size(); + let e1 = E1Store::read_at_offset(address_domain, base, offset)?; + Result::Ok((e0, e1)) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8, value: (E0, E1) + ) -> SyscallResult<()> { + let (e0, e1) = value; + E0Store::write_at_offset(address_domain, base, offset, e0)?; + offset += E0Store::size(); + E1Store::write_at_offset(address_domain, base, offset, e1) + } + #[inline(always)] + fn size() -> u8 { + E0Store::size() + E1Store::size() + } +} + +impl TupleSize3Store< + E0, + E1, + E2, + impl E0Store: Store, + impl E0Drop: Drop, + impl E1Store: Store, + impl E1Drop: Drop, + impl E2Store: Store, + impl E2Drop: Drop +> of Store<(E0, E1, E2)> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult<(E0, E1, E2)> { + let e0 = E0Store::read(address_domain, base)?; + let mut offset = E0Store::size(); + let e1 = E1Store::read_at_offset(address_domain, base, offset)?; + offset += E1Store::size(); + let e2 = E2Store::read_at_offset(address_domain, base, offset)?; + Result::Ok((e0, e1, e2)) + } + #[inline(always)] + fn write( + address_domain: u32, base: StorageBaseAddress, value: (E0, E1, E2) + ) -> SyscallResult<()> { + let (e0, e1, e2) = value; + E0Store::write(address_domain, base, e0)?; + let mut offset = E0Store::size(); + E1Store::write_at_offset(address_domain, base, offset, e1)?; + offset += E1Store::size(); + E2Store::write_at_offset(address_domain, base, offset, e2) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8 + ) -> SyscallResult<(E0, E1, E2)> { + let e0 = E0Store::read_at_offset(address_domain, base, offset)?; + offset += E0Store::size(); + let e1 = E1Store::read_at_offset(address_domain, base, offset)?; + offset += E1Store::size(); + let e2 = E2Store::read_at_offset(address_domain, base, offset)?; + Result::Ok((e0, e1, e2)) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8, value: (E0, E1, E2) + ) -> SyscallResult<()> { + let (e0, e1, e2) = value; + E0Store::write_at_offset(address_domain, base, offset, e0)?; + offset += E0Store::size(); + E1Store::write_at_offset(address_domain, base, offset, e1)?; + offset += E1Store::size(); + E2Store::write_at_offset(address_domain, base, offset, e2) + } + #[inline(always)] + fn size() -> u8 { + E0Store::size() + E1Store::size() + E2Store::size() + } +} + +impl TupleSize4Store< + E0, + E1, + E2, + E3, + impl E0Store: Store, + impl E0Drop: Drop, + impl E1Store: Store, + impl E1Drop: Drop, + impl E2Store: Store, + impl E2Drop: Drop, + impl E3Store: Store, + impl E3Drop: Drop +> of Store<(E0, E1, E2, E3)> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult<(E0, E1, E2, E3)> { + let e0 = E0Store::read(address_domain, base)?; + let mut offset = E0Store::size(); + let e1 = E1Store::read_at_offset(address_domain, base, offset)?; + offset += E1Store::size(); + let e2 = E2Store::read_at_offset(address_domain, base, offset)?; + offset += E2Store::size(); + let e3 = E3Store::read_at_offset(address_domain, base, offset)?; + Result::Ok((e0, e1, e2, e3)) + } + #[inline(always)] + fn write( + address_domain: u32, base: StorageBaseAddress, value: (E0, E1, E2, E3) + ) -> SyscallResult<()> { + let (e0, e1, e2, e3) = value; + E0Store::write(address_domain, base, e0)?; + let mut offset = E0Store::size(); + E1Store::write_at_offset(address_domain, base, offset, e1)?; + offset += E1Store::size(); + E2Store::write_at_offset(address_domain, base, offset, e2)?; + offset += E2Store::size(); + E3Store::write_at_offset(address_domain, base, offset, e3) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8 + ) -> SyscallResult<(E0, E1, E2, E3)> { + let e0 = E0Store::read_at_offset(address_domain, base, offset)?; + offset += E0Store::size(); + let e1 = E1Store::read_at_offset(address_domain, base, offset)?; + offset += E1Store::size(); + let e2 = E2Store::read_at_offset(address_domain, base, offset)?; + offset += E2Store::size(); + let e3 = E3Store::read_at_offset(address_domain, base, offset)?; + Result::Ok((e0, e1, e2, e3)) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, mut offset: u8, value: (E0, E1, E2, E3) + ) -> SyscallResult<()> { + let (e0, e1, e2, e3) = value; + E0Store::write_at_offset(address_domain, base, offset, e0)?; + offset += E0Store::size(); + E1Store::write_at_offset(address_domain, base, offset, e1)?; + offset += E1Store::size(); + E2Store::write_at_offset(address_domain, base, offset, e2)?; + offset += E2Store::size(); + E3Store::write_at_offset(address_domain, base, offset, e3) + } + #[inline(always)] + fn size() -> u8 { + E0Store::size() + E1Store::size() + E2Store::size() + E3Store::size() } } diff --git a/corelib/src/starknet/syscalls.cairo b/corelib/src/starknet/syscalls.cairo index 02a28bb..cc5904f 100644 --- a/corelib/src/starknet/syscalls.cairo +++ b/corelib/src/starknet/syscalls.cairo @@ -31,6 +31,11 @@ extern fn emit_event_syscall( keys: Span, data: Span ) -> SyscallResult<()> implicits(GasBuiltin, System) nopanic; +// Gets the block hash of the block with the given number. +extern fn get_block_hash_syscall( + block_number: u64 +) -> SyscallResult implicits(GasBuiltin, System) nopanic; + // Gets information about the current execution. extern fn get_execution_info_syscall() -> SyscallResult> implicits( GasBuiltin, System diff --git a/corelib/src/starknet/testing.cairo b/corelib/src/starknet/testing.cairo index b04dfc1..ef79fc7 100644 --- a/corelib/src/starknet/testing.cairo +++ b/corelib/src/starknet/testing.cairo @@ -1,14 +1,83 @@ use starknet::ContractAddress; +use array::ArrayTrait; +use array::SpanTrait; +use traits::Into; -extern fn set_caller_address(address: ContractAddress) implicits() nopanic; -extern fn set_contract_address(address: ContractAddress) implicits() nopanic; -extern fn set_sequencer_address(address: ContractAddress) implicits() nopanic; -extern fn set_block_number(block_number: u64) implicits() nopanic; -extern fn set_block_timestamp(block_timestamp: u64) implicits() nopanic; -extern fn set_version(version: felt252) implicits() nopanic; -extern fn set_account_contract_address(address: ContractAddress) implicits() nopanic; -extern fn set_max_fee(fee: u128) implicits() nopanic; -extern fn set_transaction_hash(hash: felt252) implicits() nopanic; -extern fn set_chain_id(chain_id: felt252) implicits() nopanic; -extern fn set_nonce(nonce: felt252) implicits() nopanic; -extern fn set_signature(signature: Span) implicits() nopanic; +// A general cheatcode function used to simplify implementation of Starknet testing functions. +// External users of the cairo crates can also implement their own cheatcodes +// by injecting custom `CairoHintProcessor`. +extern fn cheatcode( + input: Span +) -> Span implicits() nopanic; + +// Set the block number to the provided value. +fn set_block_number(block_number: u64) { + cheatcode::<'set_block_number'>(array![block_number.into()].span()); +} + +// Set the caller address to the provided value. +fn set_caller_address(address: ContractAddress) { + cheatcode::<'set_caller_address'>(array![address.into()].span()); +} + +// Set the contract address to the provided value. +fn set_contract_address(address: ContractAddress) { + cheatcode::<'set_contract_address'>(array![address.into()].span()); +} + +// Set the sequencer address to the provided value. +fn set_sequencer_address(address: ContractAddress) { + cheatcode::<'set_sequencer_address'>(array![address.into()].span()); +} + +// Set the block timestamp to the provided value. +fn set_block_timestamp(block_timestamp: u64) { + cheatcode::<'set_block_timestamp'>(array![block_timestamp.into()].span()); +} + +// Set the version to the provided value. +fn set_version(version: felt252) { + cheatcode::<'set_version'>(array![version].span()); +} + +// Set the account contract address. +fn set_account_contract_address(address: ContractAddress) { + cheatcode::<'set_account_contract_address'>(array![address.into()].span()); +} + +// Set the max fee. +fn set_max_fee(fee: u128) { + cheatcode::<'set_max_fee'>(array![fee.into()].span()); +} + +// Set the transaction hash. +fn set_transaction_hash(hash: felt252) { + cheatcode::<'set_transaction_hash'>(array![hash].span()); +} + +// Set the chain id. +fn set_chain_id(chain_id: felt252) { + cheatcode::<'set_chain_id'>(array![chain_id].span()); +} + +// Set the nonce. +fn set_nonce(nonce: felt252) { + cheatcode::<'set_nonce'>(array![nonce].span()); +} + +// Set the signature. +fn set_signature(signature: Span) { + cheatcode::<'set_signature'>(signature); +} + +// Pop the earliest unpopped logged event for the contract. +fn pop_log_raw(address: ContractAddress) -> Option<(Span, Span)> { + let mut log = cheatcode::<'pop_log'>(array![address.into()].span()); + Option::Some((serde::Serde::deserialize(ref log)?, serde::Serde::deserialize(ref log)?, )) +} + +// Pop the earliest unpopped logged event for the contract as the requested type. +fn pop_log>(address: ContractAddress) -> Option { + let (mut keys, mut data) = pop_log_raw(address)?; + starknet::Event::deserialize(ref keys, ref data) +} diff --git a/corelib/src/test.cairo b/corelib/src/test.cairo index d8cefde..6f41b0f 100644 --- a/corelib/src/test.cairo +++ b/corelib/src/test.cairo @@ -1,12 +1,17 @@ mod array_test; mod bool_test; mod box_test; +mod bytes31_test; +mod cmp_test; mod dict_test; mod ec_test; mod felt_test; -mod cmp_test; mod hash_test; mod integer_test; mod keccak_test; +mod math_test; mod plugins_test; +mod secp256k1_test; +mod secp256r1_test; +mod test_utils; mod testing_test; diff --git a/corelib/src/test/array_test.cairo b/corelib/src/test/array_test.cairo index 0873d75..f9e28b2 100644 --- a/corelib/src/test/array_test.cairo +++ b/corelib/src/test/array_test.cairo @@ -2,86 +2,78 @@ use array::{ArrayTrait, SpanTrait}; use box::BoxTrait; use clone::Clone; use option::OptionTrait; - -fn test_array_helper() -> Array { - let mut arr = ArrayTrait::new(); - arr.append(10); - arr.append(11); - arr.append(12); - arr -} +use test::test_utils::{assert_eq, assert_ne}; #[test] fn test_array() { - let arr = test_array_helper(); - assert(*arr[0] == 10, 'array[0] == 10'); - assert(*arr[1] == 11, 'array[1] == 11'); - assert(*arr[2] == 12, 'array[2] == 12'); + let arr = array![10, 11, 12]; + assert_eq(arr[0], @10, 'array[0] != 10'); + assert_eq(arr[1], @11, 'array[1] != 11'); + assert_eq(arr[2], @12, 'array[2] != 12'); } #[test] #[should_panic] fn test_array_out_of_bound_1() { - let arr = test_array_helper(); + let arr = array![10, 11, 12]; arr[3]; } #[test] #[should_panic] fn test_array_out_of_bound_2() { - let arr = test_array_helper(); + let arr = array![10, 11, 12]; arr[11]; } #[test] #[available_gas(100000)] fn test_array_clone() { - let felt252_snap_array = @test_array_helper(); + let felt252_snap_array: @Array = @array![10, 11, 12]; let felt252_snap_array_clone = felt252_snap_array.clone(); - assert(felt252_snap_array_clone.len() == 3, 'array len == 3'); - assert(*felt252_snap_array_clone[0] == 10, 'array[0] == 10'); - assert(*felt252_snap_array_clone[1] == 11, 'array[1] == 11'); - assert(*felt252_snap_array_clone[2] == 12, 'array[2] == 12'); + assert_eq(@felt252_snap_array_clone.len(), @3, 'array len != 3'); + assert_eq(felt252_snap_array_clone[0], @10, 'array[0] != 10'); + assert_eq(felt252_snap_array_clone[1], @11, 'array[1] != 11'); + assert_eq(felt252_snap_array_clone[2], @12, 'array[2] != 12'); } #[test] fn test_span() { - let mut span = test_array_helper().span(); - - assert(span.len() == 3, 'Unexpected span length.'); - assert(*span.get(0).unwrap().unbox() == 10, 'Unexpected element'); - assert(*span.pop_front().unwrap() == 10, 'Unexpected element'); - assert(span.len() == 2, 'Unexpected span length.'); - assert(*span[1] == 12, 'Unexpected element'); - assert(*span.pop_back().unwrap() == 12, 'Unexpected element'); - assert(span.len() == 1, 'Unexpected span length.'); + let mut span = array![10, 11, 12].span(); + assert_eq(@span.len(), @3, 'Unexpected span length.'); + assert_eq(span.get(0).unwrap().unbox(), @10, 'Unexpected element'); + assert_eq(span.pop_front().unwrap(), @10, 'Unexpected element'); + assert_eq(@span.len(), @2, 'Unexpected span length.'); + assert_eq(span[1], @12, 'Unexpected element'); + assert_eq(span.pop_back().unwrap(), @12, 'Unexpected element'); + assert_eq(@span.len(), @1, 'Unexpected span length.'); } #[test] fn test_slice() { - let mut span = test_array_helper().span(); - assert(span.slice(0, 3).len() == 3, 'Unexpected span length.'); - assert(*span.slice(0, 3)[0] == 10, 'Unexpected Element.'); - assert(span.slice(0, 2).len() == 2, 'Unexpected span length.'); - assert(*span.slice(0, 2)[0] == 10, 'Unexpected Element.'); - assert(span.slice(0, 1).len() == 1, 'Unexpected span length.'); - assert(*span.slice(0, 1)[0] == 10, 'Unexpected Element.'); - assert(span.slice(0, 0).len() == 0, 'Unexpected span length.'); - assert(span.slice(1, 2).len() == 2, 'Unexpected span length.'); - assert(*span.slice(1, 2)[0] == 11, 'Unexpected Element.'); - assert(span.slice(1, 1).len() == 1, 'Unexpected span length.'); - assert(*span.slice(1, 1)[0] == 11, 'Unexpected Element.'); - assert(span.slice(1, 0).len() == 0, 'Unexpected span length.'); + let span = array![10, 11, 12].span(); + assert_eq(@span.slice(0, 3).len(), @3, 'Unexpected span length.'); + assert_eq(span.slice(0, 3)[0], @10, 'Unexpected Element.'); + assert_eq(@span.slice(0, 2).len(), @2, 'Unexpected span length.'); + assert_eq(span.slice(0, 2)[0], @10, 'Unexpected Element.'); + assert_eq(@span.slice(0, 1).len(), @1, 'Unexpected span length.'); + assert_eq(span.slice(0, 1)[0], @10, 'Unexpected Element.'); + assert_eq(@span.slice(0, 0).len(), @0, 'Unexpected span length.'); + assert_eq(@span.slice(1, 2).len(), @2, 'Unexpected span length.'); + assert_eq(span.slice(1, 2)[0], @11, 'Unexpected Element.'); + assert_eq(@span.slice(1, 1).len(), @1, 'Unexpected span length.'); + assert_eq(span.slice(1, 1)[0], @11, 'Unexpected Element.'); + assert_eq(@span.slice(1, 0).len(), @0, 'Unexpected span length.'); } #[test] #[should_panic] fn test_slice_out_of_bound_1() { - test_array_helper().span().slice(3, 1); + array![10, 11, 12].span().slice(3, 1); } #[test] #[should_panic] fn test_slice_out_of_bound_2() { - test_array_helper().span().slice(0, 4); + array![10, 11, 12].span().slice(0, 4); } diff --git a/corelib/src/test/bool_test.cairo b/corelib/src/test/bool_test.cairo index 0dcb3d1..1ead7dd 100644 --- a/corelib/src/test/bool_test.cairo +++ b/corelib/src/test/bool_test.cairo @@ -1,11 +1,14 @@ +use test::test_utils::{assert_eq, assert_ne}; +use traits::Into; + #[test] fn test_bool_operators() { - assert(true == true, 't == t'); - assert(false == false, 'f == f'); - assert(!true == false, '!t == f'); - assert(!false == true, '!f == t'); - assert(true != false, 't != f'); - assert(false != true, 'f != t'); + assert_eq(@true, @true, 't != t'); + assert_eq(@false, @false, 'f != f'); + assert_eq(@!true, @false, '!t != f'); + assert_eq(@!false, @true, '!f != t'); + assert_ne(@true, @false, 't == f'); + assert_ne(@false, @true, 'f == t'); assert(!(false & false), '!(f & f)'); assert(!(true & false), '!(t & f)'); assert(!(false & true), '!(f & t)'); @@ -19,3 +22,9 @@ fn test_bool_operators() { assert(false ^ true, 'f ^ t'); assert(!(true ^ true), '!(t ^ t)'); } + +#[test] +fn test_bool_conversion() { + assert_eq(@false.into(), @0, 'f.into() != 0'); + assert_eq(@true.into(), @1, 'f.into() != 1'); +} diff --git a/corelib/src/test/box_test.cairo b/corelib/src/test/box_test.cairo index 266b9b9..9f825e0 100644 --- a/corelib/src/test/box_test.cairo +++ b/corelib/src/test/box_test.cairo @@ -1,4 +1,5 @@ use box::BoxTrait; +use test::test_utils::{assert_eq, assert_ne}; #[test] fn test_box_unbox_felt252s() { @@ -6,8 +7,8 @@ fn test_box_unbox_felt252s() { let boxed_x = BoxTrait::new(x); let y = 11; let boxed_y = BoxTrait::new(y); - assert(boxed_x.unbox() == 10, 'x == 10'); - assert(boxed_y.unbox() == 11, 'y == 11'); + assert_eq(@boxed_x.unbox(), @10, 'x != 10'); + assert_eq(@boxed_y.unbox(), @11, 'y != 11'); } // Test objects of size>1. @@ -17,6 +18,6 @@ fn test_box_unbox_u256() { let boxed_x = BoxTrait::new(x); let y = u256 { low: 1, high: 1 }; let boxed_y = BoxTrait::new(y); - assert(boxed_x.unbox() == x, 'unbox u256 x'); - assert(boxed_y.unbox() == y, 'unbox u256 y'); + assert_eq(@boxed_x.unbox(), @x, 'unbox u256 x'); + assert_eq(@boxed_y.unbox(), @y, 'unbox u256 y'); } diff --git a/corelib/src/test/bytes31_test.cairo b/corelib/src/test/bytes31_test.cairo new file mode 100644 index 0000000..44a9207 --- /dev/null +++ b/corelib/src/test/bytes31_test.cairo @@ -0,0 +1,87 @@ +use test::test_utils::assert_eq; +use option::OptionTrait; +use traits::{Into, TryInto}; +use bytes_31::{U128IntoBytes31, U8IntoBytes31}; + +const POW_2_248: felt252 = 0x100000000000000000000000000000000000000000000000000000000000000; + +#[test] +fn test_bytes31_to_from_felt252() { + let zero_as_bytes31: Option = 0.try_into(); + assert(zero_as_bytes31.is_some(), '0 is not a bytes31'); + let zero_as_felt252 = zero_as_bytes31.unwrap().into(); + assert(zero_as_felt252 == 0_felt252, 'bad cast: 0'); + + let one_as_bytes31: Option = 1.try_into(); + assert(one_as_bytes31.is_some(), '1 is not a bytes31'); + let one_as_felt252 = one_as_bytes31.unwrap().into(); + assert(one_as_felt252 == 1_felt252, 'bad cast: 1'); + + let max_as_bytes31: Option = (POW_2_248 - 1).try_into(); + assert(max_as_bytes31.is_some(), '2^248 - 1 is not a bytes31'); + let max_as_felt252 = max_as_bytes31.unwrap().into(); + assert(max_as_felt252 == POW_2_248 - 1, 'bad cast: 2^248 - 1'); + + let out_of_range: Option = POW_2_248.try_into(); + assert(out_of_range.is_none(), '2^248 is a bytes31'); + + let out_of_range: Option = (-1).try_into(); + assert(out_of_range.is_none(), '-1 is a bytes31'); +} + +#[test] +fn test_u8_into_bytes31() { + let one_u8 = 1_u8; + let one_as_bytes31: bytes31 = one_u8.into(); + assert(one_as_bytes31.into() == 1_felt252, 'bad cast: 1'); + + let max_u8 = 0xff_u8; + let max_as_bytes31: bytes31 = max_u8.into(); + assert(max_as_bytes31.into() == 0xff_felt252, 'bad cast: 2^8 - 1'); +} + +#[test] +fn test_u16_into_bytes31() { + let one_u16 = 1_u16; + let one_as_bytes31: bytes31 = one_u16.into(); + assert(one_as_bytes31.into() == 1_felt252, 'bad cast: 1'); + + let max_u16 = 0xffff_u16; + let max_as_bytes31: bytes31 = max_u16.into(); + assert(max_as_bytes31.into() == 0xffff_felt252, 'bad cast: 2^16 - 1'); +} + +#[test] +fn test_u32_into_bytes31() { + let one_u32 = 1_u32; + let one_as_bytes31: bytes31 = one_u32.into(); + assert(one_as_bytes31.into() == 1_felt252, 'bad cast: 1'); + + let max_u32 = 0xffffffff_u32; + let max_as_bytes31: bytes31 = max_u32.into(); + assert(max_as_bytes31.into() == 0xffffffff_felt252, 'bad cast: 2^32 - 1'); +} + +#[test] +fn test_u64_into_bytes31() { + let one_u64 = 1_u64; + let one_as_bytes31: bytes31 = one_u64.into(); + assert(one_as_bytes31.into() == 1_felt252, 'bad cast: 1'); + + let max_u64 = 0xffffffffffffffff_u64; + let max_as_bytes31: bytes31 = max_u64.into(); + assert(max_as_bytes31.into() == 0xffffffffffffffff_felt252, 'bad cast: 2^64 - 1'); +} + +#[test] +fn test_u128_into_bytes31() { + let one_u128 = 1_u128; + let one_as_bytes31: bytes31 = one_u128.into(); + assert(one_as_bytes31.into() == 1_felt252, 'bad cast: 1'); + + let max_u128 = 0xffffffffffffffffffffffffffffffff_u128; + let max_as_bytes31: bytes31 = max_u128.into(); + assert( + max_as_bytes31.into() == 0xffffffffffffffffffffffffffffffff_felt252, 'bad cast: 2^128 - 1' + ); +} diff --git a/corelib/src/test/cmp_test.cairo b/corelib/src/test/cmp_test.cairo index bcfed59..afccd45 100644 --- a/corelib/src/test/cmp_test.cairo +++ b/corelib/src/test/cmp_test.cairo @@ -1,191 +1,204 @@ use cmp::min; use cmp::max; +use test::test_utils::{assert_eq, assert_ne}; // Integer tests #[test] fn test_min_u8() { - assert(min(0_u8, 1_u8) == 0_u8, '0 < 1'); - assert(min(0_u8, 1_u8) == 0_u8, '0 < 1'); - assert(min(5_u8, 7_u8) == 5_u8, '5 < 7'); - assert(min(255_u8, 128_u8) == 128_u8, '128 < 255'); - assert(min(10_u8, 10_u8) == 10_u8, '10 == 10'); - assert(min(0_u8, 0_u8) == 0_u8, '0 == 0'); - assert(min(255_u8, 255_u8) == 255_u8, '255 == 255'); - assert(min(3_u8, 4_u8) == 3_u8, '3 < 4'); - assert(min(255_u8, 200_u8) == 200_u8, '200 < 255'); - assert(min(250_u8, 253_u8) == 250_u8, '250 < 253'); + assert_eq(@min(0_u8, 1_u8), @0_u8, '0 < 1'); + assert_eq(@min(0_u8, 1_u8), @0_u8, '0 < 1'); + assert_eq(@min(5_u8, 7_u8), @5_u8, '5 < 7'); + assert_eq(@min(255_u8, 128_u8), @128_u8, '128 < 255'); + assert_eq(@min(10_u8, 10_u8), @10_u8, '10 == 10'); + assert_eq(@min(0_u8, 0_u8), @0_u8, '0 == 0'); + assert_eq(@min(255_u8, 255_u8), @255_u8, '255 == 255'); + assert_eq(@min(3_u8, 4_u8), @3_u8, '3 < 4'); + assert_eq(@min(255_u8, 200_u8), @200_u8, '200 < 255'); + assert_eq(@min(250_u8, 253_u8), @250_u8, '250 < 253'); } #[test] fn test_max_u8() { - assert(max(0_u8, 1_u8) == 1_u8, '1 > 0'); - assert(max(5_u8, 7_u8) == 7_u8, '7 > 5'); - assert(max(255_u8, 128_u8) == 255_u8, '255 > 128'); - assert(max(10_u8, 10_u8) == 10_u8, '10 == 10'); - assert(max(0_u8, 0_u8) == 0_u8, '0 == 0'); - assert(max(255_u8, 255_u8) == 255_u8, '255 == 255'); - assert(max(100_u8, 200_u8) == 200_u8, '200 > 100'); - assert(max(1_u8, 2_u8) == 2_u8, '2 > 1'); - assert(max(120_u8, 130_u8) == 130_u8, '130 > 120'); - assert(max(200_u8, 150_u8) == 200_u8, '200 > 150'); + assert_eq(@max(0_u8, 1_u8), @1_u8, '1 > 0'); + assert_eq(@max(5_u8, 7_u8), @7_u8, '7 > 5'); + assert_eq(@max(255_u8, 128_u8), @255_u8, '255 > 128'); + assert_eq(@max(10_u8, 10_u8), @10_u8, '10 == 10'); + assert_eq(@max(0_u8, 0_u8), @0_u8, '0 == 0'); + assert_eq(@max(255_u8, 255_u8), @255_u8, '255 == 255'); + assert_eq(@max(100_u8, 200_u8), @200_u8, '200 > 100'); + assert_eq(@max(1_u8, 2_u8), @2_u8, '2 > 1'); + assert_eq(@max(120_u8, 130_u8), @130_u8, '130 > 120'); + assert_eq(@max(200_u8, 150_u8), @200_u8, '200 > 150'); } #[test] fn test_min_u16() { - assert(min(0_u16, 1_u16) == 0_u16, '0 < 1'); - assert(min(5_u16, 7_u16) == 5_u16, '5 < 7'); - assert(min(65535_u16, 32768_u16) == 32768_u16, '32768 < 65535'); - assert(min(10_u16, 10_u16) == 10_u16, '10 == 10'); - assert(min(0_u16, 0_u16) == 0_u16, '0 == 0'); - assert(min(65535_u16, 65535_u16) == 65535_u16, '65535 == 65535'); - assert(min(100_u16, 200_u16) == 100_u16, '100 < 200'); - assert(min(1_u16, 2_u16) == 1_u16, '1 < 2'); - assert(min(32767_u16, 32766_u16) == 32766_u16, '32766 < 32767'); - assert(min(400_u16, 300_u16) == 300_u16, '300 < 400'); + assert_eq(@min(0_u16, 1_u16), @0_u16, '0 < 1'); + assert_eq(@min(5_u16, 7_u16), @5_u16, '5 < 7'); + assert_eq(@min(65535_u16, 32768_u16), @32768_u16, '32768 < 65535'); + assert_eq(@min(10_u16, 10_u16), @10_u16, '10 == 10'); + assert_eq(@min(0_u16, 0_u16), @0_u16, '0 == 0'); + assert_eq(@min(65535_u16, 65535_u16), @65535_u16, '65535 == 65535'); + assert_eq(@min(100_u16, 200_u16), @100_u16, '100 < 200'); + assert_eq(@min(1_u16, 2_u16), @1_u16, '1 < 2'); + assert_eq(@min(32767_u16, 32766_u16), @32766_u16, '32766 < 32767'); + assert_eq(@min(400_u16, 300_u16), @300_u16, '300 < 400'); } #[test] fn test_max_u16() { - assert(max(0_u16, 1_u16) == 1_u16, '1 > 0'); - assert(max(5_u16, 7_u16) == 7_u16, '7 > 5'); - assert(max(65535_u16, 32768_u16) == 65535_u16, '65535 > 32768'); - assert(max(10_u16, 10_u16) == 10_u16, '10 == 10'); - assert(max(0_u16, 0_u16) == 0_u16, '0 == 0'); - assert(max(65535_u16, 65535_u16) == 65535_u16, '65535 == 65535'); - assert(max(100_u16, 200_u16) == 200_u16, '200 > 100'); - assert(max(1_u16, 2_u16) == 2_u16, '2 > 1'); - assert(max(32767_u16, 32766_u16) == 32767_u16, '32767 > 32766'); - assert(max(400_u16, 300_u16) == 400_u16, '400 > 300'); + assert_eq(@max(0_u16, 1_u16), @1_u16, '1 > 0'); + assert_eq(@max(5_u16, 7_u16), @7_u16, '7 > 5'); + assert_eq(@max(65535_u16, 32768_u16), @65535_u16, '65535 > 32768'); + assert_eq(@max(10_u16, 10_u16), @10_u16, '10 == 10'); + assert_eq(@max(0_u16, 0_u16), @0_u16, '0 == 0'); + assert_eq(@max(65535_u16, 65535_u16), @65535_u16, '65535 == 65535'); + assert_eq(@max(100_u16, 200_u16), @200_u16, '200 > 100'); + assert_eq(@max(1_u16, 2_u16), @2_u16, '2 > 1'); + assert_eq(@max(32767_u16, 32766_u16), @32767_u16, '32767 > 32766'); + assert_eq(@max(400_u16, 300_u16), @400_u16, '400 > 300'); } #[test] fn test_min_u32() { - assert(min(0_u32, 1_u32) == 0_u32, '0 < 1'); - assert(min(5_u32, 7_u32) == 5_u32, '5 < 7'); - assert(min(4294967295_u32, 2147483648_u32) == 2147483648_u32, '2147483648 < 4294967295'); - assert(min(10_u32, 10_u32) == 10_u32, '10 == 10'); - assert(min(0_u32, 0_u32) == 0_u32, '0 == 0'); - assert(min(4294967295_u32, 4294967295_u32) == 4294967295_u32, '4294967295 == 4294967295'); - assert(min(100_u32, 200_u32) == 100_u32, '100 < 200'); - assert(min(1_u32, 2_u32) == 1_u32, '1 < 2'); - assert(min(2147483647_u32, 2147483646_u32) == 2147483646_u32, '2147483646 < 2147483647'); - assert(min(400_u32, 300_u32) == 300_u32, '300 < 400'); + assert_eq(@min(0_u32, 1_u32), @0_u32, '0 < 1'); + assert_eq(@min(5_u32, 7_u32), @5_u32, '5 < 7'); + assert_eq(@min(4294967295_u32, 2147483648_u32), @2147483648_u32, '2147483648 < 4294967295'); + assert_eq(@min(10_u32, 10_u32), @10_u32, '10 == 10'); + assert_eq(@min(0_u32, 0_u32), @0_u32, '0 == 0'); + assert_eq(@min(4294967295_u32, 4294967295_u32), @4294967295_u32, '4294967295 == 4294967295'); + assert_eq(@min(100_u32, 200_u32), @100_u32, '100 < 200'); + assert_eq(@min(1_u32, 2_u32), @1_u32, '1 < 2'); + assert_eq(@min(2147483647_u32, 2147483646_u32), @2147483646_u32, '2147483646 < 2147483647'); + assert_eq(@min(400_u32, 300_u32), @300_u32, '300 < 400'); } #[test] fn test_max_u32() { - assert(max(0_u32, 1_u32) == 1_u32, '1 > 0'); - assert(max(5_u32, 7_u32) == 7_u32, '7 > 5'); - assert(max(4294967295_u32, 2147483648_u32) == 4294967295_u32, '4294967295 > 2147483648'); - assert(max(10_u32, 10_u32) == 10_u32, '10 == 10'); - assert(max(0_u32, 0_u32) == 0_u32, '0 == 0'); - assert(max(4294967295_u32, 4294967295_u32) == 4294967295_u32, '4294967295 == 4294967295'); - assert(max(100_u32, 200_u32) == 200_u32, '200 > 100'); - assert(max(1_u32, 2_u32) == 2_u32, '2 > 1'); - assert(max(2147483647_u32, 2147483646_u32) == 2147483647_u32, '2147483647 > 2147483646'); - assert(max(400_u32, 300_u32) == 400_u32, '400 > 300'); + assert_eq(@max(0_u32, 1_u32), @1_u32, '1 > 0'); + assert_eq(@max(5_u32, 7_u32), @7_u32, '7 > 5'); + assert_eq(@max(4294967295_u32, 2147483648_u32), @4294967295_u32, '4294967295 > 2147483648'); + assert_eq(@max(10_u32, 10_u32), @10_u32, '10 == 10'); + assert_eq(@max(0_u32, 0_u32), @0_u32, '0 == 0'); + assert_eq(@max(4294967295_u32, 4294967295_u32), @4294967295_u32, '4294967295 == 4294967295'); + assert_eq(@max(100_u32, 200_u32), @200_u32, '200 > 100'); + assert_eq(@max(1_u32, 2_u32), @2_u32, '2 > 1'); + assert_eq(@max(2147483647_u32, 2147483646_u32), @2147483647_u32, '2147483647 > 2147483646'); + assert_eq(@max(400_u32, 300_u32), @400_u32, '400 > 300'); } #[test] fn test_min_u64() { - assert(min(0_u64, 1_u64) == 0_u64, '0 < 1'); - assert(min(5_u64, 7_u64) == 5_u64, '5 < 7'); - assert( - min(18446744073709551615_u64, 9223372036854775808_u64) == 9223372036854775808_u64, + assert_eq(@min(0_u64, 1_u64), @0_u64, '0 < 1'); + assert_eq(@min(5_u64, 7_u64), @5_u64, '5 < 7'); + assert_eq( + @min(18446744073709551615_u64, 9223372036854775808_u64), + @9223372036854775808_u64, '92233... < 18446...' ); - assert(min(10_u64, 10_u64) == 10_u64, '10 == 10'); - assert(min(0_u64, 0_u64) == 0_u64, '0 == 0'); - assert( - min(18446744073709551615_u64, 18446744073709551615_u64) == 18446744073709551615_u64, + assert_eq(@min(10_u64, 10_u64), @10_u64, '10 == 10'); + assert_eq(@min(0_u64, 0_u64), @0_u64, '0 == 0'); + assert_eq( + @min(18446744073709551615_u64, 18446744073709551615_u64), + @18446744073709551615_u64, '18446... == 18446...' ); - assert(min(100_u64, 200_u64) == 100_u64, '100 < 200'); - assert(min(1_u64, 2_u64) == 1_u64, '1 < 2'); - assert( - min(9223372036854775807_u64, 9223372036854775806_u64) == 9223372036854775806_u64, + assert_eq(@min(100_u64, 200_u64), @100_u64, '100 < 200'); + assert_eq(@min(1_u64, 2_u64), @1_u64, '1 < 2'); + assert_eq( + @min(9223372036854775807_u64, 9223372036854775806_u64), + @9223372036854775806_u64, '92233... < 92233...' ); - assert(min(400_u64, 300_u64) == 300_u64, '300 < 400'); + assert_eq(@min(400_u64, 300_u64), @300_u64, '300 < 400'); } #[test] fn test_max_u64() { - assert(max(0_u64, 1_u64) == 1_u64, '1 > 0'); - assert(max(5_u64, 7_u64) == 7_u64, '7 > 5'); - assert( - max(18446744073709551615_u64, 9223372036854775808_u64) == 18446744073709551615_u64, + assert_eq(@max(0_u64, 1_u64), @1_u64, '1 > 0'); + assert_eq(@max(5_u64, 7_u64), @7_u64, '7 > 5'); + assert_eq( + @max(18446744073709551615_u64, 9223372036854775808_u64), + @18446744073709551615_u64, '18446... > 92233...' ); - assert(max(10_u64, 10_u64) == 10_u64, '10 == 10'); - assert(max(0_u64, 0_u64) == 0_u64, '0 == 0'); - assert( - max(18446744073709551615_u64, 18446744073709551615_u64) == 18446744073709551615_u64, + assert_eq(@max(10_u64, 10_u64), @10_u64, '10 == 10'); + assert_eq(@max(0_u64, 0_u64), @0_u64, '0 == 0'); + assert_eq( + @max(18446744073709551615_u64, 18446744073709551615_u64), + @18446744073709551615_u64, '18446... == 18446...' ); - assert(max(100_u64, 200_u64) == 200_u64, '200 > 100'); - assert(max(1_u64, 2_u64) == 2_u64, '2 > 1'); - assert( - max(9223372036854775807_u64, 9223372036854775806_u64) == 9223372036854775807_u64, + assert_eq(@max(100_u64, 200_u64), @200_u64, '200 > 100'); + assert_eq(@max(1_u64, 2_u64), @2_u64, '2 > 1'); + assert_eq( + @max(9223372036854775807_u64, 9223372036854775806_u64), + @9223372036854775807_u64, '92233... > 92233...' ); - assert(max(400_u64, 300_u64) == 400_u64, '400 > 300'); + assert_eq(@max(400_u64, 300_u64), @400_u64, '400 > 300'); } #[test] fn test_min_u128() { - assert(min(0_u128, 1_u128) == 0_u128, '0 < 1'); - assert(min(5_u128, 7_u128) == 5_u128, '5 < 7'); - assert( - min( + assert_eq(@min(0_u128, 1_u128), @0_u128, '0 < 1'); + assert_eq(@min(5_u128, 7_u128), @5_u128, '5 < 7'); + assert_eq( + @min( 340282366920938463463374607431768211455_u128, 170141183460469231731687303715884105728_u128 - ) == 170141183460469231731687303715884105728_u128, + ), + @170141183460469231731687303715884105728_u128, '170141... < 340282...' ); - assert(min(10_u128, 10_u128) == 10_u128, '10 == 10'); - assert(min(0_u128, 0_u128) == 0_u128, '0 == 0'); - assert( - min( + assert_eq(@min(10_u128, 10_u128), @10_u128, '10 == 10'); + assert_eq(@min(0_u128, 0_u128), @0_u128, '0 == 0'); + assert_eq( + @min( 340282366920938463463374607431768211455_u128, 340282366920938463463374607431768211455_u128 - ) == 340282366920938463463374607431768211455_u128, + ), + @340282366920938463463374607431768211455_u128, '340282366... == 340282366...' ); - assert(min(100_u128, 200_u128) == 100_u128, '100 < 200'); - assert(min(1_u128, 2_u128) == 1_u128, '1 < 2'); - assert( - min( + assert_eq(@min(100_u128, 200_u128), @100_u128, '100 < 200'); + assert_eq(@min(1_u128, 2_u128), @1_u128, '1 < 2'); + assert_eq( + @min( 170141183460469231731687303715884105727_u128, 170141183460469231731687303715884105726_u128 - ) == 170141183460469231731687303715884105726_u128, + ), + @170141183460469231731687303715884105726_u128, '17014118... < 1701411834...' ); - assert(min(400_u128, 300_u128) == 300_u128, '300 < 400'); + assert_eq(@min(400_u128, 300_u128), @300_u128, '300 < 400'); } #[test] fn test_max_u128() { - assert(max(0_u128, 1_u128) == 1_u128, '1 > 0'); - assert(max(5_u128, 7_u128) == 7_u128, '7 > 5'); - assert( - max(18446744073709551615_u128, 9223372036854775808_u128) == 18446744073709551615_u128, + assert_eq(@max(0_u128, 1_u128), @1_u128, '1 > 0'); + assert_eq(@max(5_u128, 7_u128), @7_u128, '7 > 5'); + assert_eq( + @max(18446744073709551615_u128, 9223372036854775808_u128), + @18446744073709551615_u128, '18446744... > 922337...' ); - assert(max(10_u128, 10_u128) == 10_u128, '10 == 10'); - assert(max(0_u128, 0_u128) == 0_u128, '0 == 0'); - assert( - max(18446744073709551615_u128, 18446744073709551615_u128) == 18446744073709551615_u128, + assert_eq(@max(10_u128, 10_u128), @10_u128, '10 == 10'); + assert_eq(@max(0_u128, 0_u128), @0_u128, '0 == 0'); + assert_eq( + @max(18446744073709551615_u128, 18446744073709551615_u128), + @18446744073709551615_u128, '184467...== 184467' ); - assert(max(100_u128, 200_u128) == 200_u128, '200 > 100'); - assert(max(1_u128, 2_u128) == 2_u128, '2 > 1'); - assert( - max(9223372036854775807_u128, 9223372036854775806_u128) == 9223372036854775807_u128, + assert_eq(@max(100_u128, 200_u128), @200_u128, '200 > 100'); + assert_eq(@max(1_u128, 2_u128), @2_u128, '2 > 1'); + assert_eq( + @max(9223372036854775807_u128, 9223372036854775806_u128), + @9223372036854775807_u128, '922337203... > 922337...' ); - assert(max(400_u128, 300_u128) == 400_u128, '400 > 300'); + assert_eq(@max(400_u128, 300_u128), @400_u128, '400 > 300'); } #[test] @@ -197,12 +210,12 @@ fn test_min_u256() { let e = u256 { low: 0, high: 1 }; let f = u256 { low: 0, high: 2 }; - assert(min(a, b) == a, 'a < b'); - assert(min(c, d) == c, 'c < d'); - assert(min(e, f) == e, 'e < f'); - assert(min(a, a) == a, 'a == a'); - assert(min(b, b) == b, 'b == b'); - assert(min(f, f) == f, 'f == f'); + assert_eq(@min(a, b), @a, 'a < b'); + assert_eq(@min(c, d), @c, 'c < d'); + assert_eq(@min(e, f), @e, 'e < f'); + assert_eq(@min(a, a), @a, 'a == a'); + assert_eq(@min(b, b), @b, 'b == b'); + assert_eq(@min(f, f), @f, 'f == f'); } #[test] @@ -214,12 +227,12 @@ fn test_max_u256() { let e = u256 { low: 0, high: 1 }; let f = u256 { low: 0, high: 2 }; - assert(max(a, b) == b, 'b > a'); - assert(max(c, d) == d, 'd > c'); - assert(max(e, f) == f, 'f > e'); - assert(max(a, a) == a, 'a == a'); - assert(max(b, b) == b, 'b == b'); - assert(max(f, f) == f, 'f == f'); + assert_eq(@max(a, b), @b, 'b > a'); + assert_eq(@max(c, d), @d, 'd > c'); + assert_eq(@max(e, f), @f, 'f > e'); + assert_eq(@max(a, a), @a, 'a == a'); + assert_eq(@max(b, b), @b, 'b == b'); + assert_eq(@max(f, f), @f, 'f == f'); } // User-defined types @@ -253,10 +266,10 @@ fn test_min_foo() { let c = Foo { val: 5 }; let d = Foo { val: 7 }; - assert(min(a, b).val == a.val, 'a < b'); - assert(min(c, d).val == c.val, 'c < d'); - assert(min(a, a).val == a.val, 'a == a'); - assert(min(b, b).val == b.val, 'b == b'); + assert_eq(@min(a, b).val, @a.val, 'a < b'); + assert_eq(@min(c, d).val, @c.val, 'c < d'); + assert_eq(@min(a, a).val, @a.val, 'a == a'); + assert_eq(@min(b, b).val, @b.val, 'b == b'); } #[test] @@ -266,8 +279,8 @@ fn test_max_foo() { let c = Foo { val: 5 }; let d = Foo { val: 7 }; - assert(max(a, b).val == b.val, 'b > a'); - assert(max(c, d).val == d.val, 'd > c'); - assert(max(a, a).val == a.val, 'a == a'); - assert(max(b, b).val == b.val, 'b == b'); + assert_eq(@max(a, b).val, @b.val, 'b > a'); + assert_eq(@max(c, d).val, @d.val, 'd > c'); + assert_eq(@max(a, a).val, @a.val, 'a == a'); + assert_eq(@max(b, b).val, @b.val, 'b == b'); } diff --git a/corelib/src/test/dict_test.cairo b/corelib/src/test/dict_test.cairo index f70118e..45c2ae7 100644 --- a/corelib/src/test/dict_test.cairo +++ b/corelib/src/test/dict_test.cairo @@ -2,71 +2,72 @@ use box::BoxTrait; use dict::{Felt252DictTrait, Felt252DictEntryTrait}; use nullable::NullableTrait; use traits::Index; +use test::test_utils::{assert_eq, assert_ne}; #[test] fn test_dict_new() -> Felt252Dict { - Felt252DictTrait::new() + Default::default() } #[test] fn test_dict_squash_empty() { - let mut dict: Felt252Dict = Felt252DictTrait::new(); + let mut dict: Felt252Dict = Default::default(); let squashed_dict = dict.squash(); } #[test] fn test_dict_default_val() { - let mut dict = Felt252DictTrait::new(); + let mut dict = Default::default(); let default_val = dict.get(0); - assert(default_val == 0, 'default_val == 0'); + assert_eq(@default_val, @0, 'default_val == 0'); } #[test] fn test_dict_write_read() { - let mut dict = Felt252DictTrait::new(); + let mut dict = Default::default(); dict.insert(10, 110); dict.insert(11, 111); // TODO(spapini): Use indexing operator. let val10 = dict.index(10); let val11 = dict.index(11); let val12 = dict.index(12); - assert(val10 == 110, 'dict[10] == 110'); - assert(val11 == 111, 'dict[11] == 111'); - assert(val12 == 0, 'default_val == 0'); + assert_eq(@val10, @110, 'dict[10] == 110'); + assert_eq(@val11, @111, 'dict[11] == 111'); + assert_eq(@val12, @0, 'default_val == 0'); } #[test] fn test_dict_entry() { // TODO(Gil): remove type annotation once dict index is fixed. - let mut dict: Felt252Dict = Felt252DictTrait::new(); + let mut dict: Felt252Dict = Default::default(); dict.insert(10, 110); let (entry, value) = dict.entry(10); - assert(value == 110, 'dict[10] == 110'); + assert_eq(@value, @110, 'dict[10] == 110'); let mut dict = entry.finalize(11); - assert(dict[10] == 11, 'dict[10] == 11'); + assert_eq(@dict[10], @11, 'dict[10] == 11'); } #[test] fn test_dict_entry_uninitialized() { - let mut dict: Felt252Dict = Felt252DictTrait::new(); + let mut dict: Felt252Dict = Default::default(); let (entry, value) = dict.entry(10); - assert(value == 0_felt252, 'dict[10] == 0'); + assert_eq(@value, @0_felt252, 'dict[10] == 0'); let mut dict = entry.finalize(110); - assert(dict[10] == 110, 'dict[10] == 110'); + assert_eq(@dict[10], @110, 'dict[10] == 110'); } #[test] fn test_dict_update_twice() { - let mut dict: Felt252Dict = Felt252DictTrait::new(); + let mut dict: Felt252Dict = Default::default(); dict.insert(10, 110); let (entry, value) = dict.entry(10); - assert(value == 110, 'dict[10] == 110'); + assert_eq(@value, @110, 'dict[10] == 110'); dict = entry.finalize(11); - assert(dict[10] == 11, 'dict[10] == 11'); + assert_eq(@dict[10], @11, 'dict[10] == 11'); let (entry, value) = dict.entry(10); - assert(value == 11, 'dict[10] == 11'); + assert_eq(@value, @11, 'dict[10] == 11'); dict = entry.finalize(12); - assert(dict[10] == 12, 'dict[10] == 12'); + assert_eq(@dict[10], @12, 'dict[10] == 12'); } @@ -75,7 +76,7 @@ fn test_dict_update_twice() { /// Calls the destructor of the entry, which in turn calls the destructor of the `Felt252Dict`. #[test] fn test_dict_entry_destruct() { - let mut dict: Felt252Dict = Felt252DictTrait::new(); + let mut dict: Felt252Dict = Default::default(); dict.insert(10, 110); let (entry, value) = dict.entry(10); } @@ -93,7 +94,7 @@ const KEY5: felt252 = 3334603141101959564751596861783084684819726025596122159217 /// Uses a few keys to simulate the 3 possible cases in `validate_felt252_le`. #[test] fn test_dict_big_keys() { - let mut dict = Felt252DictTrait::new(); + let mut dict = Default::default(); dict.insert(KEY1, 1); dict.insert(KEY2, 2); @@ -102,27 +103,27 @@ fn test_dict_big_keys() { dict.insert(KEY5, 5); // TODO(spapini): Use indexing operator. - assert(dict.index(KEY1) == 1, 'KEY1'); - assert(dict.index(KEY2) == 2, 'KEY2'); - assert(dict.index(KEY3) == 3, 'KEY3'); - assert(dict.index(KEY4) == 4, 'KEY4'); - assert(dict.index(KEY5) == 5, 'KEY5'); + assert_eq(@dict.index(KEY1), @1, 'KEY1'); + assert_eq(@dict.index(KEY2), @2, 'KEY2'); + assert_eq(@dict.index(KEY3), @3, 'KEY3'); + assert_eq(@dict.index(KEY4), @4, 'KEY4'); + assert_eq(@dict.index(KEY5), @5, 'KEY5'); } #[test] fn test_dict_of_nullable() { - let mut dict = Felt252DictTrait::new(); + let mut dict = Default::default(); dict.insert(10, nullable_from_box(BoxTrait::new(1))); dict.insert(11, nullable_from_box(BoxTrait::new(2))); // TODO(spapini): Use indexing operator. let val10 = dict.index(10).deref(); let val11 = dict.index(11).deref(); let val12 = dict.index(12); - assert(val10 == 1, 'dict[10] == 1'); - assert(val11 == 2, 'dict[11] == 2'); + assert_eq(@val10, @1, 'dict[10] == 1'); + assert_eq(@val11, @2, 'dict[11] == 2'); assert( match nullable::match_nullable(val12) { - nullable::FromNullableResult::Null(()) => true, + nullable::FromNullableResult::Null => true, nullable::FromNullableResult::NotNull(_) => false, }, 'default_val == null' diff --git a/corelib/src/test/ec_test.cairo b/corelib/src/test/ec_test.cairo index f4076bf..e062e11 100644 --- a/corelib/src/test/ec_test.cairo +++ b/corelib/src/test/ec_test.cairo @@ -6,6 +6,7 @@ use ec::{ ec_point_zero, ec_state_add_mul, ec_state_add, ec_state_finalize, ec_state_init, ec_state_try_finalize_nz }; +use test::test_utils::{assert_eq, assert_ne}; #[test] #[should_panic] @@ -20,47 +21,48 @@ fn test_ec_operations() { let p = ec_point_from_x(1).unwrap(); let p_nz = ec_point_non_zero(p); let (x, y) = ec_point_unwrap(p_nz); - assert(x == 1, 'x != 1'); - assert(y == beta_p2_root | y == -beta_p2_root, 'y is wrong'); + assert_eq(@x, @1, 'x != 1'); + assert(y == beta_p2_root || y == -beta_p2_root, 'y is wrong'); let mut state = ec_state_init(); ec_state_add(ref state, p_nz); let q = ec_state_try_finalize_nz(state).expect('zero point'); let (qx, qy) = ec_point_unwrap(q); - assert(qx == x, 'bad finalize x'); - assert(qy == y, 'bad finalize y'); + assert_eq(@qx, @x, 'bad finalize x'); + assert_eq(@qy, @y, 'bad finalize y'); // Try doing the same thing with the EC op builtin. let mut state = ec_state_init(); ec_state_add_mul(ref state, 1, p_nz); let q3 = ec_state_try_finalize_nz(state).expect('zero point'); let (qx, qy) = ec_point_unwrap(q3); - assert(qx == x, 'bad EC op x'); - assert(qy == y, 'bad EC op y'); + assert_eq(@qx, @x, 'bad EC op x'); + assert_eq(@qy, @y, 'bad EC op y'); // Try computing `p + p` using the ec_mul function. let double_p = ec_mul(p, 2); let (double_x, double_y) = ec_point_unwrap(ec_point_non_zero(double_p)); let expected_double_y = 3572434102142093425782752266058856056057826477682467661647843687948039943621; - assert( - double_x == 75984168971785666410219869038140038216102669781812169677875295511117260233, + assert_eq( + @double_x, + @75984168971785666410219869038140038216102669781812169677875295511117260233, 'bad double x' ); - assert(double_y == expected_double_y | double_y == -expected_double_y, 'bad double y'); + assert(double_y == expected_double_y || double_y == -expected_double_y, 'bad double y'); // Compute `2p - p`. let (sub_x, sub_y) = ec_point_unwrap(ec_point_non_zero(double_p - p)); - assert(sub_x == x, 'bad x for 2p - p'); - assert(sub_y == y, 'bad y for 2p - p'); + assert_eq(@sub_x, @x, 'bad x for 2p - p'); + assert_eq(@sub_y, @y, 'bad y for 2p - p'); // Compute `p - p`. assert(ec_point_is_zero(p - p).into(), 'p - p did not return 0.'); // Compute `(-p) - p`. let (sub2_x, sub2_y) = ec_point_unwrap(ec_point_non_zero(ec_neg(p) - p)); - assert(sub2_x == double_x, 'bad x for (-p) - p'); - assert(sub2_y == -double_y, 'bad y for (-p) - p'); + assert_eq(@sub2_x, @double_x, 'bad x for (-p) - p'); + assert_eq(@sub2_y, @-double_y, 'bad y for (-p) - p'); } #[test] @@ -121,12 +123,14 @@ fn test_ec_mul() { let m = 2713877091499598330239944961141122840311015265600950719674787125185463975936; let (x, y) = ec_point_unwrap(ec_point_non_zero(ec_mul(p, m))); - assert( - x == 2881632108168892236043523177391659237686965655035240771134509747985978822780, + assert_eq( + @x, + @2881632108168892236043523177391659237686965655035240771134509747985978822780, 'ec_mul failed (x).' ); - assert( - y == 591135563672138037839394207500885413019058613584891498394077262936524140839, + assert_eq( + @y, + @591135563672138037839394207500885413019058613584891498394077262936524140839, 'ec_mul failed (y).' ); } diff --git a/corelib/src/test/felt_test.cairo b/corelib/src/test/felt_test.cairo index d466179..6e41b11 100644 --- a/corelib/src/test/felt_test.cairo +++ b/corelib/src/test/felt_test.cairo @@ -1,19 +1,20 @@ use clone::Clone; +use test::test_utils::{assert_eq, assert_ne}; #[test] fn test_felt252_operators() { - assert(1 + 3 == 4, '1 + 3 == 4'); - assert(3 + 6 == 9, '3 + 6 == 9'); - assert(3 - 1 == 2, '3 - 1 == 2'); - assert(1231 - 231 == 1000, '1231-231=1000'); - assert(1 * 3 == 3, '1 * 3 == 3'); - assert(3 * 6 == 18, '3 * 6 == 18'); - assert(-3 == 1 - 4, '-3 == 1 - 4'); + assert_eq(@(1 + 3), @4, '1 + 3 == 4'); + assert_eq(@(3 + 6), @9, '3 + 6 == 9'); + assert_eq(@(3 - 1), @2, '3 - 1 == 2'); + assert_eq(@(1231 - 231), @1000, '1231-231=1000'); + assert_eq(@(1 * 3), @3, '1 * 3 == 3'); + assert_eq(@(3 * 6), @18, '3 * 6 == 18'); + assert_eq(@(-3), @(1 - 4), '-3 == 1 - 4'); } #[test] fn test_felt252_clone() { let felt252_snap = @2; let felt252_clone = felt252_snap.clone(); - assert(felt252_clone == 2, 'felt252_clone == 2'); + assert_eq(@felt252_clone, @2, 'felt252_clone == 2'); } diff --git a/corelib/src/test/hash_test.cairo b/corelib/src/test/hash_test.cairo index 6c6ae2a..986c184 100644 --- a/corelib/src/test/hash_test.cairo +++ b/corelib/src/test/hash_test.cairo @@ -1,11 +1,11 @@ use array::ArrayTrait; +use test::test_utils::{assert_eq, assert_ne}; #[test] fn test_pedersen_hash() { - assert( - hash::pedersen( - 1, 2 - ) == 2592987851775965742543459319508348457290966253241455514226127639100457844774, + assert_eq( + @hash::pedersen(1, 2), + @2592987851775965742543459319508348457290966253241455514226127639100457844774, 'Wrong hash value' ); } @@ -13,16 +13,19 @@ fn test_pedersen_hash() { #[test] fn test_poseidon_hades_permutation() { let (s0, s1, s2) = poseidon::hades_permutation(1, 2, 3); - assert( - s0 == 442682200349489646213731521593476982257703159825582578145778919623645026501, + assert_eq( + @s0, + @442682200349489646213731521593476982257703159825582578145778919623645026501, 'wrong s0' ); - assert( - s1 == 2233832504250924383748553933071188903279928981104663696710686541536735838182, + assert_eq( + @s1, + @2233832504250924383748553933071188903279928981104663696710686541536735838182, 'wrong s1' ); - assert( - s2 == 2512222140811166287287541003826449032093371832913959128171347018667852712082, + assert_eq( + @s2, + @2512222140811166287287541003826449032093371832913959128171347018667852712082, 'wrong s2' ); } @@ -30,25 +33,17 @@ fn test_poseidon_hades_permutation() { #[test] #[available_gas(300000)] fn test_poseidon_hash_span() { - let mut input = ArrayTrait::new(); - input.append(1); - input.append(2); - input.append(3); - // Test odd number of inputs. - assert( - poseidon::poseidon_hash_span( - input.span() - ) == 0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082, + assert_eq( + @poseidon::poseidon_hash_span(array![1, 2, 3].span()), + @0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082, 'wrong result' ); // Test even number of inputs. - input.append(4); - assert( - poseidon::poseidon_hash_span( - input.span() - ) == 0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d, + assert_eq( + @poseidon::poseidon_hash_span(array![1, 2, 3, 4].span()), + @0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d, 'wrong result' ); } diff --git a/corelib/src/test/integer_test.cairo b/corelib/src/test/integer_test.cairo index ae4d522..66ce013 100644 --- a/corelib/src/test/integer_test.cairo +++ b/corelib/src/test/integer_test.cairo @@ -1,38 +1,48 @@ -use core::traits::Into; -use traits::TryInto; +use traits::{Into, TryInto}; use core::traits::Default; use option::OptionTrait; -use integer::{u16_sqrt, u32_sqrt, u64_sqrt, u8_sqrt, BoundedInt, u128_wrapping_sub}; +use integer::{ + BoundedInt, u128_wrapping_sub, u16_sqrt, u32_sqrt, u64_sqrt, u8_sqrt, u512, u256_wide_mul, + u256_as_non_zero, u512_safe_div_rem_by_u256, u128_as_non_zero +}; +use test::test_utils::{assert_eq, assert_ne, assert_le, assert_lt, assert_gt, assert_ge}; #[test] fn test_u8_operators() { - assert(1_u8 == 1_u8, '1 == 1'); - assert(1_u8 != 2_u8, '1 != 2'); - assert(1_u8 + 3_u8 == 4_u8, '1 + 3 == 4'); - assert(3_u8 + 6_u8 == 9_u8, '3 + 6 == 9'); - assert(3_u8 - 1_u8 == 2_u8, '3 - 1 == 2'); - assert(1_u8 * 3_u8 == 3_u8, '1 * 3 == 3'); - assert(2_u8 * 4_u8 == 8_u8, '2 * 4 == 8'); - assert(19_u8 / 7_u8 == 2_u8, '19 / 7 == 2'); - assert(19_u8 % 7_u8 == 5_u8, '19 % 7 == 5'); - assert(231_u8 - 131_u8 == 100_u8, '231-131=100'); - assert(1_u8 < 4_u8, '1 < 4'); - assert(1_u8 <= 4_u8, '1 <= 4'); + assert_eq(@1_u8, @1_u8, '1 == 1'); + assert_ne(@1_u8, @2_u8, '1 != 2'); + assert_eq(@(1_u8 + 3_u8), @4_u8, '1 + 3 == 4'); + assert_eq(@(3_u8 + 6_u8), @9_u8, '3 + 6 == 9'); + assert_eq(@(3_u8 - 1_u8), @2_u8, '3 - 1 == 2'); + assert_eq(@(1_u8 * 3_u8), @3_u8, '1 * 3 == 3'); + assert_eq(@(2_u8 * 4_u8), @8_u8, '2 * 4 == 8'); + assert_eq(@(19_u8 / 7_u8), @2_u8, '19 / 7 == 2'); + assert_eq(@(19_u8 % 7_u8), @5_u8, '19 % 7 == 5'); + assert_eq(@(231_u8 - 131_u8), @100_u8, '231-131=100'); + assert_eq(@((1_u8 | 2_u8)), @3_u8, '1 | 2 == 3'); + assert_eq(@((1_u8 & 2_u8)), @0_u8, '1 & 2 == 0'); + assert_eq(@((1_u8 ^ 2_u8)), @3_u8, '1 ^ 2 == 3'); + assert_eq(@((2_u8 | 2_u8)), @2_u8, '2 | 2 == 2'); + assert_eq(@((2_u8 & 2_u8)), @2_u8, '2 & 2 == 2'); + assert_eq(@((2_u8 & 3_u8)), @2_u8, '2 & 3 == 2'); + assert_eq(@((3_u8 ^ 6_u8)), @5_u8, '3 ^ 6 == 5'); + assert_lt(1_u8, 4_u8, '1 < 4'); + assert_le(1_u8, 4_u8, '1 <= 4'); assert(!(4_u8 < 4_u8), '!(4 < 4)'); - assert(5_u8 <= 5_u8, '5 <= 5'); + assert_le(5_u8, 5_u8, '5 <= 5'); assert(!(5_u8 <= 4_u8), '!(5 <= 8)'); - assert(5_u8 > 2_u8, '5 > 2'); - assert(5_u8 >= 2_u8, '5 >= 2'); + assert_gt(5_u8, 2_u8, '5 > 2'); + assert_ge(5_u8, 2_u8, '5 >= 2'); assert(!(3_u8 > 3_u8), '!(3 > 3)'); - assert(3_u8 >= 3_u8, '3 >= 3'); - assert(u8_sqrt(9) == 3, 'u8_sqrt(9) == 3'); - assert(u8_sqrt(10) == 3, 'u8_sqrt(10) == 3'); - assert(u8_sqrt(0x40) == 0x8, 'u8_sqrt(2^6) == 2^3'); - assert(u8_sqrt(0xff) == 0xf, 'Wrong square root result.'); - assert(u8_sqrt(1) == 1, 'u8_sqrt(1) == 1'); - assert(u8_sqrt(0) == 0, 'u8_sqrt(0) == 0'); - assert(~0x00_u8 == 0xff, '~0x00 == 0xff'); - assert(~0x81_u8 == 0x7e, '~0x81 == 0x7e'); + assert_ge(3_u8, 3_u8, '3 >= 3'); + assert_eq(@u8_sqrt(9), @3, 'u8_sqrt(9) == 3'); + assert_eq(@u8_sqrt(10), @3, 'u8_sqrt(10) == 3'); + assert_eq(@u8_sqrt(0x40), @0x8, 'u8_sqrt(2^6) == 2^3'); + assert_eq(@u8_sqrt(0xff), @0xf, 'Wrong square root result.'); + assert_eq(@u8_sqrt(1), @1, 'u8_sqrt(1) == 1'); + assert_eq(@u8_sqrt(0), @0, 'u8_sqrt(0) == 0'); + assert_eq(@~0x00_u8, @0xff, '~0x00 == 0xff'); + assert_eq(@~0x81_u8, @0x7e, '~0x81 == 0x7e'); } #[test] @@ -103,32 +113,39 @@ fn test_u8_mod_by_0() { #[test] fn test_u16_operators() { - assert(1_u16 == 1_u16, '1 == 1'); - assert(1_u16 != 2_u16, '1 != 2'); - assert(1_u16 + 3_u16 == 4_u16, '1 + 3 == 4'); - assert(3_u16 + 6_u16 == 9_u16, '3 + 6 == 9'); - assert(3_u16 - 1_u16 == 2_u16, '3 - 1 == 2'); - assert(231_u16 - 131_u16 == 100_u16, '231-131=100'); - assert(1_u16 * 3_u16 == 3_u16, '1 * 3 == 3'); - assert(2_u16 * 4_u16 == 8_u16, '2 * 4 == 8'); - assert(51725_u16 / 7_u16 == 7389_u16, '51725 / 7 == 7389'); - assert(51725_u16 % 7_u16 == 2_u16, '51725 % 7 == 2'); - assert(1_u16 < 4_u16, '1 < 4'); - assert(1_u16 <= 4_u16, '1 <= 4'); + assert_eq(@1_u16, @1_u16, '1 == 1'); + assert_ne(@1_u16, @2_u16, '1 != 2'); + assert_eq(@(1_u16 + 3_u16), @4_u16, '1 + 3 == 4'); + assert_eq(@(3_u16 + 6_u16), @9_u16, '3 + 6 == 9'); + assert_eq(@(3_u16 - 1_u16), @2_u16, '3 - 1 == 2'); + assert_eq(@(231_u16 - 131_u16), @100_u16, '231-131=100'); + assert_eq(@(1_u16 * 3_u16), @3_u16, '1 * 3 == 3'); + assert_eq(@(2_u16 * 4_u16), @8_u16, '2 * 4 == 8'); + assert_eq(@(51725_u16 / 7_u16), @7389_u16, '51725 / 7 == 7389'); + assert_eq(@(51725_u16 % 7_u16), @2_u16, '51725 % 7 == 2'); + assert_eq(@((1_u16 | 2_u16)), @3_u16, '1 | 2 == 3'); + assert_eq(@((1_u16 & 2_u16)), @0_u16, '1 & 2 == 0'); + assert_eq(@((1_u16 ^ 2_u16)), @3_u16, '1 ^ 2 == 3'); + assert_eq(@((2_u16 | 2_u16)), @2_u16, '2 | 2 == 2'); + assert_eq(@((2_u16 & 2_u16)), @2_u16, '2 & 2 == 2'); + assert_eq(@((2_u16 & 3_u16)), @2_u16, '2 & 3 == 2'); + assert_eq(@((3_u16 ^ 6_u16)), @5_u16, '3 ^ 6 == 5'); + assert_lt(1_u16, 4_u16, '1 < 4'); + assert_le(1_u16, 4_u16, '1 <= 4'); assert(!(4_u16 < 4_u16), '!(4 < 4)'); - assert(4_u16 <= 4_u16, '4 <= 4'); - assert(5_u16 > 2_u16, '5 > 2'); - assert(5_u16 >= 2_u16, '5 >= 2'); + assert_le(4_u16, 4_u16, '4 <= 4'); + assert_gt(5_u16, 2_u16, '5 > 2'); + assert_ge(5_u16, 2_u16, '5 >= 2'); assert(!(3_u16 > 3_u16), '!(3 > 3)'); - assert(3_u16 >= 3_u16, '3 >= 3'); - assert(u16_sqrt(9) == 3, 'u16_sqrt(9) == 3'); - assert(u16_sqrt(10) == 3, 'u16_sqrt(10) == 3'); - assert(u16_sqrt(0x400) == 0x20, 'u16_sqrt(2^10) == 2^5'); - assert(u16_sqrt(0xffff) == 0xff, 'Wrong square root result.'); - assert(u16_sqrt(1) == 1, 'u64_sqrt(1) == 1'); - assert(u16_sqrt(0) == 0, 'u64_sqrt(0) == 0'); - assert(~0x0000_u16 == 0xffff, '~0x0000 == 0xffff'); - assert(~0x8421_u16 == 0x7bde, '~0x8421 == 0x7bde'); + assert_ge(3_u16, 3_u16, '3 >= 3'); + assert_eq(@u16_sqrt(9), @3, 'u16_sqrt(9) == 3'); + assert_eq(@u16_sqrt(10), @3, 'u16_sqrt(10) == 3'); + assert_eq(@u16_sqrt(0x400), @0x20, 'u16_sqrt(2^10) == 2^5'); + assert_eq(@u16_sqrt(0xffff), @0xff, 'Wrong square root result.'); + assert_eq(@u16_sqrt(1), @1, 'u64_sqrt(1) == 1'); + assert_eq(@u16_sqrt(0), @0, 'u64_sqrt(0) == 0'); + assert_eq(@~0x0000_u16, @0xffff, '~0x0000 == 0xffff'); + assert_eq(@~0x8421_u16, @0x7bde, '~0x8421 == 0x7bde'); } #[test] @@ -199,32 +216,39 @@ fn test_u16_mod_by_0() { #[test] fn test_u32_operators() { - assert(1_u32 == 1_u32, '1 == 1'); - assert(1_u32 != 2_u32, '1 != 2'); - assert(1_u32 + 3_u32 == 4_u32, '1 + 3 == 4'); - assert(3_u32 + 6_u32 == 9_u32, '3 + 6 == 9'); - assert(3_u32 - 1_u32 == 2_u32, '3 - 1 == 2'); - assert(231_u32 - 131_u32 == 100_u32, '231-131=100'); - assert(1_u32 * 3_u32 == 3_u32, '1 * 3 == 3'); - assert(2_u32 * 4_u32 == 8_u32, '2 * 4 == 8'); - assert(510670725_u32 / 7_u32 == 72952960_u32, '510670725 / 7 == 72952960'); - assert(510670725_u32 % 7_u32 == 5_u32, '510670725 % 7 == 5'); - assert(1_u32 < 4_u32, '1 < 4'); - assert(1_u32 <= 4_u32, '1 <= 4'); + assert_eq(@1_u32, @1_u32, '1 == 1'); + assert_ne(@1_u32, @2_u32, '1 != 2'); + assert_eq(@(1_u32 + 3_u32), @4_u32, '1 + 3 == 4'); + assert_eq(@(3_u32 + 6_u32), @9_u32, '3 + 6 == 9'); + assert_eq(@(3_u32 - 1_u32), @2_u32, '3 - 1 == 2'); + assert_eq(@(231_u32 - 131_u32), @100_u32, '231-131=100'); + assert_eq(@(1_u32 * 3_u32), @3_u32, '1 * 3 == 3'); + assert_eq(@(2_u32 * 4_u32), @8_u32, '2 * 4 == 8'); + assert_eq(@(510670725_u32 / 7_u32), @72952960_u32, '510670725 / 7 == 72952960'); + assert_eq(@(510670725_u32 % 7_u32), @5_u32, '510670725 % 7 == 5'); + assert_eq(@((1_u32 | 2_u32)), @3_u32, '1 | 2 == 3'); + assert_eq(@((1_u32 & 2_u32)), @0_u32, '1 & 2 == 0'); + assert_eq(@((1_u32 ^ 2_u32)), @3_u32, '1 ^ 2 == 3'); + assert_eq(@((2_u32 | 2_u32)), @2_u32, '2 | 2 == 2'); + assert_eq(@((2_u32 & 2_u32)), @2_u32, '2 & 2 == 2'); + assert_eq(@((2_u32 & 3_u32)), @2_u32, '2 & 3 == 2'); + assert_eq(@((3_u32 ^ 6_u32)), @5_u32, '3 ^ 6 == 5'); + assert_lt(1_u32, 4_u32, '1 < 4'); + assert_le(1_u32, 4_u32, '1 <= 4'); assert(!(4_u32 < 4_u32), '!(4 < 4)'); - assert(4_u32 <= 4_u32, '4 <= 4'); - assert(5_u32 > 2_u32, '5 > 2'); - assert(5_u32 >= 2_u32, '5 >= 2'); + assert_le(4_u32, 4_u32, '4 <= 4'); + assert_gt(5_u32, 2_u32, '5 > 2'); + assert_ge(5_u32, 2_u32, '5 >= 2'); assert(!(3_u32 > 3_u32), '!(3 > 3)'); - assert(3_u32 >= 3_u32, '3 >= 3'); - assert(u32_sqrt(9) == 3, 'u32_sqrt(9) == 3'); - assert(u32_sqrt(10) == 3, 'u32_sqrt(10) == 3'); - assert(u32_sqrt(0x100000) == 0x400, 'u32_sqrt(2^20) == 2^10'); - assert(u32_sqrt(0xffffffff) == 0xffff, 'Wrong square root result.'); - assert(u32_sqrt(1) == 1, 'u64_sqrt(1) == 1'); - assert(u32_sqrt(0) == 0, 'u64_sqrt(0) == 0'); - assert(~0x00000000_u32 == 0xffffffff, '~0x00000000 == 0xffffffff'); - assert(~0x12345678_u32 == 0xedcba987, '~0x12345678 == 0xedcba987'); + assert_ge(3_u32, 3_u32, '3 >= 3'); + assert_eq(@u32_sqrt(9), @3, 'u32_sqrt(9) == 3'); + assert_eq(@u32_sqrt(10), @3, 'u32_sqrt(10) == 3'); + assert_eq(@u32_sqrt(0x100000), @0x400, 'u32_sqrt(2^20) == 2^10'); + assert_eq(@u32_sqrt(0xffffffff), @0xffff, 'Wrong square root result.'); + assert_eq(@u32_sqrt(1), @1, 'u64_sqrt(1) == 1'); + assert_eq(@u32_sqrt(0), @0, 'u64_sqrt(0) == 0'); + assert_eq(@~0x00000000_u32, @0xffffffff, '~0x00000000 == 0xffffffff'); + assert_eq(@~0x12345678_u32, @0xedcba987, '~0x12345678 == 0xedcba987'); } #[test] @@ -295,32 +319,41 @@ fn test_u32_mod_by_0() { #[test] fn test_u64_operators() { - assert(1_u64 == 1_u64, '1 == 1'); - assert(1_u64 != 2_u64, '1 != 2'); - assert(1_u64 + 3_u64 == 4_u64, '1 + 3 == 4'); - assert(3_u64 + 6_u64 == 9_u64, '3 + 6 == 9'); - assert(3_u64 - 1_u64 == 2_u64, '3 - 1 == 2'); - assert(231_u64 - 131_u64 == 100_u64, '231-131=100'); - assert(1_u64 * 3_u64 == 3_u64, '1 * 3 == 3'); - assert(2_u64 * 4_u64 == 8_u64, '2 * 4 == 8'); - assert(5010670477878974275_u64 / 7_u64 == 715810068268424896_u64, 'Wrong division result.'); - assert(5010670477878974275_u64 % 7_u64 == 3_u64, '5010670477878974275 % 7 == 3'); - assert(1_u64 < 4_u64, '1 < 4'); - assert(1_u64 <= 4_u64, '1 <= 4'); + assert_eq(@1_u64, @1_u64, '1 == 1'); + assert_ne(@1_u64, @2_u64, '1 != 2'); + assert_eq(@(1_u64 + 3_u64), @4_u64, '1 + 3 == 4'); + assert_eq(@(3_u64 + 6_u64), @9_u64, '3 + 6 == 9'); + assert_eq(@(3_u64 - 1_u64), @2_u64, '3 - 1 == 2'); + assert_eq(@(231_u64 - 131_u64), @100_u64, '231-131=100'); + assert_eq(@(1_u64 * 3_u64), @3_u64, '1 * 3 == 3'); + assert_eq(@(2_u64 * 4_u64), @8_u64, '2 * 4 == 8'); + assert_eq( + @(5010670477878974275_u64 / 7_u64), @715810068268424896_u64, 'Wrong division result.' + ); + assert_eq(@(5010670477878974275_u64 % 7_u64), @3_u64, '5010670477878974275 % 7 == 3'); + assert_eq(@((1_u64 | 2_u64)), @3_u64, '1 | 2 == 3'); + assert_eq(@((1_u64 & 2_u64)), @0_u64, '1 & 2 == 0'); + assert_eq(@((1_u64 ^ 2_u64)), @3_u64, '1 ^ 2 == 3'); + assert_eq(@((2_u64 | 2_u64)), @2_u64, '2 | 2 == 2'); + assert_eq(@((2_u64 & 2_u64)), @2_u64, '2 & 2 == 2'); + assert_eq(@((2_u64 & 3_u64)), @2_u64, '2 & 3 == 2'); + assert_eq(@((3_u64 ^ 6_u64)), @5_u64, '3 ^ 6 == 5'); + assert_lt(1_u64, 4_u64, '1 < 4'); + assert_le(1_u64, 4_u64, '1 <= 4'); assert(!(4_u64 < 4_u64), '!(4 < 4)'); - assert(4_u64 <= 4_u64, '4 <= 4'); - assert(5_u64 > 2_u64, '5 > 2'); - assert(5_u64 >= 2_u64, '5 >= 2'); + assert_le(4_u64, 4_u64, '4 <= 4'); + assert_gt(5_u64, 2_u64, '5 > 2'); + assert_ge(5_u64, 2_u64, '5 >= 2'); assert(!(3_u64 > 3_u64), '!(3 > 3)'); - assert(3_u64 >= 3_u64, '3 >= 3'); - assert(u64_sqrt(9) == 3, 'u64_sqrt(9) == 3'); - assert(u64_sqrt(10) == 3, 'u64_sqrt(10) == 3'); - assert(u64_sqrt(0x10000000000) == 0x100000, 'u64_sqrt(2^40) == 2^20'); - assert(u64_sqrt(0xffffffffffffffff) == 0xffffffff, 'Wrong square root result.'); - assert(u64_sqrt(1) == 1, 'u64_sqrt(1) == 1'); - assert(u64_sqrt(0) == 0, 'u64_sqrt(0) == 0'); - assert(~0x0000000000000000_u64 == 0xffffffffffffffff, '~0x0..0 == 0xf..f'); - assert(~0x123456789abcdef1_u64 == 0xedcba9876543210e, '~0x12..ef1 == 0xed..10e'); + assert_ge(3_u64, 3_u64, '3 >= 3'); + assert_eq(@u64_sqrt(9), @3, 'u64_sqrt(9) == 3'); + assert_eq(@u64_sqrt(10), @3, 'u64_sqrt(10) == 3'); + assert_eq(@u64_sqrt(0x10000000000), @0x100000, 'u64_sqrt(2^40) == 2^20'); + assert_eq(@u64_sqrt(0xffffffffffffffff), @0xffffffff, 'Wrong square root result.'); + assert_eq(@u64_sqrt(1), @1, 'u64_sqrt(1) == 1'); + assert_eq(@u64_sqrt(0), @0, 'u64_sqrt(0) == 0'); + assert_eq(@~0x0000000000000000_u64, @0xffffffffffffffff, '~0x0..0 == 0xf..f'); + assert_eq(@~0x123456789abcdef1_u64, @0xedcba9876543210e, '~0x12..ef1 == 0xed..10e'); } #[test] @@ -391,48 +424,53 @@ fn test_u64_mod_by_0() { #[test] fn test_u128_operators() { - assert(1_u128 == 1_u128, '1 == 1'); - assert(!(1_u128 == 2_u128), '!(1 == 2)'); - assert(1_u128 + 3_u128 == 4_u128, '1 + 3 == 4'); - assert(3_u128 + 6_u128 == 9_u128, '3 + 6 == 9'); - assert(3_u128 - 1_u128 == 2_u128, '3 - 1 == 2'); - assert(1231_u128 - 231_u128 == 1000_u128, '1231-231=1000'); - assert(1_u128 * 3_u128 == 3_u128, '1 * 3 == 3'); - assert(2_u128 * 4_u128 == 8_u128, '2 * 4 == 8'); - assert(8_u128 / 2_u128 == 4_u128, '8 / 2 == 4'); - assert(8_u128 % 2_u128 == 0_u128, '8 % 2 == 0'); - assert(7_u128 / 3_u128 == 2_u128, '7 / 3 == 2'); - assert(7_u128 % 3_u128 == 1_u128, '7 % 3 == 1'); - assert(1_u128 < 4_u128, '1 < 4'); - assert(1_u128 <= 4_u128, '1 <= 4'); + assert_eq(@1_u128, @1_u128, '1 == 1'); + assert_ne(@1_u128, @2_u128, '1 != 2'); + assert_eq(@(1_u128 + 3_u128), @4_u128, '1 + 3 == 4'); + assert_eq(@(3_u128 + 6_u128), @9_u128, '3 + 6 == 9'); + assert_eq(@(3_u128 - 1_u128), @2_u128, '3 - 1 == 2'); + assert_eq(@(1231_u128 - 231_u128), @1000_u128, '1231-231=1000'); + assert_eq(@(1_u128 * 3_u128), @3_u128, '1 * 3 == 3'); + assert_eq(@(2_u128 * 4_u128), @8_u128, '2 * 4 == 8'); + assert_eq(@(8_u128 / 2_u128), @4_u128, '8 / 2 == 4'); + assert_eq(@(8_u128 % 2_u128), @0_u128, '8 % 2 == 0'); + assert_eq(@(7_u128 / 3_u128), @2_u128, '7 / 3 == 2'); + assert_eq(@(7_u128 % 3_u128), @1_u128, '7 % 3 == 1'); + assert_lt(1_u128, 4_u128, '1 < 4'); + assert_le(1_u128, 4_u128, '1 <= 4'); assert(!(4_u128 < 4_u128), '!(4 < 4)'); - assert(4_u128 <= 4_u128, '4 <= 4'); - assert(5_u128 > 2_u128, '5 > 2'); - assert(5_u128 >= 2_u128, '5 >= 2'); + assert_le(4_u128, 4_u128, '4 <= 4'); + assert_gt(5_u128, 2_u128, '5 > 2'); + assert_ge(5_u128, 2_u128, '5 >= 2'); assert(!(3_u128 > 3_u128), '!(3 > 3)'); - assert(3_u128 >= 3_u128, '3 >= 3'); - assert((1_u128 | 2_u128) == 3_u128, '1 | 2 == 3'); - assert((1_u128 & 2_u128) == 0_u128, '1 & 2 == 0'); - assert((1_u128 ^ 2_u128) == 3_u128, '1 ^ 2 == 3'); - assert((2_u128 | 2_u128) == 2_u128, '2 | 2 == 2'); - assert((2_u128 & 2_u128) == 2_u128, '2 & 2 == 2'); - assert((2_u128 & 3_u128) == 2_u128, '2 & 3 == 2'); - assert((3_u128 ^ 6_u128) == 5_u128, '3 ^ 6 == 5'); - assert(u128_sqrt(9) == 3, 'u128_sqrt(9) == 3'); - assert(u128_sqrt(10) == 3, 'u128_sqrt(10) == 3'); - assert(u128_sqrt(0x10000000000000000000000000) == 0x4000000000000, 'u128_sqrt(2^100) == 2^50'); - assert( - u128_sqrt(0xffffffffffffffffffffffffffffffff) == 0xffffffffffffffff, + assert_ge(3_u128, 3_u128, '3 >= 3'); + assert_eq(@((1_u128 | 2_u128)), @3_u128, '1 | 2 == 3'); + assert_eq(@((1_u128 & 2_u128)), @0_u128, '1 & 2 == 0'); + assert_eq(@((1_u128 ^ 2_u128)), @3_u128, '1 ^ 2 == 3'); + assert_eq(@((2_u128 | 2_u128)), @2_u128, '2 | 2 == 2'); + assert_eq(@((2_u128 & 2_u128)), @2_u128, '2 & 2 == 2'); + assert_eq(@((2_u128 & 3_u128)), @2_u128, '2 & 3 == 2'); + assert_eq(@((3_u128 ^ 6_u128)), @5_u128, '3 ^ 6 == 5'); + assert_eq(@u128_sqrt(9), @3, 'u128_sqrt(9) == 3'); + assert_eq(@u128_sqrt(10), @3, 'u128_sqrt(10) == 3'); + assert_eq( + @u128_sqrt(0x10000000000000000000000000), @0x4000000000000, 'u128_sqrt(2^100) == 2^50' + ); + assert_eq( + @u128_sqrt(0xffffffffffffffffffffffffffffffff), + @0xffffffffffffffff, 'Wrong square root result.' ); - assert(u128_sqrt(1) == 1, 'u128_sqrt(1) == 1'); - assert(u128_sqrt(0) == 0, 'u128_sqrt(0) == 0'); - assert( - ~0x00000000000000000000000000000000_u128 == 0xffffffffffffffffffffffffffffffff, + assert_eq(@u128_sqrt(1), @1, 'u128_sqrt(1) == 1'); + assert_eq(@u128_sqrt(0), @0, 'u128_sqrt(0) == 0'); + assert_eq( + @~0x00000000000000000000000000000000_u128, + @0xffffffffffffffffffffffffffffffff, '~0x0..0 == 0xf..f' ); - assert( - ~0x123456789abcdef123456789abcdef12_u128 == 0xedcba9876543210edcba9876543210ed, + assert_eq( + @~0x123456789abcdef123456789abcdef12_u128, + @0xedcba9876543210edcba9876543210ed, '~0x12..ef12 == 0xed..10ed' ); } @@ -469,27 +507,27 @@ fn test_u128_sub_overflow_4() { fn test_u128_wrapping_sub_1() { let max_u128: u128 = BoundedInt::max(); let should_be_max = u128_wrapping_sub(0_u128, 1_u128); - assert(max_u128 == should_be_max, 'Should be max u128') + assert_eq(@max_u128, @should_be_max, 'Should be max u128') } #[test] fn test_u128_wrapping_sub_2() { let max_u128_minus_two: u128 = BoundedInt::max() - 2; let should_be_max = u128_wrapping_sub(0_u128, 3_u128); - assert(max_u128_minus_two == should_be_max, 'Should be max u128 - 2') + assert_eq(@max_u128_minus_two, @should_be_max, 'Should be max u128 - 2') } #[test] fn test_u128_wrapping_sub_3() { let max_u128_minus_899: u128 = BoundedInt::max() - 899; let should_be_max = u128_wrapping_sub(100, 1000); - assert(max_u128_minus_899 == should_be_max, 'Should be max u128 - 899') + assert_eq(@max_u128_minus_899, @should_be_max, 'Should be max u128 - 899') } #[test] fn test_u128_wrapping_sub_4() { let should_be_zero = u128_wrapping_sub(0_u128, 0_u128); - assert(should_be_zero == 0, 'Should be 0') + assert_eq(@should_be_zero, @0, 'Should be 0') } #[test] @@ -540,10 +578,10 @@ fn pow_2_127() -> u256 { #[test] fn test_u256_from_felt252() { - assert(1.into() == 1_u256, 'into 1'); - assert( - (170141183460469231731687303715884105728 * 2) - .into() == 0x100000000000000000000000000000000_u256, + assert_eq(@1.into(), @1_u256, 'into 1'); + assert_eq( + @(170141183460469231731687303715884105728 * 2).into(), + @0x100000000000000000000000000000000_u256, 'into 2**128' ); } @@ -551,45 +589,48 @@ fn test_u256_from_felt252() { #[test] fn test_u256_operators() { let max_u128 = 0xffffffffffffffffffffffffffffffff_u256; - assert( - 0x100000000000000000000000000000001 - + 0x300000000000000000000000000000002 == 0x400000000000000000000000000000003_u256, + assert_eq( + @(0x100000000000000000000000000000001 + 0x300000000000000000000000000000002), + @0x400000000000000000000000000000003_u256, 'no Overflow' ); - assert( - 0x180000000000000000000000000000000 - + 0x380000000000000000000000000000000 == 0x500000000000000000000000000000000_u256, + assert_eq( + @(0x180000000000000000000000000000000 + 0x380000000000000000000000000000000), + @0x500000000000000000000000000000000_u256, 'basic Overflow' ); - assert( - 0x400000000000000000000000000000003 - - 0x100000000000000000000000000000001 == 0x300000000000000000000000000000002_u256, + assert_eq( + @(0x400000000000000000000000000000003 - 0x100000000000000000000000000000001), + @0x300000000000000000000000000000002_u256, 'no UF' ); - assert( - 0x500000000000000000000000000000000 - - 0x180000000000000000000000000000000 == 0x380000000000000000000000000000000_u256, + assert_eq( + @(0x500000000000000000000000000000000 - 0x180000000000000000000000000000000), + @0x380000000000000000000000000000000_u256, 'basic UF' ); - assert( - 0x400000000000000000000000000000003 * 1 == 0x400000000000000000000000000000003_u256, + assert_eq( + @(0x400000000000000000000000000000003 * 1), + @0x400000000000000000000000000000003_u256, 'mul by 1' ); - assert( - 0x400000000000000000000000000000003 * 2 == 0x800000000000000000000000000000006_u256, + assert_eq( + @(0x400000000000000000000000000000003 * 2), + @0x800000000000000000000000000000006_u256, 'mul by 2' ); - assert( - 0x80000000000000000000000000000000 * 2 == 0x100000000000000000000000000000000_u256, + assert_eq( + @(0x80000000000000000000000000000000 * 2), + @0x100000000000000000000000000000000_u256, 'basic mul Overflow' ); - assert( - max_u128 - * max_u128 == 0xfffffffffffffffffffffffffffffffe00000000000000000000000000000001_u256, + assert_eq( + @(max_u128 * max_u128), + @0xfffffffffffffffffffffffffffffffe00000000000000000000000000000001_u256, 'max_u128 * max_u128' ); - assert(max_u128 * 1 == max_u128, 'max_u128 * 1'); - assert(1 * max_u128 == max_u128, '1 * max_u128'); + assert_eq(@(max_u128 * 1), @max_u128, 'max_u128 * 1'); + assert_eq(@(1 * max_u128), @max_u128, '1 * max_u128'); let v0_2: u256 = 0x000000000000000000000000000000002; let v0_3: u256 = 0x000000000000000000000000000000003; let v1_1: u256 = 0x100000000000000000000000000000001; @@ -600,62 +641,79 @@ fn test_u256_operators() { let v2_3: u256 = 0x200000000000000000000000000000003; let v3_0: u256 = 0x300000000000000000000000000000000; let v3_2: u256 = 0x300000000000000000000000000000002; - assert((v1_2 | v2_2) == v3_2, '1.2|2.2==3.2'); - assert((v2_1 | v2_2) == v2_3, '2.1|2.2==2.3'); - assert((v2_2 | v1_2) == v3_2, '2.2|1.2==3.2'); - assert((v2_2 | v2_1) == v2_3, '2.2|2.1==2.3'); - assert((v1_2 & v2_2) == v0_2, '1.2&2.2==0.2'); - assert((v2_1 & v2_2) == v2_0, '2.1&2.2==2.0'); - assert((v2_2 & v1_2) == v0_2, '2.2&1.2==0.2'); - assert((v2_2 & v2_1) == v2_0, '2.2&2.1==2.0'); - assert((v1_2 ^ v2_2) == v3_0, '1.2^2.2==3.0'); - assert((v2_1 ^ v2_2) == v0_3, '2.1^2.2==0.3'); - assert((v2_2 ^ v1_2) == v3_0, '2.2^1.2==3.0'); - assert((v2_2 ^ v2_1) == v0_3, '2.2^2.1==0.3'); - assert(v1_2 < v2_2, '1.2<2.2'); - assert(v2_1 < v2_2, '2.1<2.2'); + assert_eq(@(v1_2 | v2_2), @v3_2, '1.2|2.2==3.2'); + assert_eq(@(v2_1 | v2_2), @v2_3, '2.1|2.2==2.3'); + assert_eq(@(v2_2 | v1_2), @v3_2, '2.2|1.2==3.2'); + assert_eq(@(v2_2 | v2_1), @v2_3, '2.2|2.1==2.3'); + assert_eq(@(v1_2 & v2_2), @v0_2, '1.2&2.2==0.2'); + assert_eq(@(v2_1 & v2_2), @v2_0, '2.1&2.2==2.0'); + assert_eq(@(v2_2 & v1_2), @v0_2, '2.2&1.2==0.2'); + assert_eq(@(v2_2 & v2_1), @v2_0, '2.2&2.1==2.0'); + assert_eq(@(v1_2 ^ v2_2), @v3_0, '1.2^2.2==3.0'); + assert_eq(@(v2_1 ^ v2_2), @v0_3, '2.1^2.2==0.3'); + assert_eq(@(v2_2 ^ v1_2), @v3_0, '2.2^1.2==3.0'); + assert_eq(@(v2_2 ^ v2_1), @v0_3, '2.2^2.1==0.3'); + assert_lt(v1_2, v2_2, '1.2<2.2'); + assert_lt(v2_1, v2_2, '2.1<2.2'); assert(!(v2_2 < v1_2), '2.2<1.2'); assert(!(v2_2 < v2_1), '2.2<2.1'); assert(!(v2_2 < v2_2), '2.2<2.2'); - assert(v1_2 <= v2_2, '1.2<=2.2'); - assert(v2_1 <= v2_2, '2.1<=2.2'); + assert_le(v1_2, v2_2, '1.2<=2.2'); + assert_le(v2_1, v2_2, '2.1<=2.2'); assert(!(v2_2 <= v1_2), '2.2<=1.2'); assert(!(v2_2 <= v2_1), '2.2<=2.1'); - assert(v2_2 <= v2_2, '2.2<=2.2'); + assert_le(v2_2, v2_2, '2.2<=2.2'); assert(!(v1_2 > v2_2), '1.2>2.2'); assert(!(v2_1 > v2_2), '2.1>2.2'); - assert(v2_2 > v1_2, '2.2>1.2'); - assert(v2_2 > v2_1, '2.2>2.1'); + assert_gt(v2_2, v1_2, '2.2>1.2'); + assert_gt(v2_2, v2_1, '2.2>2.1'); assert(!(v2_2 > v2_2), '2.2>2.2'); assert(!(v1_2 >= v2_2), '1.2>=2.2'); assert(!(v2_1 >= v2_2), '2.1>=2.2'); - assert(v2_2 >= v1_2, '2.2>=1.2'); - assert(v2_2 >= v2_1, '2.2>=2.1'); - assert(v2_2 >= v2_2, '2.2>=2.2'); - - assert(v3_2 / v1_1 == v0_2, 'u256 div'); - assert( - 0x400000000000000000000000000000002 / 3 == 0x155555555555555555555555555555556_u256, + assert_ge(v2_2, v1_2, '2.2>=1.2'); + assert_ge(v2_2, v2_1, '2.2>=2.1'); + assert_ge(v2_2, v2_2, '2.2>=2.2'); + + assert_eq(@(v3_2 / v1_1), @v0_2, 'u256 div'); + assert_eq( + @(0x400000000000000000000000000000002 / 3), + @0x155555555555555555555555555555556_u256, 'u256 div' ); - assert(0x400000000000000000000000000000002 % 3 == 0_u256, 'u256 mod'); - assert(0x10000000000000000 / 0x10000000000000000 == 1_u256, 'u256 div'); - assert(0x10000000000000000 % 0x10000000000000000 == 0_u256, 'u256 mod'); - assert( - 0x1000000000000000000000000000000000000000000000000 - / 0x1000000000000000000000000000000000000000000000000 == 1_u256, + assert_eq(@(0x400000000000000000000000000000002 % 3), @0_u256, 'u256 mod'); + assert_eq(@(0x10000000000000000 / 0x10000000000000000), @1_u256, 'u256 div'); + assert_eq(@(0x10000000000000000 % 0x10000000000000000), @0_u256, 'u256 mod'); + assert_eq( + @(0x1000000000000000000000000000000000000000000000000 + / 0x1000000000000000000000000000000000000000000000000), + @1_u256, 'u256 div' ); - assert( - 0x1000000000000000000000000000000000000000000000000 % 0x1000000000000000000000000000000000000000000000000 == 0_u256, + assert_eq( + @(0x1000000000000000000000000000000000000000000000000 % 0x1000000000000000000000000000000000000000000000000), + @0_u256, 'u256 mod' ); - assert( - ~max_u128 == 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000, + assert_eq(@(BoundedInt::max() % 0x100000000), @0xffffffff_u256, 'u256 mod'); + assert_eq(@(BoundedInt::max() % 0x10000000000000000), @0xffffffffffffffff_u256, 'u256 mod'); + assert_eq( + @(BoundedInt::max() / 0x10000000000000000000000000000000000000000), + @0xffffffffffffffffffffffff_u256, + 'u256 div' + ); + assert_eq( + @(BoundedInt::max() / 0x1000000000000000000000000000000000000000000000000), + @0xffffffffffffffff_u256, + 'u256 div' + ); + assert_eq( + @~max_u128, + @0xffffffffffffffffffffffffffffffff00000000000000000000000000000000, '~0x0..0f..f == 0xf..f0..0' ); - assert( - ~0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 == max_u128, + assert_eq( + @~0xffffffffffffffffffffffffffffffff00000000000000000000000000000000, + @max_u128, '~0xf..f0..0 == 0x0..0f..f' ); } @@ -685,16 +743,15 @@ fn test_u256_mul_overflow_2() { pow_2_127() * 0x200000000000000000000000000000000; } -use integer::{u512, u256_wide_mul, u256_as_non_zero, u512_safe_div_rem_by_u256}; - #[test] fn test_u256_wide_mul() { - assert(u256_wide_mul(0, 0) == u512 { limb0: 0, limb1: 0, limb2: 0, limb3: 0 }, '0 * 0 != 0'); - assert( - u256_wide_mul( + assert_eq(@u256_wide_mul(0, 0), @u512 { limb0: 0, limb1: 0, limb2: 0, limb3: 0 }, '0 * 0 != 0'); + assert_eq( + @u256_wide_mul( 0x1001001001001001001001001001001001001001001001001001, 0x1000100010001000100010001000100010001000100010001000100010001 - ) == u512 { + ), + @u512 { limb0: 0x33233223222222122112111111011001, limb1: 0x54455445544554454444443443343333, limb2: 0x21222222322332333333433443444444, @@ -714,17 +771,17 @@ fn test_u512_safe_div_rem_by_u256() { limb2: 0x21222222322332333333433443444444, limb3: 0x1001101111112112 }; - let (q, r) = u512_safe_div_rem_by_u256(zero, u256_as_non_zero(1)); + let (q, r) = u512_safe_div_rem_by_u256(zero, 1_u256.try_into().unwrap()); assert(q == zero, '0 / 1 != 0'); assert(r == 0, '0 % 1 != 0'); - let (q, r) = u512_safe_div_rem_by_u256(one, u256_as_non_zero(1)); + let (q, r) = u512_safe_div_rem_by_u256(one, 1_u256.try_into().unwrap()); assert(q == one, '1 / 1 != 1'); assert(r == 0, '1 % 1 != 0'); - let (q, r) = u512_safe_div_rem_by_u256(large_num, u256_as_non_zero(1)); + let (q, r) = u512_safe_div_rem_by_u256(large_num, 1_u256.try_into().unwrap()); assert(q == large_num, 'LARGE / 1 != LARGE'); assert(r == 0, 'LARGE % 1 != 0'); let (q, r) = u512_safe_div_rem_by_u256( - large_num, u256_as_non_zero(0x33233223222222122112111111011001) + large_num, 0x33233223222222122112111111011001_u256.try_into().unwrap() ); assert( q == u512 { @@ -746,12 +803,12 @@ fn test_min() { let min_u64: u64 = BoundedInt::min(); let min_u128: u128 = BoundedInt::min(); let min_u256: u256 = BoundedInt::min(); - assert(min_u8 == 0_u8, 'not zero'); - assert(min_u16 == 0_u16, 'not zero'); - assert(min_u32 == 0_u32, 'not zero'); - assert(min_u64 == 0_u64, 'not zero'); - assert(min_u128 == 0_u128, 'not zero'); - assert(min_u256 == 0_u256, 'not zero'); + assert_eq(@min_u8, @0_u8, 'not zero'); + assert_eq(@min_u16, @0_u16, 'not zero'); + assert_eq(@min_u32, @0_u32, 'not zero'); + assert_eq(@min_u64, @0_u64, 'not zero'); + assert_eq(@min_u128, @0_u128, 'not zero'); + assert_eq(@min_u256, @0_u256, 'not zero'); } #[test] @@ -762,13 +819,14 @@ fn test_max() { let max_u64: u64 = BoundedInt::max(); let max_u128: u128 = BoundedInt::max(); let max_u256: u256 = BoundedInt::max(); - assert(max_u8 == 0xff_u8, 'not max'); - assert(max_u16 == 0xffff_u16, 'not max'); - assert(max_u32 == 0xffffffff_u32, 'not max'); - assert(max_u64 == 0xffffffffffffffff_u64, 'not max'); - assert(max_u128 == 0xffffffffffffffffffffffffffffffff_u128, 'not max'); - assert( - max_u256 == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_u256, + assert_eq(@max_u8, @0xff_u8, 'not max'); + assert_eq(@max_u16, @0xffff_u16, 'not max'); + assert_eq(@max_u32, @0xffffffff_u32, 'not max'); + assert_eq(@max_u64, @0xffffffffffffffff_u64, 'not max'); + assert_eq(@max_u128, @0xffffffffffffffffffffffffffffffff_u128, 'not max'); + assert_eq( + @max_u256, + @0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_u256, 'not max' ); } @@ -810,73 +868,70 @@ fn test_max_u256_plus_1_overflow() { #[test] fn test_default_values() { - assert(Default::default() == 0, '0 == 0'); - assert(Default::default() == 0_u8, '0 == 0'); - assert(Default::default() == 0_u16, '0 == 0'); - assert(Default::default() == 0_u32, '0 == 0'); - assert(Default::default() == 0_u64, '0 == 0'); - assert(Default::default() == 0_u128, '0 == 0'); - assert(Default::default() == 0_u256, '0 == 0'); + assert_eq(@Default::default(), @0, '0 == 0'); + assert_eq(@Default::default(), @0_u8, '0 == 0'); + assert_eq(@Default::default(), @0_u16, '0 == 0'); + assert_eq(@Default::default(), @0_u32, '0 == 0'); + assert_eq(@Default::default(), @0_u64, '0 == 0'); + assert_eq(@Default::default(), @0_u128, '0 == 0'); + assert_eq(@Default::default(), @0_u256, '0 == 0'); } #[test] fn test_default_felt252dict_values() { - assert(Felt252DictValue::zero_default() == 0, '0 == 0'); - assert(Felt252DictValue::zero_default() == 0_u8, '0 == 0'); - assert(Felt252DictValue::zero_default() == 0_u16, '0 == 0'); - assert(Felt252DictValue::zero_default() == 0_u32, '0 == 0'); - assert(Felt252DictValue::zero_default() == 0_u64, '0 == 0'); - assert(Felt252DictValue::zero_default() == 0_u128, '0 == 0'); + assert_eq(@Felt252DictValue::zero_default(), @0, '0 == 0'); + assert_eq(@Felt252DictValue::zero_default(), @0_u8, '0 == 0'); + assert_eq(@Felt252DictValue::zero_default(), @0_u16, '0 == 0'); + assert_eq(@Felt252DictValue::zero_default(), @0_u32, '0 == 0'); + assert_eq(@Felt252DictValue::zero_default(), @0_u64, '0 == 0'); + assert_eq(@Felt252DictValue::zero_default(), @0_u128, '0 == 0'); } #[test] fn test_u256_sqrt() { - assert(u256_sqrt(9.into()) == 3, 'u256_sqrt(9) == 3'); - assert(u256_sqrt(10.into()) == 3, 'u256_sqrt(10) == 3'); - assert( - u256_sqrt(1267650600228229401496703205376.into()) == 1125899906842624, + assert_eq(@u256_sqrt(9.into()), @3, 'u256_sqrt(9) == 3'); + assert_eq(@u256_sqrt(10.into()), @3, 'u256_sqrt(10) == 3'); + assert_eq( + @u256_sqrt(1267650600228229401496703205376.into()), + @1125899906842624, 'u256_sqrt(2^100) == 2^50' ); - assert( - u256_sqrt(340282366920938463463374607431768211455.into()) == 18446744073709551615, + assert_eq( + @u256_sqrt(340282366920938463463374607431768211455.into()), + @18446744073709551615, 'Wrong square root result.' ); - assert(u256_sqrt(1.into()) == 1, 'u256_sqrt(1) == 1'); - assert(u256_sqrt(0.into()) == 0, 'u256_sqrt(0) == 0'); + assert_eq(@u256_sqrt(1.into()), @1, 'u256_sqrt(1) == 1'); + assert_eq(@u256_sqrt(0.into()), @0, 'u256_sqrt(0) == 0'); - assert(u256_sqrt(BoundedInt::max()) == BoundedInt::max(), 'u256::MAX**0.5==u128::MAX'); + assert_eq(@u256_sqrt(BoundedInt::max()), @BoundedInt::max(), 'u256::MAX**0.5==u128::MAX'); let (high, low) = integer::u128_wide_mul(BoundedInt::max(), BoundedInt::max()); - assert(u256_sqrt(u256 { low, high }) == BoundedInt::max(), '(u128::MAX**2)**0.5==u128::MAX'); + assert_eq(@u256_sqrt(u256 { low, high }), @BoundedInt::max(), '(u128::MAX**2)**0.5==u128::MAX'); } #[test] fn test_u256_try_into_felt252() { let FELT252_PRIME = 0x800000000000011000000000000000000000000000000000000000000000001_u256; - assert(1_u256.try_into().unwrap() == 1_felt252, '1 == 1'_felt252); - assert( - 0x800000000000011000000000000000000000000000000000000000000000000_u256 - .try_into() - .unwrap() == 0x800000000000011000000000000000000000000000000000000000000000000_felt252, + assert_eq(@1_u256.try_into().unwrap(), @1_felt252, '1 == 1'_felt252); + assert_eq( + @0x800000000000011000000000000000000000000000000000000000000000000_u256.try_into().unwrap(), + @0x800000000000011000000000000000000000000000000000000000000000000_felt252, 'P-1 == P-1'_felt252 ); - assert( - 0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffff_u256 - .try_into() - .unwrap() == 0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffff_felt252, + assert_eq( + @0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffff_u256.try_into().unwrap(), + @0x800000000000010ffffffffffffffffffffffffffffffffffffffffffffffff_felt252, 'P-2 == P-2'_felt252 ); - assert( - 0x800000000000011000000000000000000000000000000000000000000000001_u256.try_into().is_none(), - 'prime is not felt252' - ); - assert( - 0x800000000000011000000000000000000000000000000000000000000000002_u256.try_into().is_none(), - 'prime+1 is not felt252' - ); - assert( - 0x800000000000011000000000000000100000000000000000000000000000001_u256.try_into().is_none(), - 'prime+2**128 is not felt252' - ); + let f: Option = 0x800000000000011000000000000000000000000000000000000000000000001_u256 + .try_into(); + assert(f.is_none(), 'prime is not felt252'); + let f: Option = 0x800000000000011000000000000000000000000000000000000000000000002_u256 + .try_into(); + assert(f.is_none(), 'prime+1 is not felt252'); + let f: Option = 0x800000000000011000000000000000100000000000000000000000000000001_u256 + .try_into(); + assert(f.is_none(), 'prime+2**128 is not felt252'); } fn cast_must_pass< @@ -895,10 +950,10 @@ fn cast_must_pass< >( ui: A, uj: B ) -> bool { - (uj == ui.into() - & (ui == uj.try_into().unwrap()) - & (BoundedInt::::min() == BoundedInt::::min().into() - & (BoundedInt::::min() == BoundedInt::::min().try_into().unwrap()))) + uj == ui.into() + && ui == uj.try_into().unwrap() + && BoundedInt::::min() == BoundedInt::::min().into() + && BoundedInt::::min() == BoundedInt::::min().try_into().unwrap() } #[test] fn proper_cast() { @@ -916,20 +971,20 @@ fn proper_cast() { #[test] fn test_into_self_type() { - assert(0xFF_u8.into() == 0xFF_u8, 'u8 into u8'); - assert(0xFFFF_u16.into() == 0xFFFF_u16, 'u16 into u16'); - assert(0xFFFFFFFF_u32.into() == 0xFFFFFFFF_u32, 'u32 into u32'); - assert(0xFFFFFFFFFFFFFFFF_u64.into() == 0xFFFFFFFFFFFFFFFF_u64, 'u64 into u64'); - assert( - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128.into() == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128, + assert_eq(@0xFF_u8.into(), @0xFF_u8, 'u8 into u8'); + assert_eq(@0xFFFF_u16.into(), @0xFFFF_u16, 'u16 into u16'); + assert_eq(@0xFFFFFFFF_u32.into(), @0xFFFFFFFF_u32, 'u32 into u32'); + assert_eq(@0xFFFFFFFFFFFFFFFF_u64.into(), @0xFFFFFFFFFFFFFFFF_u64, 'u64 into u64'); + assert_eq( + @0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128.into(), + @0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF_u128, 'u128 into u128' ); - assert( - u256 { + assert_eq( + @u256 { low: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, high: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - }.into() == u256 { - high: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, low: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - }, + }.into(), + @u256 { high: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, low: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF }, 'u256 into u256' ); } @@ -1059,10 +1114,43 @@ fn panic_u128_u64_2() { #[test] fn test_u128_byte_reverse() { - assert( - integer::u128_byte_reverse( - 0x000102030405060708090a0b0c0d0e0f - ) == 0x0f0e0d0c0b0a09080706050403020100, + assert_eq( + @integer::u128_byte_reverse(0x000102030405060708090a0b0c0d0e0f), + @0x0f0e0d0c0b0a09080706050403020100, 'Wrong byte reverse' ); } + +#[test] +fn test_i8_operators() { + assert_eq(@1_i8, @1_i8, '1 == 1'); + assert_ne(@1_i8, @2_i8, '1 != 2'); +} + + +#[test] +fn test_i16_operators() { + assert_eq(@1_i16, @1_i16, '1 == 1'); + assert_ne(@1_i16, @2_i16, '1 != 2'); +} + + +#[test] +fn test_i32_operators() { + assert_eq(@1_i32, @1_i32, '1 == 1'); + assert_ne(@1_i32, @2_i32, '1 != 2'); +} + + +#[test] +fn test_i64_operators() { + assert_eq(@1_i64, @1_i64, '1 == 1'); + assert_ne(@1_i64, @2_i64, '1 != 2'); +} + + +#[test] +fn test_i128_operators() { + assert_eq(@1_i128, @1_i128, '1 == 1'); + assert_ne(@1_i128, @2_i128, '1 != 2'); +} diff --git a/corelib/src/test/keccak_test.cairo b/corelib/src/test/keccak_test.cairo index 09fc6d4..3a15357 100644 --- a/corelib/src/test/keccak_test.cairo +++ b/corelib/src/test/keccak_test.cairo @@ -1,32 +1,16 @@ +use core::clone::Clone; use array::ArrayTrait; use core::traits::Into; use starknet::SyscallResultTrait; +use test::test_utils::{assert_eq, assert_ne}; #[test] -#[available_gas(100000)] +#[available_gas(10000000)] fn test_keccak_syscall() { - let mut input = ArrayTrait::new(); - input.append(1); - input.append(2); - input.append(3); - input.append(4); - input.append(5); - input.append(6); - input.append(7); - input.append(8); - input.append(9); - input.append(10); - input.append(11); - input.append(12); - input.append(13); - input.append(14); - input.append(15); - input.append(16); - input.append(17); - assert( - starknet::syscalls::keccak_syscall(input.span()).unwrap_syscall() == u256 { - low: 0xec687be9c50d2218388da73622e8fdd5, high: 0xd2eb808dfba4703c528d145dfe6571af - }, + let input = array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; + assert_eq( + @starknet::syscalls::keccak_syscall(input.span()).unwrap_syscall(), + @u256 { low: 0xec687be9c50d2218388da73622e8fdd5, high: 0xd2eb808dfba4703c528d145dfe6571af }, 'Wrong hash value' ); } @@ -34,28 +18,195 @@ fn test_keccak_syscall() { #[test] #[available_gas(10000000)] fn test_keccak_hash() { - let mut input = ArrayTrait::new(); - input.append(u256 { low: 1, high: 0 }); + let res = keccak::keccak_u256s_le_inputs(array![1].span()); + assert_eq(@res.low, @0x587f7cc3722e9654ea3963d5fe8c0748, 'Wrong hash low 1'); + assert_eq(@res.high, @0xa5963aa610cb75ba273817bce5f8c48f, 'Wrong hash high 1'); + + let res = keccak::keccak_u256s_be_inputs(array![1].span()); + assert_eq(@res.low, @0x326a7e71fdcdee263b071276522d0eb1, 'Wrong hash low 2'); + assert_eq(@res.high, @0xf60cfab7e2cb9f2d73b0c2fa4a4bf40c, 'Wrong hash high 2'); + + let res = keccak::keccak_u256s_le_inputs(array![1, 2, 3, 4].span()); + assert_eq(@res.low, @0x845f8e9f5191367fb5181e74f6eb550d, 'Wrong hash low 3'); + assert_eq(@res.high, @0x17a2126cf7391a26b41c36a687090cc5, 'Wrong hash high 3'); + + let res = keccak::keccak_u256s_be_inputs(array![1, 2, 3, 4].span()); + assert_eq(@res.low, @0x6510e6fd534f267a01086462df912739, 'Wrong hash low 4'); + assert_eq(@res.high, @0x2d9982dfaf468a9ddf7101b6323aa9d5, 'Wrong hash high 4'); +} + +// Same input as in `test_keccak_hash` but as a u64 array. +#[test] +#[available_gas(10000000)] +fn test_keccak_u64() { + let mut input = array![ + 0x0000000000000001, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 + ]; + + let res = keccak::cairo_keccak(ref input, 0, 0); + + assert_eq(@res.low, @0x587f7cc3722e9654ea3963d5fe8c0748, 'Wrong hash low'); + assert_eq(@res.high, @0xa5963aa610cb75ba273817bce5f8c48f, 'Wrong hash high'); +} + +#[test] +#[available_gas(10000000)] +fn test_keccak_u64_full_block() { + let mut input = array![ + 0x0000000000000001, + 0x0000000000000002, + 0x0000000000000003, + 0x0000000000000004, + 0x0000000000000005, + 0x0000000000000006, + 0x0000000000000007, + 0x0000000000000008, + 0x0000000000000009, + 0x000000000000000a, + 0x000000000000000b, + 0x000000000000000c, + 0x000000000000000d, + 0x000000000000000e, + 0x000000000000000f, + 0x0000000000000010, + 0x0000000000000011 + ]; + + let res = keccak::cairo_keccak(ref input, 0, 0); + + assert_eq(@res.low, @0x5d291eebae35b254ff50ec1fc57832e8, 'Wrong hash low'); + assert_eq(@res.high, @0x210740d45b1fe2ac908a497ef45509f5, 'Wrong hash high'); +} + +#[test] +#[available_gas(10000000)] +fn test_keccak_u64_full_block_minus_byte() { + let mut input = array![ + 0x0000000000000001, + 0x0000000000000002, + 0x0000000000000003, + 0x0000000000000004, + 0x0000000000000005, + 0x0000000000000006, + 0x0000000000000007, + 0x0000000000000008, + 0x0000000000000009, + 0x000000000000000a, + 0x000000000000000b, + 0x000000000000000c, + 0x000000000000000d, + 0x000000000000000e, + 0x000000000000000f, + 0x0000000000000010, + ]; - let res = keccak::keccak_uint256s_le(input.span()); + // We must clone the array to be used in the second part, as it's modified by `cairo_keccak`. + let mut orig_array = input.clone(); - assert(res.low == 0x587f7cc3722e9654ea3963d5fe8c0748, 'Wrong hash value1'); - assert(res.high == 0xa5963aa610cb75ba273817bce5f8c48f, 'Wrong hash value2'); + let res = keccak::cairo_keccak(ref input, 0x12000000000011, 7); - let res = keccak::keccak_uint256s_be(input.span()); + assert_eq(@res.low, @0xbb968836d1704bea541a8d79dcc9067c, 'Wrong hash low 1'); + assert_eq(@res.high, @0xc98592786514c87f1a1a3d567b4dcd75, 'Wrong hash high 1'); - assert(res.low == 0x326a7e71fdcdee263b071276522d0eb1, 'Wrong hash value3'); - assert(res.high == 0xf60cfab7e2cb9f2d73b0c2fa4a4bf40c, 'Wrong hash value4'); + // With "garbage" at the end (note the `aa`), we should get the same result. + let res = keccak::cairo_keccak(ref orig_array, 0xaa12000000000011, 7); + + assert_eq(@res.low, @0xbb968836d1704bea541a8d79dcc9067c, 'Wrong hash low 2'); + assert_eq(@res.high, @0xc98592786514c87f1a1a3d567b4dcd75, 'Wrong hash high 2'); +} + +#[test] +#[available_gas(10000000)] +fn test_keccak_u64_full_block_minus_word() { + let mut input = array![ + 0x0000000000000001, + 0x0000000000000002, + 0x0000000000000003, + 0x0000000000000004, + 0x0000000000000005, + 0x0000000000000006, + 0x0000000000000007, + 0x0000000000000008, + 0x0000000000000009, + 0x000000000000000a, + 0x000000000000000b, + 0x000000000000000c, + 0x000000000000000d, + 0x000000000000000e, + 0x000000000000000f, + 0x0000000000000010, + ]; + + let res = keccak::cairo_keccak(ref input, 0, 0); + + assert_eq(@res.low, @0xfd9895001ee22e79b59c7997b3618a01, 'Wrong hash low'); + assert_eq(@res.high, @0x6851f2dbbfb3bfadff94db3ad476164f, 'Wrong hash high'); +} + +#[test] +#[available_gas(10000000)] +fn test_keccak_u64_full_block_minus_word_minus_byte() { + let mut input = array![ + 0x0000000000000001, + 0x0000000000000002, + 0x0000000000000003, + 0x0000000000000004, + 0x0000000000000005, + 0x0000000000000006, + 0x0000000000000007, + 0x0000000000000008, + 0x0000000000000009, + 0x000000000000000a, + 0x000000000000000b, + 0x000000000000000c, + 0x000000000000000d, + 0x000000000000000e, + 0x000000000000000f, + ]; + + // We must clone the array to be used in the second part, as it's modified by `cairo_keccak`. + let mut orig_array = input.clone(); + + let res = keccak::cairo_keccak(ref input, 0x11000000000010, 7); + + assert_eq(@res.low, @0x2cc6d33d8630a63c428d9cf38a89568b, 'Wrong hash low'); + assert_eq(@res.high, @0xdd7e11698dc8b37323c854a53abcd330, 'Wrong hash high'); + + // With "garbage" at the end (note the `aa`), we should get the same result. + let res = keccak::cairo_keccak(ref orig_array, 0xaa11000000000010, 7); + + assert_eq(@res.low, @0x2cc6d33d8630a63c428d9cf38a89568b, 'Wrong hash low 2'); + assert_eq(@res.high, @0xdd7e11698dc8b37323c854a53abcd330, 'Wrong hash high 2'); +} + +#[test] +#[available_gas(10000000)] +fn test_keccak_u64_full_block_minus_3_words_minus_4_bytes() { + let mut input = array![ + 0x0000000000000001, + 0x0000000000000002, + 0x0000000000000003, + 0x0000000000000004, + 0x0000000000000005, + 0x0000000000000006, + 0x0000000000000007, + 0x0000000000000008, + 0x0000000000000009, + 0x000000000000000a, + 0x000000000000000b, + 0x000000000000000c, + 0x000000000000000d + ]; - input.append(u256 { low: 2, high: 0 }); - input.append(u256 { low: 3, high: 0 }); - input.append(u256 { low: 4, high: 0 }); + // We must clone the array to be used in the second part, as it's modified by `cairo_keccak`. + let mut orig_array = input.clone(); - let res = keccak::keccak_uint256s_le(input.span()); - assert(res.low == 0x845f8e9f5191367fb5181e74f6eb550d, 'Wrong hash value5'); - assert(res.high == 0x17a2126cf7391a26b41c36a687090cc5, 'Wrong hash value6'); + let res = keccak::cairo_keccak(ref input, 0x11000010, 4); + assert_eq(@res.low, @0x43ccdbe17ae03b02b308ebe4a23c4cc9, 'Wrong hash low 1'); + assert_eq(@res.high, @0xf3cc56e9bd860f83e3e3bc69919b176a, 'Wrong hash high 1'); - let res = keccak::keccak_uint256s_be(input.span()); - assert(res.low == 0x6510e6fd534f267a01086462df912739, 'Wrong hash value7'); - assert(res.high == 0x2d9982dfaf468a9ddf7101b6323aa9d5, 'Wrong hash value8'); + // With "garbage" at the end (note the `aa`s), we should get the same result. + let res = keccak::cairo_keccak(ref orig_array, 0xaaaaaaaa11000010, 4); + assert_eq(@res.low, @0x43ccdbe17ae03b02b308ebe4a23c4cc9, 'Wrong hash low 2'); + assert_eq(@res.high, @0xf3cc56e9bd860f83e3e3bc69919b176a, 'Wrong hash high 2'); } diff --git a/corelib/src/test/math_test.cairo b/corelib/src/test/math_test.cairo new file mode 100644 index 0000000..e50748b --- /dev/null +++ b/corelib/src/test/math_test.cairo @@ -0,0 +1,98 @@ +use traits::TryInto; +use option::OptionTrait; + +#[test] +#[available_gas(10000000)] +fn test_egcd() { + let (g, s, t, sub_direction) = math::egcd(68_u8.try_into().unwrap(), 16_u8.try_into().unwrap()); + assert(g == 4, 'g != 4'); + assert(s == 1, 's != 1'); + assert(t == 4, 't != 4'); + assert(sub_direction, 'sub_direction is wrong'); + assert(1 * 68 - 4 * 16 == 4, 'Sanity check failed'); + + let (g, s, t, sub_direction) = math::egcd( + 240_u256.try_into().unwrap(), 46_u256.try_into().unwrap() + ); + assert(g == 2, 'g != 2'); + assert(s == 9, 's != 9'); + assert(t == 47, 't != 47'); + assert(!sub_direction, 'sub_direction is wrong'); + assert(47 * 46 - 9 * 240 == 2, 'Sanity check failed'); + + let (g, s, t, sub_direction) = math::egcd( + 50_u128.try_into().unwrap(), 17_u128.try_into().unwrap() + ); + assert(g == 1, 'g != 1'); + assert(s == 1, 's != 1'); + assert(t == 3, 't != 3'); + assert(!sub_direction, 'sub_direction is wrong'); + assert(3 * 17 - 1 * 50 == 1, 'Sanity check failed'); + + let (g, s, t, sub_direction) = math::egcd( + 5_u128.try_into().unwrap(), 15_u128.try_into().unwrap() + ); + assert(g == 5, 'g != 5'); + assert(s == 1, 's != 1'); + assert(t == 0, 't != 0'); + assert(sub_direction, 'sub_direction is wrong'); + assert(1 * 5 - 0 * 15 == 5, 'Sanity check failed'); + + let (g, s, t, sub_direction) = math::egcd( + 1_u128.try_into().unwrap(), 1_u128.try_into().unwrap() + ); + assert(g == 1, 'g != 1'); + assert(s == 0, 's != 0'); + assert(t == 1, 't != 1'); + assert(!sub_direction, 'sub_direction is wrong'); + assert(1 * 1 - 0 * 1 == 1, 'Sanity check failed'); +} + +#[test] +#[available_gas(10000000)] +fn test_inv_mod() { + let inv = math::inv_mod(5_u256.try_into().unwrap(), 24_u256.try_into().unwrap()).unwrap(); + assert(inv == 5, 'inv != 5'); + + let inv = math::inv_mod(29_u128.try_into().unwrap(), 24_u128.try_into().unwrap()).unwrap(); + assert(inv == 5, 'inv != 5'); + + let inv = math::inv_mod(1_u16.try_into().unwrap(), 24_u16.try_into().unwrap()).unwrap(); + assert(inv == 1, 'inv != 1'); + + let inv = math::inv_mod(1_u32.try_into().unwrap(), 5_u32.try_into().unwrap()).unwrap(); + assert(inv == 1, 'inv != 1'); + + let inv = math::inv_mod(8_usize.try_into().unwrap(), 24_usize.try_into().unwrap()); + assert(inv.is_none(), 'inv should be None'); + + let inv = math::inv_mod(1_usize.try_into().unwrap(), 1_usize.try_into().unwrap()).unwrap(); + assert(inv == 0, 'inv != 0'); + + let inv = math::inv_mod(7_usize.try_into().unwrap(), 1_usize.try_into().unwrap()).unwrap(); + assert(inv == 0, 'inv != 0'); +} + +#[test] +#[available_gas(10000000)] +fn test_u256_div_mod_n() { + let q = math::u256_div_mod_n(6_u256, 2_u256.try_into().unwrap(), 7_u256.try_into().unwrap()) + .unwrap(); + assert(q == 3, '6 / 2 != 3 (7)'); + + let q = math::u256_div_mod_n(5_u256, 1_u256.try_into().unwrap(), 7_u256.try_into().unwrap()) + .unwrap(); + assert(q == 5, '5 / 1 != 5 (7)'); + + let q = math::u256_div_mod_n(1_u256, 1_u256.try_into().unwrap(), 7_u256.try_into().unwrap()) + .unwrap(); + assert(q == 1, '1 / 1 != 1 (7)'); + + let q = math::u256_div_mod_n(7_u256, 2_u256.try_into().unwrap(), 13_u256.try_into().unwrap()) + .unwrap(); + assert(q == 10, '7 / 2 != 10 (13)'); + + let q = math::u256_div_mod_n(0_u256, 3_u256.try_into().unwrap(), 13_u256.try_into().unwrap()) + .unwrap(); + assert(q == 0, '0 / 3 != 0 (13)'); +} diff --git a/corelib/src/test/plugins_test.cairo b/corelib/src/test/plugins_test.cairo index f50d6e3..78f26f1 100644 --- a/corelib/src/test/plugins_test.cairo +++ b/corelib/src/test/plugins_test.cairo @@ -2,10 +2,11 @@ use array::ArrayTrait; use array::SpanTrait; use serde::Serde; use option::OptionTrait; +use test::test_utils::{assert_eq, assert_ne}; #[derive(Copy, Drop, Serde, PartialEq)] enum EnumForSerde { - A: (), + A, B: u32, C: u64, } @@ -15,31 +16,36 @@ fn test_derive_serde_enum() { let a = EnumForSerde::A(()); let b = EnumForSerde::B(1); let c = EnumForSerde::C(2); - let mut output = ArrayTrait::new(); + let mut output = Default::default(); a.serialize(ref output); a.serialize(ref output); c.serialize(ref output); b.serialize(ref output); a.serialize(ref output); let mut serialized = output.span(); - assert( - Serde::::deserialize(ref serialized).expect('failed to read') == a, + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @a, 'expected a' ); - assert( - Serde::::deserialize(ref serialized).expect('failed to read') == a, + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @a, 'expected a' ); - assert( - Serde::::deserialize(ref serialized).expect('failed to read') == c, + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @c, 'expected c' ); - assert( - Serde::::deserialize(ref serialized).expect('failed to read') == b, + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @b, 'expected b' ); - assert( - Serde::::deserialize(ref serialized).expect('failed to read') == a, + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @a, 'expected a' ); assert(serialized.is_empty(), 'expected empty'); diff --git a/corelib/src/test/secp256k1_test.cairo b/corelib/src/test/secp256k1_test.cairo new file mode 100644 index 0000000..7dfa501 --- /dev/null +++ b/corelib/src/test/secp256k1_test.cairo @@ -0,0 +1,115 @@ +use starknet::{ + eth_address::U256IntoEthAddress, EthAddress, secp256k1::Secp256k1Impl, SyscallResultTrait +}; +use option::OptionTrait; +use traits::{Into, TryInto}; +use starknet::secp256_trait::{ + Signature, recover_public_key, verify_eth_signature, Secp256PointTrait, signature_from_vrs +}; +use starknet::secp256k1::{Secp256k1Point, Secp256k1PointImpl}; + +#[test] +#[available_gas(100000000)] +fn test_secp256k1_recover_public_key() { + let y_parity = true; + let (msg_hash, signature, expected_public_key_x, expected_public_key_y, _) = + get_message_and_signature( + :y_parity + ); + let public_key = recover_public_key::(msg_hash, signature).unwrap(); + let (x, y) = public_key.get_coordinates().unwrap_syscall(); + assert(expected_public_key_x == x, 'recover failed 1'); + assert(expected_public_key_y == y, 'recover failed 2'); + + let y_parity = false; + let (msg_hash, signature, expected_public_key_x, expected_public_key_y, _) = + get_message_and_signature( + :y_parity + ); + let public_key = recover_public_key::(msg_hash, signature).unwrap(); + let (x, y) = public_key.get_coordinates().unwrap_syscall(); + assert(expected_public_key_x == x, 'recover failed 3'); + assert(expected_public_key_y == y, 'recover failed 4'); +} + +#[test] +fn test_signature_from_vrs() { + let v = 18; + let r = 1; + let s = 2; + let signature = signature_from_vrs(v, r, s); + + assert(signature == Signature { r, s, y_parity: false }, 'Wrong result'); +} + +/// Returns a golden valid message hash and its signature, for testing. +fn get_message_and_signature(y_parity: bool) -> (u256, Signature, u256, u256, EthAddress) { + let msg_hash = 0xe888fbb4cf9ae6254f19ba12e6d9af54788f195a6f509ca3e934f78d7a71dd85; + let r = 0x4c8e4fbc1fbb1dece52185e532812c4f7a5f81cf3ee10044320a0d03b62d3e9a; + let s = 0x4ac5e5c0c0e8a4871583cc131f35fb49c2b7f60e6a8b84965830658f08f7410c; + + let (public_key_x, public_key_y) = if y_parity { + ( + 0xa9a02d48081294b9bb0d8740d70d3607feb20876964d432846d9b9100b91eefd, + 0x18b410b5523a1431024a6ab766c89fa5d062744c75e49efb9925bf8025a7c09e + ) + } else { + ( + 0x57a910a2a58ef7d57f452e1f6ea7ee0080789091de946b0ca6e5c6af2c8ff5c8, + 0x249d233d0d21f35db55ce852edbd340d31e92ea4d591886149ca5d89911331ac + ) + }; + let eth_address = 0x767410c1bb448978bd42b984d7de5970bcaf5c43_u256.into(); + + (msg_hash, Signature { r, s, y_parity }, public_key_x, public_key_y, eth_address) +} + +#[test] +#[available_gas(100000000)] +fn test_verify_eth_signature() { + let y_parity = true; + let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature( + :y_parity + ); + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} + +#[test] +#[should_panic(expected: ('Invalid signature', ))] +#[available_gas(100000000)] +fn test_verify_eth_signature_wrong_eth_address() { + let y_parity = true; + let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature( + :y_parity + ); + let eth_address = (eth_address.into() + 1).try_into().unwrap(); + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} + +#[test] +#[should_panic(expected: ('Signature out of range', ))] +#[available_gas(100000000)] +fn test_verify_eth_signature_overflowing_signature_r() { + let y_parity = true; + let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature( + :y_parity + ); + signature.r = Secp256k1Impl::get_curve_size() + 1; + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} + +#[test] +#[should_panic(expected: ('Signature out of range', ))] +#[available_gas(100000000)] +fn test_verify_eth_signature_overflowing_signature_s() { + let y_parity = true; + let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature( + :y_parity + ); + signature.s = Secp256k1Impl::get_curve_size() + 1; + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} diff --git a/corelib/src/test/secp256r1_test.cairo b/corelib/src/test/secp256r1_test.cairo new file mode 100644 index 0000000..6dc8966 --- /dev/null +++ b/corelib/src/test/secp256r1_test.cairo @@ -0,0 +1,95 @@ +use starknet::{ + eth_address::U256IntoEthAddress, EthAddress, secp256r1::Secp256r1Impl, SyscallResultTrait +}; +use option::OptionTrait; +use traits::{Into, TryInto}; +use starknet::secp256_trait::{ + recover_public_key, verify_eth_signature, Secp256PointTrait, Signature +}; +use starknet::secp256r1::{Secp256r1Point, Secp256r1PointImpl}; +use test::test_utils::assert_eq; + +#[test] +#[available_gas(100000000)] +fn test_secp256r1_recover_public_key() { + let (msg_hash, signature, expected_public_key_x, expected_public_key_y, _) = + get_message_and_signature(); + let public_key = recover_public_key::(msg_hash, signature).unwrap(); + let (x, y) = public_key.get_coordinates().unwrap_syscall(); + assert(expected_public_key_x == x, 'recover failed 1'); + assert(expected_public_key_y == y, 'recover failed 2'); +} + + +/// Returns a golden valid message hash and its signature, for testing. +fn get_message_and_signature() -> (u256, Signature, u256, u256, EthAddress) { + // msg = "" + // public key: (0x04aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad5, + // 0x0087d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d) + let msg_hash = 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855; + let r = 0xb292a619339f6e567a305c951c0dcbcc42d16e47f219f9e98e76e09d8770b34a; + let s = 0x177e60492c5a8242f76f07bfe3661bde59ec2a17ce5bd2dab2abebdf89a62e2; + + let (public_key_x, public_key_y) = ( + 0x04aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad5, + 0x0087d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d + ); + let eth_address = 0x492882426e1cda979008bfaf874ff796eb3bb1c0_u256.into(); + + (msg_hash, Signature { r, s, y_parity: true }, public_key_x, public_key_y, eth_address) +} + +#[test] +#[available_gas(100000000)] +fn test_verify_eth_signature() { + let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature(); + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} + +#[test] +#[should_panic(expected: ('Invalid signature', ))] +#[available_gas(100000000)] +fn test_verify_eth_signature_wrong_eth_address() { + let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature(); + let eth_address = (eth_address.into() + 1).try_into().unwrap(); + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} + +#[test] +#[should_panic(expected: ('Signature out of range', ))] +#[available_gas(100000000)] +fn test_verify_eth_signature_overflowing_signature_r() { + let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature(); + signature.r = Secp256r1Impl::get_curve_size() + 1; + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} + +#[test] +#[should_panic(expected: ('Signature out of range', ))] +#[available_gas(100000000)] +fn test_verify_eth_signature_overflowing_signature_s() { + let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = + get_message_and_signature(); + signature.s = Secp256r1Impl::get_curve_size() + 1; + verify_eth_signature::(:msg_hash, :signature, :eth_address); +} + + +#[test] +#[available_gas(100_000_000)] +fn test_recover_public_key_y_even() { + let x: u256 = 0x502a43ce77c6f5c736a82f847fa95f8c2d483fe223b12b91047d83258a958b0f; + let y: u256 = 0xdb0a2e6710c71ba80afeb3abdf69d306ce729c7704f4ddf2eaaf0b76209fe1b0; + let r: u256 = 0x7380df4a623c5c2259a5e5f5b225d7265a9e24b3a13c101d1afddcf29e3cf8b2; + let s: u256 = 0x0d131afacdd17a4ea1b544bb3ade677ff8accbe7830e15b9c225e6031155946a; + let y_parity = false; + let message_hash: u256 = 0x28c7fff9aef4847a82cd64280434712a5b49205831b60eea6e70614077e672eb; + let recovered = recover_public_key::(message_hash, Signature { r, s, y_parity }) + .unwrap(); + let (recovered_x, recovered_y) = recovered.get_coordinates().unwrap_syscall(); + + assert_eq(@recovered_x, @x, 'Signature is not valid'); +} diff --git a/corelib/src/test/test_utils.cairo b/corelib/src/test/test_utils.cairo new file mode 100644 index 0000000..bd001de --- /dev/null +++ b/corelib/src/test/test_utils.cairo @@ -0,0 +1,29 @@ +#[inline] +fn assert_eq>(a: @T, b: @T, err_code: felt252) { + assert(a == b, err_code); +} + +#[inline] +fn assert_ne>(a: @T, b: @T, err_code: felt252) { + assert(a != b, err_code); +} + +#[inline] +fn assert_le>(a: T, b: T, err_code: felt252) { + assert(a <= b, err_code); +} + +#[inline] +fn assert_lt>(a: T, b: T, err_code: felt252) { + assert(a < b, err_code); +} + +#[inline] +fn assert_ge>(a: T, b: T, err_code: felt252) { + assert(a >= b, err_code); +} + +#[inline] +fn assert_gt>(a: T, b: T, err_code: felt252) { + assert(a > b, err_code); +} diff --git a/corelib/src/test/testing_test.cairo b/corelib/src/test/testing_test.cairo index 117331b..4c95bda 100644 --- a/corelib/src/test/testing_test.cairo +++ b/corelib/src/test/testing_test.cairo @@ -1,3 +1,5 @@ +use test::test_utils::{assert_eq, assert_ne, assert_gt}; + #[test] #[should_panic(expected: ('panic_with_felt252()', ))] fn test_panic_with_felt252() { @@ -18,11 +20,11 @@ fn test_assert_true() { #[test] fn test_get_available_gas_no_gas_supply() { - assert(testing::get_available_gas() == 0, 'expected no_gas_supply') + assert_eq(@testing::get_available_gas(), @0, 'expected no_gas_supply') } #[test] #[available_gas(10000)] fn test_get_available_gas_with_gas_supply() { - assert(testing::get_available_gas() > 5000, 'high amount of gas used') + assert_gt(testing::get_available_gas(), 5000, 'high amount of gas used') } diff --git a/corelib/src/traits.cairo b/corelib/src/traits.cairo index 7e07f68..b0d264e 100644 --- a/corelib/src/traits.cairo +++ b/corelib/src/traits.cairo @@ -1,3 +1,5 @@ +use core::panics::Panic; + trait Copy; trait Drop; @@ -51,8 +53,16 @@ trait DivRem { } trait PartialEq { - fn eq(lhs: T, rhs: T) -> bool; - fn ne(lhs: T, rhs: T) -> bool; + fn eq(lhs: @T, rhs: @T) -> bool; + fn ne(lhs: @T, rhs: @T) -> bool; +} +impl PartialEqSnap> of PartialEq<@T> { + fn eq(lhs: @@T, rhs: @@T) -> bool { + TEq::eq(*lhs, *rhs) + } + fn ne(lhs: @@T, rhs: @@T) -> bool { + TEq::ne(*lhs, *rhs) + } } // TODO(spapini): When associated types are supported, support the general trait BitAnd. @@ -119,17 +129,33 @@ trait Index { trait Destruct { fn destruct(self: T) nopanic; } - // TODO(spapini): Remove this, it can lead to multiple impls and unwanted Destruct implementation. impl DestructFromDrop> of Destruct { #[inline(always)] fn destruct(self: T) nopanic {} } +trait PanicDestruct { + fn panic_destruct(self: T, ref panic: Panic) nopanic; +} +impl PanicDestructForDestruct> of PanicDestruct { + #[inline(always)] + fn panic_destruct(self: T, ref panic: Panic) nopanic { + TDestruct::destruct(self); + } +} + trait Default { fn default() -> T; } +impl SnapshotDefault, impl TDrop: Drop> of Default<@T> { + #[inline(always)] + fn default() -> @T { + @Default::default() + } +} + /// Trait for types allowed as values in a Felt252Dict. trait Felt252DictValue { /// Returns the default value for this type as a value in a Felt252Dict. @@ -184,24 +210,81 @@ impl TupleSize4Drop< // Tuple PartialEq impls. impl TupleSize0PartialEq of PartialEq<()> { #[inline(always)] - fn eq(lhs: (), rhs: ()) -> bool { + fn eq(lhs: @(), rhs: @()) -> bool { true } #[inline(always)] - fn ne(lhs: (), rhs: ()) -> bool { + fn ne(lhs: @(), rhs: @()) -> bool { false } } impl TupleSize1PartialEq> of PartialEq<(E0, )> { #[inline(always)] - fn eq(lhs: (E0, ), rhs: (E0, )) -> bool { + fn eq(lhs: @(E0, ), rhs: @(E0, )) -> bool { let (lhs, ) = lhs; let (rhs, ) = rhs; lhs == rhs } #[inline(always)] - fn ne(lhs: (E0, ), rhs: (E0, )) -> bool { + fn ne(lhs: @(E0, ), rhs: @(E0, )) -> bool { + !(rhs == lhs) + } +} + +impl TupleSize2PartialEq< + E0, E1, impl E0PartialEq: PartialEq, impl E1PartialEq: PartialEq +> of PartialEq<(E0, E1)> { + #[inline(always)] + fn eq(lhs: @(E0, E1), rhs: @(E0, E1)) -> bool { + let (lhs0, lhs1) = lhs; + let (rhs0, rhs1) = rhs; + lhs0 == rhs0 && lhs1 == rhs1 + } + #[inline(always)] + fn ne(lhs: @(E0, E1), rhs: @(E0, E1)) -> bool { + !(rhs == lhs) + } +} + +impl TupleSize3PartialEq< + E0, + E1, + E2, + impl E0PartialEq: PartialEq, + impl E1PartialEq: PartialEq, + impl E2PartialEq: PartialEq +> of PartialEq<(E0, E1, E2)> { + #[inline(always)] + fn eq(lhs: @(E0, E1, E2), rhs: @(E0, E1, E2)) -> bool { + let (lhs0, lhs1, lhs2) = lhs; + let (rhs0, rhs1, rhs2) = rhs; + lhs0 == rhs0 && lhs1 == rhs1 && lhs2 == rhs2 + } + #[inline(always)] + fn ne(lhs: @(E0, E1, E2), rhs: @(E0, E1, E2)) -> bool { + !(rhs == lhs) + } +} + +impl TupleSize4PartialEq< + E0, + E1, + E2, + E3, + impl E0PartialEq: PartialEq, + impl E1PartialEq: PartialEq, + impl E2PartialEq: PartialEq, + impl E3PartialEq: PartialEq +> of PartialEq<(E0, E1, E2, E3)> { + #[inline(always)] + fn eq(lhs: @(E0, E1, E2, E3), rhs: @(E0, E1, E2, E3)) -> bool { + let (lhs0, lhs1, lhs2, lhs3) = lhs; + let (rhs0, rhs1, rhs2, rhs3) = rhs; + lhs0 == rhs0 && lhs1 == rhs1 && lhs2 == rhs2 && lhs3 == rhs3 + } + #[inline(always)] + fn ne(lhs: @(E0, E1, E2, E3), rhs: @(E0, E1, E2, E3)) -> bool { !(rhs == lhs) } } diff --git a/corelib/src/zeroable.cairo b/corelib/src/zeroable.cairo index 9f78eae..6dc32b8 100644 --- a/corelib/src/zeroable.cairo +++ b/corelib/src/zeroable.cairo @@ -29,7 +29,7 @@ extern type NonZero; impl NonZeroTCopy> of Copy>; impl NonZeroTDrop> of Drop>; enum IsZeroResult { - Zero: (), + Zero, NonZero: NonZero, } extern fn unwrap_non_zero(a: NonZero) -> T nopanic; @@ -43,7 +43,7 @@ impl NonZeroIntoImpl of Into, T> { impl IsZeroResultIntoBool> of Into, bool> { fn into(self: IsZeroResult) -> bool { match self { - IsZeroResult::Zero(()) => true, + IsZeroResult::Zero => true, IsZeroResult::NonZero(_) => false, } } diff --git a/src/compilation/standard.rs b/src/compilation/standard.rs index d97dcee..d15f743 100644 --- a/src/compilation/standard.rs +++ b/src/compilation/standard.rs @@ -1,5 +1,4 @@ use anyhow::{bail, Context, Result}; -use std::collections::HashMap; use std::env; use std::sync::Arc; @@ -10,15 +9,16 @@ use cairo_lang_filesystem::ids::Directory; use cairo_lang_sierra_generator::db::SierraGenGroup; use cairo_lang_sierra_generator::replace_ids::replace_sierra_ids_in_program; use cairo_lang_starknet::abi::{AbiBuilder, Contract}; -use cairo_lang_starknet::contract::{find_contracts, get_abi}; +use cairo_lang_starknet::contract::find_contracts; use cairo_lang_starknet::plugin::StarkNetPlugin; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use super::ProgramCompiled; use crate::core::core_unit::CoreOpts; pub fn compile(opts: CoreOpts) -> Result> { // NOTE: compiler_version module is not public so we need to update it as we update the Cairo version we use - println!("Compiling with cairo-lang-starknet 1.1.1"); + println!("Compiling with starknet-compile 2.1.0."); // corelib cli option has priority over the environment variable let corelib = match opts.corelib { @@ -36,7 +36,7 @@ pub fn compile(opts: CoreOpts) -> Result> { corelib: Some(Directory(corelib)), base_path: "".into(), content: ProjectConfigContent { - crate_roots: HashMap::new(), + crate_roots: OrderedHashMap::default(), }, }; @@ -58,8 +58,8 @@ pub fn compile(opts: CoreOpts) -> Result> { let mut abi: Contract = Default::default(); contracts.iter().for_each(|c| { abi.items.extend( - AbiBuilder::from_trait(&db, get_abi(&db, c).expect("Error when getting the ABI.")) - .unwrap() + AbiBuilder::submodule_as_contract_abi(&db, c.submodule_id) + .expect("Error when getting the ABI.") .items, ) }); diff --git a/src/core/compilation_unit.rs b/src/core/compilation_unit.rs index c5c1b1f..33c3750 100644 --- a/src/core/compilation_unit.rs +++ b/src/core/compilation_unit.rs @@ -9,10 +9,7 @@ use cairo_lang_sierra::program::{ Function as SierraFunction, GenStatement, Program, Statement as SierraStatement, }; use cairo_lang_sierra::program_registry::ProgramRegistry; -use cairo_lang_starknet::abi::{ - Contract, - Item::{Event, Function as AbiFunction}, -}; +use cairo_lang_starknet::abi::{Contract, Item::Function as AbiFunction}; pub struct CompilationUnit { /// The compiled sierra program @@ -67,6 +64,18 @@ impl CompilationUnit { self.functions.iter().find(|f| f.name().as_str() == name) } + /// Returns all events name + pub fn all_events_name(&self) -> impl Iterator + '_ { + self.functions + .iter() + .filter(|f| { + f.name().ends_with("::append_keys_and_data") + && !f.name().contains("::EventIsEvent::") // EventIsEvent represents the enum Event that contains the events so we discard it + }) + // We discard IsEvent to have only the events' name e.g. MyEventIsEvent -> MyEvent + .map(|event| event.name().rsplit_once("IsEvent::").unwrap().0.to_owned()) + } + pub fn registry(&self) -> &ProgramRegistry { &self.registry } @@ -104,7 +113,11 @@ impl CompilationUnit { } fn append_function(&mut self, data: SierraFunction, statements: Vec) { - self.functions.push(Function::new(data, statements)); + // The compiler adds unsafe_new_contract_state which holds the storage variables + // for now we don't consider it + if !data.id.to_string().ends_with("::unsafe_new_contract_state") { + self.functions.push(Function::new(data, statements)); + } } fn set_functions_type(&mut self) { @@ -128,7 +141,8 @@ impl CompilationUnit { for f in self.functions.iter_mut() { let full_name = f.name(); - if full_name.starts_with("core::") { + // append_keys_and_data is a function implemented by the starknet::Event trait + if full_name.starts_with("core::") || full_name.ends_with("::append_keys_and_data") { f.set_ty(Type::Core); } else if full_name.contains("::__external::") || full_name.contains("::__constructor::") @@ -160,9 +174,9 @@ impl CompilationUnit { } } else if l1_handler_functions.contains(&full_name) { f.set_ty(Type::L1Handler); - } else if full_name.ends_with("::address") - || full_name.ends_with("::read") - || full_name.ends_with("::write") + } else if full_name.ends_with("::InternalContractStateImpl::address") + || full_name.ends_with("::InternalContractStateImpl::read") + || full_name.ends_with("::InternalContractStateImpl::write") { // A user defined function named address/read/write can be incorrectly set to Storage f.set_ty(Type::Storage); @@ -174,26 +188,13 @@ impl CompilationUnit { f.set_ty(Type::AbiCallContract) } else { // Event or private function - // Could be an event a loop function or a private function in the contract's module - if full_name.ends_with(']') { + // Could be an event emission or a private function in the contract's module + if full_name.contains("::ContractStateEventEmitter::emit::") { + f.set_ty(Type::Event); + } else if full_name.ends_with(']') { f.set_ty(Type::Loop); } else { - let possible_event_name = full_name.rsplit_once("::").unwrap().1; - - let mut found = false; - for item in self.abi.items.iter() { - if let Event(e) = item { - if e.name == possible_event_name { - f.set_ty(Type::Event); - found = true; - break; - } - } - } - - if !found { - f.set_ty(Type::Private); - } + f.set_ty(Type::Private); } } } diff --git a/src/core/function.rs b/src/core/function.rs index 54cd029..ace3524 100644 --- a/src/core/function.rs +++ b/src/core/function.rs @@ -32,7 +32,13 @@ pub enum Type { Private, /// Constructor function defined by the user Constructor, - /// Event function + /// Event function. These are the events emitted. + /// If the function name is ...::ContractStateEventEmitter::emit::> + /// this doesn't explicitly mention the event emitted but rather the generic ::Event + /// it happens when the more verbose syntax is used e.g. self.emit(Event::MyUsedEvent(MyUsedEvent { value: amount })); + /// While when the other syntax is used it's possible to infer which event has been emitted + /// e.g. self.emit(MyUsedEvent { value: amount }); + /// ContractStateEventEmitter::emit:: Event, /// Function made by the compiler for storage variables /// typically address, read, write diff --git a/src/detectors/mod.rs b/src/detectors/mod.rs index cf724f2..f299a0f 100644 --- a/src/detectors/mod.rs +++ b/src/detectors/mod.rs @@ -3,11 +3,11 @@ use self::detector::Detector; pub mod controlled_library_call; pub mod dead_code; pub mod detector; +pub mod read_only_reentrancy; pub mod reentrancy; pub mod reentrancy_benign; pub mod reentrancy_events; pub mod unchecked_l1_handler_from; -pub mod unenforced_view; pub mod unused_arguments; pub mod unused_events; pub mod unused_return; @@ -19,10 +19,10 @@ pub fn get_detectors() -> Vec> { Box::::default(), Box::::default(), Box::::default(), - Box::::default(), Box::::default(), Box::::default(), Box::::default(), + Box::::default(), Box::::default(), ] } diff --git a/src/detectors/read_only_reentrancy.rs b/src/detectors/read_only_reentrancy.rs new file mode 100644 index 0000000..92e8d1a --- /dev/null +++ b/src/detectors/read_only_reentrancy.rs @@ -0,0 +1,109 @@ +use super::detector::{Confidence, Detector, Impact, Result}; +use crate::analysis::dataflow::AnalysisState; +use crate::analysis::reentrancy::ReentrancyDomain; +use crate::core::core_unit::CoreUnit; +use crate::core::function::Type; +use std::collections::HashMap; +use std::collections::HashSet; + +#[derive(Default)] +pub struct ReadOnlyReentrancy; + +impl Detector for ReadOnlyReentrancy { + fn name(&self) -> &str { + "read-only-reentrancy" + } + + fn description(&self) -> &str { + "Detect when a view function read a storage variable written after an external call" + } + + fn confidence(&self) -> Confidence { + Confidence::Medium + } + + fn impact(&self) -> Impact { + Impact::Medium + } + + fn run(&self, core: &CoreUnit) -> Vec { + let mut results: HashSet = HashSet::new(); + let compilation_units = core.get_compilation_units(); + + for compilation_unit in compilation_units { + // Key the storage variable read - Value the functions name where it's read + let mut vars_read: HashMap> = HashMap::new(); + + for f in compilation_unit + .functions_user_defined() + .filter(|f| f.ty() == &Type::View) + { + for storage_var_read in f.storage_vars_read() { + let var_read = storage_var_read + .to_string() + .rsplit_once("::") + .unwrap() + .0 + .to_string(); + let functions_name = vars_read.entry(var_read).or_insert(HashSet::new()); + functions_name.insert(f.name()); + } + } + + for f in compilation_unit.functions_user_defined() { + for bb_info in f.analyses().reentrancy.iter() { + if let AnalysisState { + post: ReentrancyDomain::State(reentrancy_info), + .. + } = bb_info.1 + { + for call in reentrancy_info.external_calls.iter() { + for written_variable in reentrancy_info.storage_variables_written.iter() + { + let written_variable_name = written_variable + .get_storage_variable_written() + .as_ref() + .unwrap() + .get_statement() + .to_string() + .rsplit_once("::") + .unwrap() + .0 + .to_string(); + + if vars_read.contains_key(&written_variable_name) { + for view_function in + vars_read.get(&written_variable_name).unwrap() + { + results.insert(Result { + name: self.name().to_string(), + impact: self.impact(), + confidence: self.confidence(), + message: format!( + "Read only reentrancy in {}\n\tExternal call {} done in {}\n\tVariable written after {} in {}", + view_function, + call.get_external_call() + .as_ref() + .unwrap() + .get_statement(), + call.get_function(), + written_variable + .get_storage_variable_written() + .as_ref() + .unwrap() + .get_statement(), + written_variable.get_function(), + ), + }); + } + } + } + } + } + } + } + } + + Vec::from_iter(results) + } +} diff --git a/src/detectors/unchecked_l1_handler_from.rs b/src/detectors/unchecked_l1_handler_from.rs index a056a56..b335798 100644 --- a/src/detectors/unchecked_l1_handler_from.rs +++ b/src/detectors/unchecked_l1_handler_from.rs @@ -41,7 +41,7 @@ impl Detector for UncheckedL1HandlerFrom { for f in l1_handler_funcs { let from_address = - f.params().map(|p| p.id.clone()).collect::>()[0].clone(); + f.params().map(|p| p.id.clone()).collect::>()[1].clone(); let mut sources = HashSet::new(); sources.insert(WrapperVariable::new(f.name(), from_address)); diff --git a/src/detectors/unenforced_view.rs b/src/detectors/unenforced_view.rs deleted file mode 100644 index 02fc60a..0000000 --- a/src/detectors/unenforced_view.rs +++ /dev/null @@ -1,131 +0,0 @@ -use super::detector::{Confidence, Detector, Impact, Result}; -use crate::core::compilation_unit::CompilationUnit; -use crate::core::core_unit::CoreUnit; -use crate::core::function::Type; -use cairo_lang_sierra::extensions::core::CoreConcreteLibfunc; -use cairo_lang_sierra::program::Statement as SierraStatement; -use std::collections::HashSet; - -#[derive(Default)] -pub struct UnenforcedView {} - -impl Detector for UnenforcedView { - fn name(&self) -> &str { - "unenforced-view" - } - fn description(&self) -> &str { - "function has view decorator but modifies state" - } - fn confidence(&self) -> Confidence { - Confidence::Medium - } - fn impact(&self) -> Impact { - Impact::Medium - } - fn run(&self, core: &CoreUnit) -> Vec { - let mut results = Vec::new(); - let compilation_units = core.get_compilation_units(); - - for compilation_unit in compilation_units { - let view_funcs: Vec<_> = compilation_unit - .functions() - .filter(|f| *f.ty() == Type::View) - .collect(); - - for func in view_funcs { - let mut tracked_fns: HashSet = HashSet::new(); - let func_name = func.name(); - let (declaration, name) = func_name.rsplit_once("::").unwrap(); - if func.storage_vars_written().count() > 0 || func.events_emitted().count() > 0 { - results.push(Result { - name: self.name().to_string(), - impact: self.impact(), - confidence: self.confidence(), - message: format!( - "{} defined in {} is declared as view but changes state", - name, declaration - ), - }); - } - let subcalls = func.private_functions_calls().collect(); - self.check_view_subcalls( - compilation_unit, - declaration, - name, - &mut results, - subcalls, - &mut tracked_fns, - ) - } - } - results - } -} -impl UnenforcedView { - fn check_view_subcalls( - &self, - compilation_unit: &CompilationUnit, - declaration: &str, - name: &str, - results: &mut Vec, - subcalls: Vec<&SierraStatement>, - tracked_fns: &mut HashSet, - ) { - if subcalls.is_empty() { - return; - } - - for call in subcalls { - // do lookup - if let SierraStatement::Invocation(invoc) = call { - let libfunc = compilation_unit - .registry() - .get_libfunc(&invoc.libfunc_id) - .expect("Library not found in core registry"); - if let CoreConcreteLibfunc::FunctionCall(f_called) = libfunc { - let func_name = &f_called - .function - .id - .debug_name - .as_ref() - .unwrap() - .to_string(); - let called_fn = compilation_unit - .functions_user_defined() - .find(|f| &f.name() == func_name) - .unwrap(); - // if we already analyzed this function we continue - if tracked_fns.contains(func_name) { - continue; - } - // if lookup writes to storage push result using original view function + declaration - if called_fn.storage_vars_written().count() > 0 - || called_fn.events_emitted().count() > 0 - { - results.push(Result { - name: self.name().to_string(), - impact: self.impact(), - confidence: self.confidence(), - message: format!( - "{} defined in {} is declared as view but changes state", - name, declaration - ), - }); - } - // mark this function in the case of recursive calls - tracked_fns.insert(func_name.clone()); - // for now we just check over the private calls, even though the compiler doesn't validate that an external call can be called from within the contract - let subcalls_to_check = called_fn.private_functions_calls().collect(); - self.check_view_subcalls( - compilation_unit, - declaration, - name, - results, - subcalls_to_check, - tracked_fns, - ); - } - } - } - } -} diff --git a/src/detectors/unused_arguments.rs b/src/detectors/unused_arguments.rs index d52d659..fb2c520 100644 --- a/src/detectors/unused_arguments.rs +++ b/src/detectors/unused_arguments.rs @@ -45,17 +45,28 @@ impl Detector for UnusedArguments { // If an argument is unused there is a Drop as the first instruction // When we don't have any more Drop instructions we are sure the others are used - if let CoreConcreteLibfunc::Drop(_) = libfunc { - results.push(Result { - name: self.name().to_string(), - impact: self.impact(), - confidence: self.confidence(), - message: format!( - "The {} argument in {} is never used", - number_to_ordinal(invoc.args[0].id - offset as u64 + 1), - f.name() - ), - }) + if let CoreConcreteLibfunc::Drop(drop_libfunc) = libfunc { + // We don't report if self (the first argument) is unused + // NOTE: as of now the compiler allows to use a ContractState argument everywhere + if !drop_libfunc.signature.param_signatures[0] + .ty + .debug_name + .as_ref() + .unwrap() + .as_str() + .ends_with("::ContractState") + { + results.push(Result { + name: self.name().to_string(), + impact: self.impact(), + confidence: self.confidence(), + message: format!( + "The {} argument in {} is never used", + number_to_ordinal(invoc.args[0].id - offset as u64 + 1), + f.name() + ), + }) + } } else { break; } diff --git a/src/detectors/unused_events.rs b/src/detectors/unused_events.rs index fc100d0..d3674d9 100644 --- a/src/detectors/unused_events.rs +++ b/src/detectors/unused_events.rs @@ -1,10 +1,13 @@ use super::detector::{Confidence, Detector, Impact, Result}; use crate::core::core_unit::CoreUnit; -use crate::core::function::Type; +use crate::utils::filter_builtins_from_signature; use cairo_lang_sierra::extensions::core::CoreConcreteLibfunc; use cairo_lang_sierra::program::Statement as SierraStatement; use std::collections::HashSet; +// Note: It's possible to have FPs when the long syntax to emit events is used +// e.g. self.emit(Event::MyUsedEvent(MyUsedEvent { value: amount })); + #[derive(Default)] pub struct UnusedEvents {} @@ -30,11 +33,7 @@ impl Detector for UnusedEvents { let compilation_units = core.get_compilation_units(); for compilation_unit in compilation_units { - let mut events: HashSet = compilation_unit - .functions() - .filter(|f| *f.ty() == Type::Event) - .map(|f| f.name()) - .collect(); + let mut events: HashSet = compilation_unit.all_events_name().collect(); for f in compilation_unit.functions_user_defined() { for event_stmt in f.events_emitted() { @@ -46,16 +45,17 @@ impl Detector for UnusedEvents { .expect("Library function not found in the registry"); if let CoreConcreteLibfunc::FunctionCall(f_called) = libfunc { - // We remove the function called from events - events.remove( - &f_called - .function - .id - .debug_name - .as_ref() - .unwrap() - .to_string(), - ); + // The first non builtin argument is the ContractState, the event is the second + let event_name = filter_builtins_from_signature( + &f_called.signature.param_signatures, + )[1] + .ty + .debug_name + .as_ref() + .unwrap() + .as_str(); + // We remove the event emitted from events + events.remove(event_name); } } } diff --git a/src/detectors/unused_return.rs b/src/detectors/unused_return.rs index 2db732b..11ea202 100644 --- a/src/detectors/unused_return.rs +++ b/src/detectors/unused_return.rs @@ -2,7 +2,6 @@ use super::detector::{Confidence, Detector, Impact, Result}; use crate::core::compilation_unit::CompilationUnit; use crate::core::core_unit::CoreUnit; use crate::core::function::Type; -use crate::utils::filter_builtins_from_returns; use cairo_lang_sierra::extensions::core::CoreConcreteLibfunc; use cairo_lang_sierra::extensions::enm::EnumConcreteLibfunc; use cairo_lang_sierra::extensions::structure::StructConcreteLibfunc; @@ -60,7 +59,11 @@ impl Detector for UnusedReturn { f.name() == f_called.function.id.debug_name.clone().unwrap() }) { // We don't check for unused return in case of Storage functions - if f.ty() == &Type::Storage { + // When a loop function is called in sierra and in that function + // an array is emptied with pop_front this array is dropped + // when returning from the function call and it would be incorrectly + // reported as unused-return + if matches!(f.ty(), &Type::Storage | &Type::Loop) { continue; } } else { @@ -87,7 +90,7 @@ impl Detector for UnusedReturn { .expect("Type not found in registry"); let info = ty_dropped.info(); // If size is 0 it's the Unit type - if info.size != 0 { + if !info.zero_sized { results.push(Result { name: self.name().to_string(), impact: self.impact(), @@ -103,11 +106,8 @@ impl Detector for UnusedReturn { StructConcreteLibfunc::Deconstruct(_), ) = libfunc { - let return_variables = filter_builtins_from_returns( - &f_called.signature.branch_signatures[0].vars, - invoc.branches[0].results.clone(), - ) - .len(); + let return_variables = invoc.branches[0].results.len(); + // Go to the next statement and update the libfunc let stmt_to_check = &following_stmts[1..]; if let SierraStatement::Invocation(invoc) = &stmt_to_check[0] { @@ -130,11 +130,8 @@ impl Detector for UnusedReturn { EnumConcreteLibfunc::Match(_), ) = libfunc { - let return_variables = filter_builtins_from_returns( - &f_called.signature.branch_signatures[0].vars, - invoc.branches[0].results.clone(), - ) - .len(); + let return_variables = invoc.branches[0].results.len(); + // Jump one statement which is a branch_align and the next one will be a struct_deconstruct let stmt_to_check = &following_stmts[2..]; if let SierraStatement::Invocation(invoc) = &stmt_to_check[0] { @@ -179,16 +176,17 @@ impl<'a> UnusedReturn { let mut return_variables_counter = 0; while let CoreConcreteLibfunc::Struct(StructConcreteLibfunc::Deconstruct(_)) = libfunc { if let SierraStatement::Invocation(invoc) = &stmt_to_check[0] { + libfunc = compilation_unit + .registry() + .get_libfunc(&invoc.libfunc_id) + .expect("Library function not found in the registry"); + // If there are other struct deconstruction are not related to the returned variables if return_variables_counter == return_variables { break; } return_variables_counter += 1; - libfunc = compilation_unit - .registry() - .get_libfunc(&invoc.libfunc_id) - .expect("Library function not found in the registry"); stmt_to_check = &stmt_to_check[1..]; } else { @@ -204,7 +202,7 @@ impl<'a> UnusedReturn { .expect("Type not found in registry"); let info = ty_dropped.info(); // If size is 0 it's the Unit type - if info.size != 0 { + if !info.zero_sized { results.push(Result { name: self.name().to_string(), impact: self.impact(), diff --git a/src/utils/mod.rs b/src/utils/mod.rs index e169bd8..b03ce87 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,16 +1,25 @@ use cairo_lang_sierra::extensions::lib_func::{OutputVarInfo, ParamSignature}; use cairo_lang_sierra::ids::VarId; -pub const BUILTINS: [&str; 7] = [ +pub const BUILTINS: [&str; 8] = [ "Pedersen", "RangeCheck", "Bitwise", "EcOp", + "Poseidon", "SegmentArena", "GasBuiltin", "System", ]; +/// Filter the builtins from a function signature +pub fn filter_builtins_from_signature(signature: &[ParamSignature]) -> Vec<&ParamSignature> { + signature + .iter() + .filter(|sig_elem| !BUILTINS.contains(&sig_elem.ty.debug_name.as_ref().unwrap().as_str())) + .collect() +} + /// Filter the builtins arguments and returns only the user defined arguments pub fn filter_builtins_from_arguments( signature: &[ParamSignature], @@ -20,13 +29,14 @@ pub fn filter_builtins_from_arguments( .iter() .zip(arguments) .filter(|(sig_elem, _)| { - !BUILTINS.contains(&sig_elem.ty.debug_name.clone().unwrap().as_str()) + !BUILTINS.contains(&sig_elem.ty.debug_name.as_ref().unwrap().as_str()) }) .map(|(_, arg_elem)| arg_elem) .collect() } /// Filter the builtins from the return variables and returns only the user defined variables +#[allow(dead_code)] pub fn filter_builtins_from_returns( signature: &[OutputVarInfo], returns: Vec, @@ -35,7 +45,7 @@ pub fn filter_builtins_from_returns( .iter() .zip(returns) .filter(|(sig_elem, _)| { - !BUILTINS.contains(&sig_elem.ty.debug_name.clone().unwrap().as_str()) + !BUILTINS.contains(&sig_elem.ty.debug_name.as_ref().unwrap().as_str()) }) .map(|(_, arg_elem)| arg_elem) .collect() diff --git a/tests/detectors/controlled_library_call.cairo b/tests/detectors/controlled_library_call.cairo index 22a4da8..ebd6329 100644 --- a/tests/detectors/controlled_library_call.cairo +++ b/tests/detectors/controlled_library_call.cairo @@ -1,21 +1,24 @@ -#[abi] -trait IAnotherContract { - fn foo(a: u128) -> u128; +#[starknet::interface] +trait IAnotherContract { + fn foo(ref self: T, a: u128) -> u128; } -#[contract] +#[starknet::contract] mod TestContract { use super::IAnotherContractDispatcherTrait; use super::IAnotherContractLibraryDispatcher; use starknet::class_hash::ClassHash; - #[external] - fn bad1(class_hash: ClassHash) -> u128 { + #[storage] + struct Storage {} + + #[external(v0)] + fn bad1(ref self: ContractState, class_hash: ClassHash) -> u128 { IAnotherContractLibraryDispatcher { class_hash: class_hash }.foo(2_u128) } - #[external] - fn bad2(class_hash: ClassHash) -> u128 { + #[external(v0)] + fn bad2(ref self: ContractState, class_hash: ClassHash) -> u128 { internal_lib_call(class_hash) } @@ -23,8 +26,8 @@ mod TestContract { IAnotherContractLibraryDispatcher { class_hash: class_hash }.foo(2_u128) } - #[external] - fn good() -> u128 { + #[external(v0)] + fn good(ref self: ContractState) -> u128 { IAnotherContractLibraryDispatcher { class_hash: starknet::class_hash_const::<0>() }.foo(2_u128) } diff --git a/tests/detectors/dead_code.cairo b/tests/detectors/dead_code.cairo index 72c9cef..1dc9ff2 100644 --- a/tests/detectors/dead_code.cairo +++ b/tests/detectors/dead_code.cairo @@ -1,8 +1,11 @@ -#[contract] +#[starknet::contract] mod DeadCode { - - #[external] - fn use_event(amount: felt252) -> felt252{ + + #[storage] + struct Storage {} + + #[external(v0)] + fn use_add_1(self: @ContractState, amount: felt252) -> felt252{ add_1(amount) } diff --git a/tests/detectors/read_only_reentrancy.cairo b/tests/detectors/read_only_reentrancy.cairo new file mode 100644 index 0000000..5059723 --- /dev/null +++ b/tests/detectors/read_only_reentrancy.cairo @@ -0,0 +1,39 @@ +#[starknet::interface] +trait IAnotherContract { + fn foo(self: @T, a: felt252); +} + +#[starknet::contract] +mod TestContract { + use super::IAnotherContractDispatcherTrait; + use super::IAnotherContractDispatcher; + use starknet::ContractAddress; + + #[storage] + struct Storage { + a: felt252, + b: felt252, + } + + #[external(v0)] + fn get_a(self: @ContractState) -> felt252 { + self.a.read() + } + + #[external(v0)] + fn get_b(self: @ContractState) -> felt252 { + self.b.read() + } + + #[external(v0)] + fn bad(ref self: ContractState, address: ContractAddress) { + IAnotherContractDispatcher { contract_address: address }.foo(4); + self.a.write(4); + } + + #[external(v0)] + fn ok(ref self: ContractState, address: ContractAddress) { + IAnotherContractDispatcher { contract_address: address }.foo(4); + } + +} diff --git a/tests/detectors/reentrancy.cairo b/tests/detectors/reentrancy.cairo index 9fdf9e9..f6b8da1 100644 --- a/tests/detectors/reentrancy.cairo +++ b/tests/detectors/reentrancy.cairo @@ -1,75 +1,76 @@ -#[abi] -trait IAnotherContract { - fn foo(a: felt252); +#[starknet::interface] +trait IAnotherContract { + fn foo(self: @T, a: felt252); } -#[contract] +#[starknet::contract] mod TestContract { use super::IAnotherContractDispatcherTrait; use super::IAnotherContractDispatcher; use starknet::ContractAddress; - + + #[storage] struct Storage { a: felt252, b: felt252, } - #[external] - fn good1(address: ContractAddress) { - let a = a::read(); - a::write(4); + #[external(v0)] + fn good1(ref self: ContractState, address: ContractAddress) { + let a = self.a.read(); + self.a.write(4); IAnotherContractDispatcher { contract_address: address }.foo(a); } - #[external] - fn bad1(address: ContractAddress) { - let a = a::read(); + #[external(v0)] + fn bad1(ref self: ContractState, address: ContractAddress) { + let a = self.a.read(); IAnotherContractDispatcher { contract_address: address }.foo(a); - a::write(4); + self.a.write(4); } - #[external] - fn bad2(address: ContractAddress) { + #[external(v0)] + fn bad2(ref self: ContractState, address: ContractAddress) { if 2 == 2 { - let a = a::read(); + let a = self.a.read(); IAnotherContractDispatcher { contract_address: address }.foo(a); } else { - let b = b::read(); + let b = self.b.read(); IAnotherContractDispatcher { contract_address: address }.foo(b); } - a::write(4); - b::write(4); + self.a.write(4); + self.b.write(4); } - #[external] - fn bad3(address: ContractAddress) { - let a = a::read(); + #[external(v0)] + fn bad3(ref self: ContractState, address: ContractAddress) { + let a = self.a.read(); internal_ext_call(address); - a::write(4); + self.a.write(4); } fn internal_ext_call(address: ContractAddress) { IAnotherContractDispatcher { contract_address: address }.foo(4); } - #[external] - fn bad4(address: ContractAddress) { - internal_ext_call2(address); - a::write(4); + #[external(v0)] + fn bad4(ref self: ContractState, address: ContractAddress) { + internal_ext_call2(ref self, address); + self.a.write(4); } - #[external] - fn bad5(address: ContractAddress) { - let a = a::read(); + #[external(v0)] + fn bad5(ref self: ContractState, address: ContractAddress) { + let a = self.a.read(); loop { IAnotherContractDispatcher { contract_address: address }.foo(a); - break (); + break; }; - a::write(4); + self.a.write(4); } - fn internal_ext_call2(address: ContractAddress) { - let a = a::read(); + fn internal_ext_call2(ref self: ContractState, address: ContractAddress) { + let a = self.a.read(); IAnotherContractDispatcher { contract_address: address }.foo(4); } diff --git a/tests/detectors/reentrancy_benign.cairo b/tests/detectors/reentrancy_benign.cairo index 57e8c65..c56b9f0 100644 --- a/tests/detectors/reentrancy_benign.cairo +++ b/tests/detectors/reentrancy_benign.cairo @@ -1,57 +1,58 @@ -#[abi] -trait IAnotherContract { - fn foo(a: felt252); +#[starknet::interface] +trait IAnotherContract { + fn foo(self: @T, a: felt252); } -#[contract] +#[starknet::contract] mod TestContract { use super::IAnotherContractDispatcherTrait; use super::IAnotherContractDispatcher; use starknet::ContractAddress; + #[storage] struct Storage { a: felt252, b: felt252, } - #[external] - fn good1(address: ContractAddress) { - let a = a::read(); - a::write(4); + #[external(v0)] + fn good1(ref self: ContractState, address: ContractAddress) { + let a = self.a.read(); + self.a.write(4); IAnotherContractDispatcher { contract_address: address }.foo(a); } - #[external] - fn bad1(address: ContractAddress) { + #[external(v0)] + fn bad1(ref self: ContractState, address: ContractAddress) { IAnotherContractDispatcher { contract_address: address }.foo(4); - a::write(4); + self.a.write(4); } - #[external] - fn bad2(address: ContractAddress) { + #[external(v0)] + fn bad2(ref self: ContractState, address: ContractAddress) { if 2 == 2 { IAnotherContractDispatcher { contract_address: address }.foo(4); } else { IAnotherContractDispatcher { contract_address: address }.foo(4); } - a::write(4); - b::write(4); + self.a.write(4); + self.b.write(4); } - #[external] - fn bad3(address: ContractAddress) { + #[external(v0)] + fn bad3(ref self: ContractState, address: ContractAddress) { internal_ext_call(address); - a::write(4); + self.a.write(4); } fn internal_ext_call(address: ContractAddress) { IAnotherContractDispatcher { contract_address: address }.foo(4); } - #[external] - fn bad4(address: ContractAddress) { + #[external(v0)] + fn bad4(ref self: ContractState, address: ContractAddress) { internal_ext_call2(address); - a::write(4); + self.a.write(4); } fn internal_ext_call2(address: ContractAddress) { diff --git a/tests/detectors/reentrancy_events.cairo b/tests/detectors/reentrancy_events.cairo index d944045..4469965 100644 --- a/tests/detectors/reentrancy_events.cairo +++ b/tests/detectors/reentrancy_events.cairo @@ -1,29 +1,36 @@ -#[abi] -trait IAnotherContract { - fn foo(a: felt252); +#[starknet::interface] +trait IAnotherContract { + fn foo(self: @T, a: felt252); } -#[contract] +#[starknet::contract] mod TestContract { use super::IAnotherContractDispatcherTrait; use super::IAnotherContractDispatcher; use starknet::ContractAddress; #[event] - fn MyEvent() {} + #[derive(Drop, starknet::Event)] + enum Event { + MyEvent: MyEvent, + } + + #[derive(Drop, starknet::Event)] + struct MyEvent {} + #[storage] struct Storage {} - #[external] - fn good1(address: ContractAddress) { - MyEvent(); + #[external(v0)] + fn good1(ref self: ContractState, address: ContractAddress) { + self.emit(MyEvent { }); IAnotherContractDispatcher { contract_address: address }.foo(4); } - #[external] - fn bad1(address: ContractAddress) { + #[external(v0)] + fn bad1(ref self: ContractState, address: ContractAddress) { IAnotherContractDispatcher { contract_address: address }.foo(4); - MyEvent(); + self.emit(MyEvent { }); } } diff --git a/tests/detectors/unchecked_l1_handler_from.cairo b/tests/detectors/unchecked_l1_handler_from.cairo index 823cb87..c5b73ba 100644 --- a/tests/detectors/unchecked_l1_handler_from.cairo +++ b/tests/detectors/unchecked_l1_handler_from.cairo @@ -1,18 +1,20 @@ -#[contract] +#[starknet::contract] mod UncheckedL1HandlerFrom { + #[storage] + struct Storage {} #[l1_handler] - fn bad(from_address: felt252) { + fn bad(self: @ContractState, from_address: felt252) { from_address + 1; } #[l1_handler] - fn good(from_address: felt252) { + fn good(self: @ContractState, from_address: felt252) { assert(from_address == 0, 'Wrong L1 sender'); } #[l1_handler] - fn good2(from_address: felt252) { + fn good2(self: @ContractState, from_address: felt252) { check_from_address(from_address); } @@ -22,7 +24,7 @@ mod UncheckedL1HandlerFrom { } #[l1_handler] - fn good3(from_address: felt252) { + fn good3(self: @ContractState, from_address: felt252) { let x = check_recursive(from_address, 0); x + 2; } diff --git a/tests/detectors/unenforced_view.cairo b/tests/detectors/unenforced_view.cairo deleted file mode 100644 index 6f7ff7b..0000000 --- a/tests/detectors/unenforced_view.cairo +++ /dev/null @@ -1,56 +0,0 @@ -#[contract] -mod UnenforcedView { - struct Storage { - value: felt252, - } - - #[view] - fn writes_to_storage_indirect(val: felt252) { - f1(val); - } - - fn f1(val: felt252) { - f2(val); - } - - - fn f2(val: felt252) { - value::write(val); - } - - #[view] - fn writes_to_storage_direct(val:felt252) { - value::write(val); - } - - #[view] - fn recursive_storage_write_direct(val: felt252) { - if val ==0 { - () - } - value::write(val); - recursive_storage_write_direct(val-1); - } - - #[view] - fn recursive_storage_write_indirect(val: felt252) { - if val ==0 { - () - } - f3(val); - } - - fn f3(val: felt252) { - value::write(val); - recursive_storage_write_indirect(val-1); - } - - - #[view] - fn does_not_write_to_storage() -> felt252 { - value::read() - } -} - - - diff --git a/tests/detectors/unused_arguments.cairo b/tests/detectors/unused_arguments.cairo index 80269fa..5fa45af 100644 --- a/tests/detectors/unused_arguments.cairo +++ b/tests/detectors/unused_arguments.cairo @@ -1,18 +1,19 @@ use array::ArrayTrait; -#[contract] -mod UnusedArguments { +#[starknet::contract] +mod UnusedArguments { + #[storage] struct Storage { value: felt252, } - #[external] - fn unused_1(a: felt252, b: felt252) { - value::write(a); + #[external(v0)] + fn unused_1(ref self: ContractState, a: felt252, b: felt252) { + self.value.write(a); } - #[external] - fn unused_2(array: Array::, l: felt252) -> felt252{ + #[external(v0)] + fn unused_2(self: @ContractState, array: Array::, l: felt252) -> felt252{ 1 } diff --git a/tests/detectors/unused_events.cairo b/tests/detectors/unused_events.cairo index 15a91f0..c79e1e5 100644 --- a/tests/detectors/unused_events.cairo +++ b/tests/detectors/unused_events.cairo @@ -1,13 +1,29 @@ -#[contract] +#[starknet::contract] mod UnusedEvents { - #[event] - fn MyUnusedEvent(value: u256) {} + + #[storage] + struct Storage {} #[event] - fn MyUsedEvent(value: u256) {} + #[derive(Drop, starknet::Event)] + enum Event { + MyUnusedEvent: MyUnusedEvent, + MyUsedEvent: MyUsedEvent + } + + #[derive(Drop, starknet::Event)] + struct MyUnusedEvent { + value: u256, + } + + #[derive(Drop, starknet::Event)] + struct MyUsedEvent { + value: u256, + } - #[external] - fn use_event(amount: u256) { - MyUsedEvent(amount); + #[external(v0)] + fn use_event1(ref self: ContractState, amount: u256) { + self.emit(MyUsedEvent { value: amount }); } + } diff --git a/tests/detectors/unused_return.cairo b/tests/detectors/unused_return.cairo index 3e46e8f..aa22c9c 100644 --- a/tests/detectors/unused_return.cairo +++ b/tests/detectors/unused_return.cairo @@ -1,48 +1,62 @@ use option::OptionTrait; -#[contract] +#[starknet::contract] mod UnusedReturn { + #[storage] struct Storage { value: felt252, } - #[external] - fn unused_return_1(amount: felt252) { - f_1(amount); + #[external(v0)] + fn unused_return_1(ref self: ContractState, amount: felt252) { + f_1(ref self, amount); } - #[external] - fn unused_return_2(amount: felt252) { - f_2(amount); + #[external(v0)] + fn unused_return_2(ref self: ContractState, amount: felt252) -> felt252 { + let (a,b,d) = f_2(amount); + let c = a ; + a } - #[external] - fn unused_return_3(amount: felt252) { + #[external(v0)] + fn unused_return_3(ref self: ContractState, amount: felt252) { f_3(amount); } - #[external] - fn unused_return_4(amount: felt252) { + #[external(v0)] + fn unused_return_4(ref self: ContractState, amount: felt252) { f_4(amount); } - #[external] - fn unused_return_5() { - let a = f_5(); + #[external(v0)] + fn unused_return_5(ref self: ContractState) { + let a = f_5(ref self); } - #[external] - fn no_report() { - let a = value::read(); + #[external(v0)] + fn no_report(ref self: ContractState) { + let a = self.value.read(); } - fn f_1(amount: felt252) -> felt252 { - value::write(amount); + #[external(v0)] + fn no_report2(ref self: ContractState) -> felt252 { + let a = f_5(ref self); + a + } + + #[external(v0)] + fn no_report3(ref self: ContractState) -> felt252 { + f_5(ref self) + } + + fn f_1(ref self: ContractState, amount: felt252) -> felt252 { + self.value.write(amount); 23 } - fn f_2(amount: felt252) -> (felt252, felt252) { - (amount, amount) + fn f_2(amount: felt252) -> (felt252, felt252, felt252) { + (amount, amount, amount) } fn f_3(amount: felt252) -> felt252 { @@ -53,8 +67,8 @@ mod UnusedReturn { Option::Some(amount) } - fn f_5() -> felt252 { - let a = value::read(); + fn f_5(ref self: ContractState) -> felt252 { + let a = self.value.read(); a * 2 } diff --git a/tests/snapshots/integration_tests__detectors@controlled_library_call.cairo.snap b/tests/snapshots/integration_tests__detectors@controlled_library_call.cairo.snap index 2036df2..1221adc 100644 --- a/tests/snapshots/integration_tests__detectors@controlled_library_call.cairo.snap +++ b/tests/snapshots/integration_tests__detectors@controlled_library_call.cairo.snap @@ -1,13 +1,14 @@ --- source: tests/integration_tests.rs expression: results +input_file: tests/detectors/controlled_library_call.cairo --- [ Result { impact: High, name: "controlled-library-call", confidence: Medium, - message: "Library call to user controlled class hash in controlled_library_call::controlled_library_call::TestContract::bad1\n function_call([10], [11], [12], [13], [14]) -> ([6], [7], [8], [9])", + message: "Library call to user controlled class hash in controlled_library_call::controlled_library_call::TestContract::bad1\n function_call([11], [12], [13], [14], [15]) -> ([7], [8], [9], [10])", }, Result { impact: High, diff --git a/tests/snapshots/integration_tests__detectors@read_only_reentrancy.cairo.snap b/tests/snapshots/integration_tests__detectors@read_only_reentrancy.cairo.snap new file mode 100644 index 0000000..59a317d --- /dev/null +++ b/tests/snapshots/integration_tests__detectors@read_only_reentrancy.cairo.snap @@ -0,0 +1,19 @@ +--- +source: tests/integration_tests.rs +expression: results +input_file: tests/detectors/read_only_reentrancy.cairo +--- +[ + Result { + impact: Medium, + name: "read-only-reentrancy", + confidence: Medium, + message: "Read only reentrancy in read_only_reentrancy::read_only_reentrancy::TestContract::get_a\n\tExternal call function_call([9], [10], [11], [12]) -> ([6], [7], [8]) done in read_only_reentrancy::read_only_reentrancy::TestContract::bad\n\tVariable written after function_call([21], [22], [23], [24]) -> ([18], [19], [20]) in read_only_reentrancy::read_only_reentrancy::TestContract::bad", + }, + Result { + impact: Low, + name: "reentrancy-benign", + confidence: Medium, + message: "Reentrancy in read_only_reentrancy::read_only_reentrancy::TestContract::bad\n\tExternal call function_call([9], [10], [11], [12]) -> ([6], [7], [8]) done in read_only_reentrancy::read_only_reentrancy::TestContract::bad\n\tVariable written after function_call([21], [22], [23], [24]) -> ([18], [19], [20]) in read_only_reentrancy::read_only_reentrancy::TestContract::bad.", + }, +] diff --git a/tests/snapshots/integration_tests__detectors@reentrancy.cairo.snap b/tests/snapshots/integration_tests__detectors@reentrancy.cairo.snap index daf7643..fbe5072 100644 --- a/tests/snapshots/integration_tests__detectors@reentrancy.cairo.snap +++ b/tests/snapshots/integration_tests__detectors@reentrancy.cairo.snap @@ -8,48 +8,48 @@ input_file: tests/detectors/reentrancy.cairo impact: Medium, name: "reentrancy", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad1\n\tExternal call function_call([15], [16], [17], [18]) -> ([12], [13], [14]) done in reentrancy::reentrancy::TestContract::bad1\n\tVariable written after function_call([25], [26], [27]) -> ([22], [23], [24]) in reentrancy::reentrancy::TestContract::bad1.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad1\n\tExternal call function_call([21], [22], [23], [24]) -> ([18], [19], [20]) done in reentrancy::reentrancy::TestContract::bad1\n\tVariable written after function_call([31], [32], [33], [34]) -> ([28], [29], [30]) in reentrancy::reentrancy::TestContract::bad1.", }, Result { impact: Medium, name: "reentrancy", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([19], [20], [21], [22]) -> ([16], [17], [18]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([57], [58], [59]) -> ([54], [55], [56]) in reentrancy::reentrancy::TestContract::bad2.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([25], [26], [27], [28]) -> ([22], [23], [24]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([73], [74], [75], [76]) -> ([70], [71], [72]) in reentrancy::reentrancy::TestContract::bad2.", }, Result { impact: Medium, name: "reentrancy", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([47], [48], [49], [50]) -> ([44], [45], [46]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([66], [67], [68]) -> ([63], [64], [65]) in reentrancy::reentrancy::TestContract::bad2.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([60], [61], [62], [63]) -> ([57], [58], [59]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([85], [86], [87], [88]) -> ([82], [83], [84]) in reentrancy::reentrancy::TestContract::bad2.", }, Result { impact: Medium, name: "reentrancy", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad3\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy::reentrancy::TestContract::internal_ext_call\n\tVariable written after function_call([22], [23], [24]) -> ([19], [20], [21]) in reentrancy::reentrancy::TestContract::bad3.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad3\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy::reentrancy::TestContract::internal_ext_call\n\tVariable written after function_call([28], [29], [30], [31]) -> ([25], [26], [27]) in reentrancy::reentrancy::TestContract::bad3.", }, Result { impact: Medium, name: "reentrancy", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad4\n\tExternal call function_call([15], [16], [17], [18]) -> ([12], [13], [14]) done in reentrancy::reentrancy::TestContract::internal_ext_call2\n\tVariable written after function_call([15], [16], [17]) -> ([12], [13], [14]) in reentrancy::reentrancy::TestContract::bad4.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad4\n\tExternal call function_call([21], [22], [23], [24]) -> ([18], [19], [20]) done in reentrancy::reentrancy::TestContract::internal_ext_call2\n\tVariable written after function_call([21], [22], [23], [24]) -> ([18], [19], [20]) in reentrancy::reentrancy::TestContract::bad4.", }, Result { impact: Medium, name: "reentrancy", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad5\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in bad5[expr6]\n\tVariable written after function_call([24], [25], [26]) -> ([21], [22], [23]) in reentrancy::reentrancy::TestContract::bad5.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad5\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy::reentrancy::TestContract::bad5[expr8]\n\tVariable written after function_call([30], [31], [32], [33]) -> ([27], [28], [29]) in reentrancy::reentrancy::TestContract::bad5.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([19], [20], [21], [22]) -> ([16], [17], [18]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([66], [67], [68]) -> ([63], [64], [65]) in reentrancy::reentrancy::TestContract::bad2.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([25], [26], [27], [28]) -> ([22], [23], [24]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([85], [86], [87], [88]) -> ([82], [83], [84]) in reentrancy::reentrancy::TestContract::bad2.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([47], [48], [49], [50]) -> ([44], [45], [46]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([57], [58], [59]) -> ([54], [55], [56]) in reentrancy::reentrancy::TestContract::bad2.", + message: "Reentrancy in reentrancy::reentrancy::TestContract::bad2\n\tExternal call function_call([60], [61], [62], [63]) -> ([57], [58], [59]) done in reentrancy::reentrancy::TestContract::bad2\n\tVariable written after function_call([73], [74], [75], [76]) -> ([70], [71], [72]) in reentrancy::reentrancy::TestContract::bad2.", }, ] diff --git a/tests/snapshots/integration_tests__detectors@reentrancy_benign.cairo.snap b/tests/snapshots/integration_tests__detectors@reentrancy_benign.cairo.snap index 2bd6dd1..2270b91 100644 --- a/tests/snapshots/integration_tests__detectors@reentrancy_benign.cairo.snap +++ b/tests/snapshots/integration_tests__detectors@reentrancy_benign.cairo.snap @@ -1,48 +1,49 @@ --- source: tests/integration_tests.rs expression: results +input_file: tests/detectors/reentrancy_benign.cairo --- [ Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad1\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy_benign::reentrancy_benign::TestContract::bad1\n\tVariable written after function_call([18], [19], [20]) -> ([15], [16], [17]) in reentrancy_benign::reentrancy_benign::TestContract::bad1.", + message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad1\n\tExternal call function_call([9], [10], [11], [12]) -> ([6], [7], [8]) done in reentrancy_benign::reentrancy_benign::TestContract::bad1\n\tVariable written after function_call([21], [22], [23], [24]) -> ([18], [19], [20]) in reentrancy_benign::reentrancy_benign::TestContract::bad1.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([12], [13], [14], [15]) -> ([9], [10], [11]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([39], [40], [41]) -> ([36], [37], [38]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", + message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([13], [14], [15], [16]) -> ([10], [11], [12]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([42], [43], [44], [45]) -> ([39], [40], [41]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([12], [13], [14], [15]) -> ([9], [10], [11]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([48], [49], [50]) -> ([45], [46], [47]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", + message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([13], [14], [15], [16]) -> ([10], [11], [12]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([54], [55], [56], [57]) -> ([51], [52], [53]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([29], [30], [31], [32]) -> ([26], [27], [28]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([39], [40], [41]) -> ([36], [37], [38]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", + message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([30], [31], [32], [33]) -> ([27], [28], [29]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([42], [43], [44], [45]) -> ([39], [40], [41]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([29], [30], [31], [32]) -> ([26], [27], [28]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([48], [49], [50]) -> ([45], [46], [47]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", + message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tExternal call function_call([30], [31], [32], [33]) -> ([27], [28], [29]) done in reentrancy_benign::reentrancy_benign::TestContract::bad2\n\tVariable written after function_call([54], [55], [56], [57]) -> ([51], [52], [53]) in reentrancy_benign::reentrancy_benign::TestContract::bad2.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad3\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy_benign::reentrancy_benign::TestContract::internal_ext_call\n\tVariable written after function_call([15], [16], [17]) -> ([12], [13], [14]) in reentrancy_benign::reentrancy_benign::TestContract::bad3.", + message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad3\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy_benign::reentrancy_benign::TestContract::internal_ext_call\n\tVariable written after function_call([18], [19], [20], [21]) -> ([15], [16], [17]) in reentrancy_benign::reentrancy_benign::TestContract::bad3.", }, Result { impact: Low, name: "reentrancy-benign", confidence: Medium, - message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad4\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy_benign::reentrancy_benign::TestContract::internal_ext_call2\n\tVariable written after function_call([15], [16], [17]) -> ([12], [13], [14]) in reentrancy_benign::reentrancy_benign::TestContract::bad4.", + message: "Reentrancy in reentrancy_benign::reentrancy_benign::TestContract::bad4\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy_benign::reentrancy_benign::TestContract::internal_ext_call2\n\tVariable written after function_call([18], [19], [20], [21]) -> ([15], [16], [17]) in reentrancy_benign::reentrancy_benign::TestContract::bad4.", }, ] diff --git a/tests/snapshots/integration_tests__detectors@reentrancy_events.cairo.snap b/tests/snapshots/integration_tests__detectors@reentrancy_events.cairo.snap index 1eb2ce5..267fab0 100644 --- a/tests/snapshots/integration_tests__detectors@reentrancy_events.cairo.snap +++ b/tests/snapshots/integration_tests__detectors@reentrancy_events.cairo.snap @@ -1,12 +1,13 @@ --- source: tests/integration_tests.rs expression: results +input_file: tests/detectors/reentrancy_events.cairo --- [ Result { impact: Low, name: "reentrancy-events", confidence: Medium, - message: "Reentrancy in reentrancy_events::reentrancy_events::TestContract::bad1\n\tExternal call function_call([8], [9], [10], [11]) -> ([5], [6], [7]) done in reentrancy_events::reentrancy_events::TestContract::bad1\n\tEvent emitted after function_call([17], [18]) -> ([14], [15], [16]) in reentrancy_events::reentrancy_events::TestContract::bad1.", + message: "Reentrancy in reentrancy_events::reentrancy_events::TestContract::bad1\n\tExternal call function_call([9], [10], [11], [12]) -> ([6], [7], [8]) done in reentrancy_events::reentrancy_events::TestContract::bad1\n\tEvent emitted after function_call>([19], [20], [21], [22]) -> ([16], [17], [18]) in reentrancy_events::reentrancy_events::TestContract::bad1.", }, ] diff --git a/tests/snapshots/integration_tests__detectors@unenforced_view.cairo.snap b/tests/snapshots/integration_tests__detectors@unenforced_view.cairo.snap deleted file mode 100644 index 60da58e..0000000 --- a/tests/snapshots/integration_tests__detectors@unenforced_view.cairo.snap +++ /dev/null @@ -1,30 +0,0 @@ ---- -source: tests/integration_tests.rs -expression: results ---- -[ - Result { - impact: Medium, - name: "unenforced-view", - confidence: Medium, - message: "recursive_storage_write_direct defined in unenforced_view::unenforced_view::UnenforcedView is declared as view but changes state", - }, - Result { - impact: Medium, - name: "unenforced-view", - confidence: Medium, - message: "recursive_storage_write_indirect defined in unenforced_view::unenforced_view::UnenforcedView is declared as view but changes state", - }, - Result { - impact: Medium, - name: "unenforced-view", - confidence: Medium, - message: "writes_to_storage_direct defined in unenforced_view::unenforced_view::UnenforcedView is declared as view but changes state", - }, - Result { - impact: Medium, - name: "unenforced-view", - confidence: Medium, - message: "writes_to_storage_indirect defined in unenforced_view::unenforced_view::UnenforcedView is declared as view but changes state", - }, -] diff --git a/tests/snapshots/integration_tests__detectors@unused_arguments.cairo.snap b/tests/snapshots/integration_tests__detectors@unused_arguments.cairo.snap index b7bc658..00d5219 100644 --- a/tests/snapshots/integration_tests__detectors@unused_arguments.cairo.snap +++ b/tests/snapshots/integration_tests__detectors@unused_arguments.cairo.snap @@ -1,24 +1,25 @@ --- source: tests/integration_tests.rs expression: results +input_file: tests/detectors/unused_arguments.cairo --- [ Result { impact: Low, name: "unused-arguments", confidence: Medium, - message: "The 1st argument in unused_arguments::unused_arguments::UnusedArguments::unused_2 is never used", + message: "The 2nd argument in unused_arguments::unused_arguments::UnusedArguments::unused_2 is never used", }, Result { impact: Low, name: "unused-arguments", confidence: Medium, - message: "The 2nd argument in unused_arguments::unused_arguments::UnusedArguments::unused_1 is never used", + message: "The 3rd argument in unused_arguments::unused_arguments::UnusedArguments::unused_1 is never used", }, Result { impact: Low, name: "unused-arguments", confidence: Medium, - message: "The 2nd argument in unused_arguments::unused_arguments::UnusedArguments::unused_2 is never used", + message: "The 3rd argument in unused_arguments::unused_arguments::UnusedArguments::unused_2 is never used", }, ] diff --git a/tests/snapshots/integration_tests__detectors@unused_return.cairo.snap b/tests/snapshots/integration_tests__detectors@unused_return.cairo.snap index e428a47..7fe636d 100644 --- a/tests/snapshots/integration_tests__detectors@unused_return.cairo.snap +++ b/tests/snapshots/integration_tests__detectors@unused_return.cairo.snap @@ -1,36 +1,37 @@ --- source: tests/integration_tests.rs expression: results +input_file: tests/detectors/unused_return.cairo --- [ Result { impact: Medium, name: "unused-return", confidence: Medium, - message: "Return value unused for the function call function_call([6], [7], [8]) -> ([3], [4], [5]) in unused_return::unused_return::UnusedReturn::unused_return_1", + message: "Return value unused for the function call function_call([7], [8], [9], [10]) -> ([4], [5], [6]) in unused_return::unused_return::UnusedReturn::unused_return_1", }, Result { impact: Medium, name: "unused-return", confidence: Medium, - message: "Return value unused for the function call function_call([2]) -> ([1]) in unused_return::unused_return::UnusedReturn::unused_return_2", + message: "Return value unused for the function call function_call([3]) -> ([2]) in unused_return::unused_return::UnusedReturn::unused_return_2", }, Result { impact: Medium, name: "unused-return", confidence: Medium, - message: "Return value unused for the function call function_call([2]) -> ([1]) in unused_return::unused_return::UnusedReturn::unused_return_3", + message: "Return value unused for the function call function_call([3]) -> ([2]) in unused_return::unused_return::UnusedReturn::unused_return_3", }, Result { impact: Medium, name: "unused-return", confidence: Medium, - message: "Return value unused for the function call function_call([2]) -> ([1]) in unused_return::unused_return::UnusedReturn::unused_return_4", + message: "Return value unused for the function call function_call([3]) -> ([2]) in unused_return::unused_return::UnusedReturn::unused_return_4", }, Result { impact: Medium, name: "unused-return", confidence: Medium, - message: "Return value unused for the function call function_call([5], [6]) -> ([2], [3], [4]) in unused_return::unused_return::UnusedReturn::unused_return_5", + message: "Return value unused for the function call function_call([6], [7], [8]) -> ([3], [4], [5]) in unused_return::unused_return::UnusedReturn::unused_return_5", }, ]