diff --git a/Cargo.lock b/Cargo.lock index 44ec7b53b..7f52b6fbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.3" @@ -22,6 +33,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "anstream" version = "0.3.2" @@ -105,6 +122,23 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits 0.2.15", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.4.2" @@ -117,7 +151,7 @@ dependencies = [ "ark-std 0.4.0", "derivative", "digest", - "itertools", + "itertools 0.10.5", "num-bigint", "num-traits 0.2.15", "paste", @@ -148,17 +182,64 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-secp256k1" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02e954eaeb4ddb29613fee20840c2bbc85ca4396d53e33837e11905363c5f2" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-secp256r1" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3975a01b0a6e3eae0f72ec7ca8598a6620fc72fa5981f6f5cca33b7cd788f633" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std 0.4.0", +] + [[package]] name = "ark-serialize" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ + "ark-serialize-derive", "ark-std 0.4.0", "digest", "num-bigint", ] +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-std" version = "0.3.0" @@ -179,6 +260,12 @@ dependencies = [ "rand", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -280,11 +367,17 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93dedd19b8edf685798f1f12e4e0ac21ac196ea5262c300783f69f3fa0cb28b" +checksum = "c495417de017d516c679f07f63c76a037521b5a80cbbf0928389c70987f6db3a" dependencies = [ "lazy_static", "num-bigint", @@ -295,21 +388,24 @@ dependencies = [ [[package]] name = "cairo-lang-casm" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-utils", "indoc", "num-bigint", "num-traits 0.2.15", + "parity-scale-codec", + "parity-scale-codec-derive", + "schemars", "serde", "thiserror", ] [[package]] name = "cairo-lang-compiler" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "anyhow", "cairo-lang-defs", @@ -324,7 +420,7 @@ dependencies = [ "cairo-lang-sierra-generator", "cairo-lang-syntax", "cairo-lang-utils", - "clap", + "itertools 0.11.0", "log", "salsa", "smol_str", @@ -333,13 +429,16 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" +dependencies = [ + "cairo-lang-utils", +] [[package]] name = "cairo-lang-defs" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -347,38 +446,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.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" 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.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-utils", "good_lp", - "indexmap", - "itertools", + "indexmap 2.0.0", + "itertools 0.11.0", ] [[package]] name = "cairo-lang-filesystem" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -390,8 +490,8 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -403,19 +503,20 @@ 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", + "once_cell", "salsa", "smol_str", ] [[package]] name = "cairo-lang-parser" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -423,7 +524,7 @@ dependencies = [ "cairo-lang-syntax-codegen", "cairo-lang-utils", "colored", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-traits 0.2.15", @@ -434,52 +535,56 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-defs", "cairo-lang-diagnostics", "cairo-lang-filesystem", "cairo-lang-parser", - "cairo-lang-semantic", "cairo-lang-syntax", "cairo-lang-utils", + "indent", "indoc", - "itertools", + "itertools 0.11.0", + "num-bigint", "salsa", "smol_str", ] [[package]] name = "cairo-lang-proc-macros" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-debug", "quote", - "syn 1.0.109", + "syn 2.0.29", ] [[package]] name = "cairo-lang-project" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-filesystem", + "cairo-lang-utils", "serde", "smol_str", "thiserror", - "toml 0.4.10", + "toml 0.7.6", ] [[package]] name = "cairo-lang-runner" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "anyhow", "ark-ff", - "ark-std 0.3.0", + "ark-secp256k1", + "ark-secp256r1", + "ark-std 0.4.0", "cairo-felt", "cairo-lang-casm", "cairo-lang-compiler", @@ -493,11 +598,11 @@ dependencies = [ "cairo-lang-sierra-gas", "cairo-lang-sierra-generator", "cairo-lang-sierra-to-casm", + "cairo-lang-sierra-type-size", "cairo-lang-starknet", "cairo-lang-utils", "cairo-vm", - "clap", - "itertools", + "itertools 0.11.0", "keccak", "num-bigint", "num-integer", @@ -508,36 +613,38 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", "cairo-lang-diagnostics", "cairo-lang-filesystem", "cairo-lang-parser", + "cairo-lang-plugins", "cairo-lang-proc-macros", "cairo-lang-syntax", "cairo-lang-utils", "id-arena", - "itertools", + "itertools 0.11.0", "log", "num-bigint", "num-traits 0.2.15", + "once_cell", "salsa", "smol_str", ] [[package]] name = "cairo-lang-sierra" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-utils", "const-fnv1a-hash", "convert_case", "derivative", - "itertools", + "itertools 0.11.0", "lalrpop", "lalrpop-util", "num-bigint", @@ -552,32 +659,34 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" 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.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" 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.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -592,39 +701,48 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "id-arena", - "indexmap", - "itertools", + "indexmap 2.0.0", + "itertools 0.11.0", "num-bigint", + "once_cell", "salsa", "smol_str", ] [[package]] name = "cairo-lang-sierra-to-casm" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" 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", "thiserror", ] +[[package]] +name = "cairo-lang-sierra-type-size" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" +dependencies = [ + "cairo-lang-sierra", + "cairo-lang-utils", +] + [[package]] name = "cairo-lang-starknet" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "anyhow", "cairo-felt", @@ -644,11 +762,11 @@ 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", @@ -663,8 +781,8 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -679,53 +797,74 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ - "cairo-lang-utils", "genco", - "log", "xshell", ] [[package]] -name = "cairo-lang-utils" -version = "1.1.0" -source = "git+https://github.com/starkware-libs/cairo?rev=1003d5d14c09191bbfb64ee318d1975584b3c819#1003d5d14c09191bbfb64ee318d1975584b3c819" +name = "cairo-lang-test-runner" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ - "env_logger", - "indexmap", - "itertools", - "log", + "anyhow", + "cairo-felt", + "cairo-lang-casm", + "cairo-lang-compiler", + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-plugins", + "cairo-lang-project", + "cairo-lang-runner", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-generator", + "cairo-lang-sierra-to-casm", + "cairo-lang-starknet", + "cairo-lang-syntax", + "cairo-lang-utils", + "cairo-vm", + "colored", + "itertools 0.11.0", "num-bigint", - "num-integer", "num-traits 0.2.15", - "serde", - "time", + "rayon 1.7.0", + "salsa", + "thiserror", ] [[package]] -name = "cairo-take_until_unbalanced" -version = "0.24.2-rc1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e174056df7cfe9b579376f32de405722e1090c74deb2540bb0cd9e7931772d" +name = "cairo-lang-utils" +version = "2.1.1" +source = "git+https://github.com/starkware-libs/cairo#b97054e2ffd6764ce924250957a194c5448eea6a" dependencies = [ - "nom", + "indexmap 2.0.0", + "itertools 0.11.0", + "num-bigint", + "num-integer", + "num-traits 0.2.15", + "parity-scale-codec", + "schemars", + "serde", ] [[package]] name = "cairo-vm" -version = "0.3.0-rc1" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f4af8c3e7be5ac46b7da4a3ab551ee4c8c8e2fdb501a4dda4dceef4c66dbcde" +checksum = "2dfb63df27509b6c86a58b23646248949020dbb225c9d2b6a396d4eac4f1bac6" dependencies = [ "anyhow", "bincode", "bitvec", "cairo-felt", - "cairo-take_until_unbalanced", "generic-array", - "hashbrown 0.13.2", + "hashbrown 0.14.0", "hex", "keccak", "lazy_static", @@ -733,15 +872,14 @@ dependencies = [ "nom", "num-bigint", "num-integer", + "num-prime", "num-traits 0.2.15", - "rand_core", + "rand", "serde", - "serde_bytes", "serde_json", "sha2", "sha3", "starknet-crypto", - "thiserror", "thiserror-no-std", ] @@ -796,7 +934,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.29", ] [[package]] @@ -908,24 +1046,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-bigint" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" -dependencies = [ - "generic-array", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ + "generic-array", "subtle", + "zeroize", ] [[package]] @@ -993,6 +1122,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "dyn-clone" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" + [[package]] name = "either" version = "1.8.1" @@ -1015,17 +1150,10 @@ 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" @@ -1200,6 +1328,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] [[package]] name = "hashbrown" @@ -1207,7 +1338,17 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash", + "ahash 0.8.3", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash 0.8.3", + "allocator-api2", "serde", ] @@ -1274,18 +1415,29 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "id-arena" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indent" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f1a0777d972970f204fdf8ef319f1f4f8459131636d7e3c96c5d59570d0fa6" + [[package]] name = "indexmap" version = "1.9.3" @@ -1297,6 +1449,17 @@ dependencies = [ "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 = "indicatif" version = "0.16.2" @@ -1385,6 +1548,15 @@ 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.6" @@ -1421,20 +1593,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", @@ -1443,9 +1616,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", ] @@ -1503,6 +1676,15 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + [[package]] name = "matrixmultiply" version = "0.2.4" @@ -1668,6 +1850,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits 0.2.15", + "rand", "serde", ] @@ -1691,6 +1874,33 @@ dependencies = [ "num-traits 0.2.15", ] +[[package]] +name = "num-modular" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits 0.2.15", +] + +[[package]] +name = "num-prime" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4e3bc495f6e95bc15a6c0c55ac00421504a5a43d09e3cc455d1fea7015581d" +dependencies = [ + "bitvec", + "either", + "lru", + "num-bigint", + "num-integer", + "num-modular", + "num-traits 0.2.15", + "rand", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -1719,15 +1929,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "number_prefix" version = "0.4.0" @@ -1746,6 +1947,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" @@ -1802,9 +2028,9 @@ checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[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 = "petgraph" @@ -1813,7 +2039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.3", ] [[package]] @@ -1825,6 +2051,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" @@ -1866,20 +2098,30 @@ dependencies = [ "termtree", ] +[[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.59" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1896,6 +2138,7 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", "rand_chacha", "rand_core", ] @@ -1915,6 +2158,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "rawpointer" @@ -1932,6 +2178,16 @@ dependencies = [ "rayon-core", ] +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + [[package]] name = "rayon-core" version = "1.11.0" @@ -1972,15 +2228,9 @@ checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.2", + "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.2" @@ -1995,13 +2245,12 @@ checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698" [[package]] name = "rfc6979" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "crypto-bigint 0.4.9", "hmac", - "zeroize", + "subtle", ] [[package]] @@ -2052,7 +2301,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", @@ -2083,6 +2332,31 @@ 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" @@ -2097,31 +2371,33 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] -name = "serde_bytes" -version = "0.11.9" +name = "serde_derive" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ - "serde", + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] -name = "serde_derive" -version = "1.0.163" +name = "serde_derive_internals" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 1.0.109", ] [[package]] @@ -2135,11 +2411,20 @@ dependencies = [ "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.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -2230,20 +2515,20 @@ dependencies = [ "cairo-lang-sierra-to-casm", "cairo-lang-starknet", "cairo-lang-syntax", + "cairo-lang-test-runner", "cairo-lang-utils", - "cairo-vm", "clap", "colored", "console", "glob", "home", "indicatif", - "itertools", + "itertools 0.10.5", "notify", "num-bigint", "num-traits 0.2.15", "predicates", - "rayon", + "rayon 0.9.0", "regex", "salsa", "serde", @@ -2255,11 +2540,11 @@ dependencies = [ [[package]] name = "starknet-crypto" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8802a516a2556b2ddb9630898d2c8387d928a3e603799b8b2a7dc4018b852c8f" +checksum = "693e6362f150f9276e429a910481fb7f3bcb8d6aa643743f587cfece0b374874" dependencies = [ - "crypto-bigint 0.4.9", + "crypto-bigint", "hex", "hmac", "num-bigint", @@ -2268,7 +2553,7 @@ dependencies = [ "rfc6979", "sha2", "starknet-crypto-codegen", - "starknet-curve 0.2.1", + "starknet-curve", "starknet-ff", "zeroize", ] @@ -2279,18 +2564,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6dc88f1f470d9de1001ffbb90d2344c9dd1a615f5467daf0574e2975dfd9ebd" dependencies = [ - "starknet-curve 0.3.0", - "starknet-ff", - "syn 2.0.18", -] - -[[package]] -name = "starknet-curve" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0dbde7ef14d54c2117bc6d2efb68c2383005f1cd749b277c11df874d07b7af" -dependencies = [ + "starknet-curve", "starknet-ff", + "syn 2.0.29", ] [[package]] @@ -2309,7 +2585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f5e4d14a7e5a93027baa42f514459acd1e07799f886604d8bf5d30a0d28111f" dependencies = [ "ark-ff", - "crypto-bigint 0.5.2", + "crypto-bigint", "getrandom", "hex", ] @@ -2352,9 +2628,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -2378,15 +2654,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" version = "0.4.1" @@ -2410,7 +2677,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.29", ] [[package]] @@ -2434,59 +2701,55 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.21" +name = "tiny-keccak" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +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.9" +name = "toml" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "time-core", + "serde", ] [[package]] -name = "tiny-keccak" -version = "2.0.2" +name = "toml" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ - "crunchy", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", ] [[package]] -name = "toml" -version = "0.4.10" +name = "toml_datetime" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] -name = "toml" -version = "0.5.11" +name = "toml_edit" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ + "indexmap 2.0.0", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -2577,7 +2840,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -2599,7 +2862,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2785,6 +3048,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +dependencies = [ + "memchr", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -2836,5 +3108,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.29", ] diff --git a/Cargo.toml b/Cargo.toml index b882d3302..8fd6d92d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,33 +21,33 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.81" home = "0.5.3" glob = "0.3.0" +cairo-felt = "0.8.2" # Cairo runner dependencies -cairo-lang-runner = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-compiler = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-casm = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-diagnostics = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-debug = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-defs = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-sierra = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-sierra-ap-change = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-sierra-gas = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-sierra-generator = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-semantic = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-sierra-to-casm = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-utils = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-filesystem = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-starknet = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-syntax = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-plugins = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} -cairo-lang-lowering = { git = "https://github.com/starkware-libs/cairo",rev = "1003d5d14c09191bbfb64ee318d1975584b3c819"} +cairo-lang-runner = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-test-runner = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-compiler = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-casm = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-diagnostics = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-debug = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-defs = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-sierra = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-sierra-ap-change = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-sierra-gas = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-sierra-generator = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-semantic = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-sierra-to-casm = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-utils = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-filesystem = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-starknet = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-syntax = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-plugins = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} +cairo-lang-lowering = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} anyhow = "1.0.66" ark-ff = "0.4.0-alpha.7" ark-std = "0.3.0" -cairo-felt = "0.3.0-rc1" -cairo-vm = "0.3.0-rc1" clap = { version = "4.0", features = ["derive"] } itertools = "0.10.3" num-bigint = "0.4" diff --git a/Scarb.toml b/Scarb.toml index 428277d2e..56de6143f 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -5,3 +5,6 @@ version = "0.1.0" # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest [dependencies] +starknet = ">=2.1.0" + +[[target.starknet-contract]] \ No newline at end of file diff --git a/corelib/Scarb.toml b/corelib/Scarb.toml index 28feb194a..06d283665 100644 --- a/corelib/Scarb.toml +++ b/corelib/Scarb.toml @@ -1,6 +1,6 @@ [package] name = "core" -version = "1.1.0" +version = "2.1.1" # 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 71b9794bf..0b1a2edaa 100644 --- a/corelib/src/array.cairo +++ b/corelib/src/array.cairo @@ -3,11 +3,15 @@ use traits::IndexView; use box::BoxTrait; use gas::withdraw_gas; use option::OptionTrait; +use serde::Serde; +#[derive(Drop)] 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 +23,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 +37,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,14 +68,52 @@ 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() } } -// Impls for common generic types -impl ArrayDrop> of Drop>; +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 = array![]; + 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) +} // Span. struct Span { @@ -83,15 +123,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 +145,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 +155,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 +197,7 @@ impl ArrayTCloneImpl, impl TDrop: Drop> of Clone { response.append(TClone::clone(v)); }, - Option::None(_) => { + Option::None => { break (); }, }; @@ -160,3 +205,37 @@ impl ArrayTCloneImpl, impl TDrop: Drop> of Clone> of PartialEq> { + fn eq(lhs: @Array, rhs: @Array) -> bool { + lhs.span() == rhs.span() + } + fn ne(lhs: @Array, rhs: @Array) -> bool { + !(lhs == rhs) + } +} + +impl SpanPartialEq> of PartialEq> { + fn eq(lhs: @Span, rhs: @Span) -> bool { + if (*lhs).len() != (*rhs).len() { + return false; + } + let mut lhs_span = *lhs; + let mut rhs_span = *rhs; + loop { + match lhs_span.pop_front() { + Option::Some(lhs_v) => { + if lhs_v != rhs_span.pop_front().unwrap() { + break false; + } + }, + Option::None => { + break true; + }, + }; + } + } + fn ne(lhs: @Span, rhs: @Span) -> bool { + !(lhs == rhs) + } +} diff --git a/corelib/src/box.cairo b/corelib/src/box.cairo index 7887fb3d6..83210965b 100644 --- a/corelib/src/box.cairo +++ b/corelib/src/box.cairo @@ -1,6 +1,5 @@ +#[derive(Copy, Drop)] extern type Box; -impl BoxTCopy> of Copy>; -impl BoxTDrop> of Drop>; // These functions are only exposed in the corelib through the trait below since calling them // directly with tuples panics due to auto unpacking of the tuple. @@ -8,11 +7,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/byte_array.cairo b/corelib/src/byte_array.cairo new file mode 100644 index 000000000..676f47afa --- /dev/null +++ b/corelib/src/byte_array.cairo @@ -0,0 +1,376 @@ +use array::{ArrayTrait, SpanTrait}; +use bytes_31::{ + BYTES_IN_BYTES31, Bytes31Trait, one_shift_left_bytes_felt252, one_shift_left_bytes_u128, + POW_2_128, POW_2_8, U128IntoBytes31, U8IntoBytes31 +}; +use clone::Clone; +use cmp::min; +use integer::{u128_safe_divmod, U32TryIntoNonZero}; +use option::OptionTrait; +use traits::{Into, TryInto}; +use zeroable::NonZeroIntoImpl; + +const BYTES_IN_U128: usize = 16; +// TODO(yuval): change to `BYTES_IN_BYTES31 - 1` once consteval_int supports non-literals. +const BYTES_IN_BYTES31_MINUS_ONE: usize = consteval_int!(31 - 1); + +// TODO(yuval): don't allow creation of invalid ByteArray? +#[derive(Drop, Clone, PartialEq)] +struct ByteArray { + // Full "words" of 31 bytes each. The first byte of each word in the byte array + // is the most significant byte in the word. + data: Array, + // This felt252 actually represents a bytes31, with < 31 bytes. + // It is represented as a felt252 to improve performance of building the byte array. + // The number of bytes in here is specified in `pending_word_len`. + // The first byte is the most significant byte among the `pending_word_len` bytes in the word. + pending_word: felt252, + // Should be in range [0, 30]. + pending_word_len: usize, +} + +impl ByteArrayDefault of Default { + fn default() -> ByteArray { + ByteArray { data: Default::default(), pending_word: 0, pending_word_len: 0 } + } +} + +#[generate_trait] +impl ByteArrayImpl of ByteArrayTrait { + // TODO(yuval): add a `new` function for initialization. + + // Appends a single word of `len` bytes to the end of the ByteArray. + // Note: this function assumes that: + // 1. `word` could be validly converted to a `bytes31` which has no more than `len` bytes + // of data. + // 2. len <= BYTES_IN_BYTES31. + // If these assumptions are not met, it can corrupt the ByteArray. Thus, this should be a + // private function. We could add masking/assertions but it would be more expensive. + fn append_word(ref self: ByteArray, word: felt252, len: usize) { + if len == 0 { + return; + } + let total_pending_bytes = self.pending_word_len + len; + + if total_pending_bytes < BYTES_IN_BYTES31 { + self.append_word_fits_into_pending(word, len); + return; + } + + if total_pending_bytes == BYTES_IN_BYTES31 { + self + .data + .append( + (word + self.pending_word * one_shift_left_bytes_felt252(len)) + .try_into() + .unwrap() + ); + self.pending_word = 0; + self.pending_word_len = 0; + return; + } + + // The split index is the number of bytes left for the next word (new pending_word of the + // modified ByteArray). + let split_index = total_pending_bytes - BYTES_IN_BYTES31; + if split_index == BYTES_IN_U128 { + self.append_split_index_16(word); + } else if split_index < BYTES_IN_U128 { + self.append_split_index_lt_16(word, split_index); + } else { // split_index > BYTES_IN_U128 + self.append_split_index_gt_16(word, split_index); + } + self.pending_word_len = split_index; + } + + // Appends a byte array to the end of `self`. + fn append(ref self: ByteArray, mut other: @ByteArray) { + let mut other_data = other.data.span(); + + if self.pending_word_len == 0 { + loop { + match other_data.pop_front() { + Option::Some(current_word) => { + self.data.append(*current_word); + }, + Option::None => { + break; + } + }; + }; + self.pending_word = *other.pending_word; + self.pending_word_len = *other.pending_word_len; + return; + } + + // self.pending_word_len is in [1, 30]. This is the split index for all the full words of + // `other`, as for each word, this is the number of bytes left for the next word. + if self.pending_word_len == BYTES_IN_U128 { + loop { + match other_data.pop_front() { + Option::Some(current_word) => { + self.append_split_index_16((*current_word).into()); + }, + Option::None => { + break; + } + }; + }; + } else if self.pending_word_len < BYTES_IN_U128 { + loop { + match other_data.pop_front() { + Option::Some(current_word) => { + self + .append_split_index_lt_16( + (*current_word).into(), self.pending_word_len + ); + }, + Option::None => { + break; + } + }; + }; + } else { + // self.pending_word_len > BYTES_IN_U128 + loop { + match other_data.pop_front() { + Option::Some(current_word) => { + self + .append_split_index_gt_16( + (*current_word).into(), self.pending_word_len + ); + }, + Option::None => { + break; + } + }; + }; + } + + // Add the pending word of `other`. + self.append_word(*other.pending_word, *other.pending_word_len); + } + + // Concatenates two byte arrays and returns the result. + fn concat(left: @ByteArray, right: @ByteArray) -> ByteArray { + let mut result = left.clone(); + result.append(right); + result + } + + // Appends a single byte to the end of `self`. + fn append_byte(ref self: ByteArray, byte: u8) { + if self.pending_word_len == 0 { + self.pending_word = byte.into(); + self.pending_word_len = 1; + return; + } + + let new_pending = self.pending_word * POW_2_8.into() + byte.into(); + + if self.pending_word_len != BYTES_IN_BYTES31_MINUS_ONE { + self.pending_word = new_pending; + self.pending_word_len += 1; + return; + } + + // self.pending_word_len == 30 + self.data.append(new_pending.try_into().unwrap()); + self.pending_word = 0; + self.pending_word_len = 0; + } + + fn len(self: @ByteArray) -> usize { + self.data.len() * BYTES_IN_BYTES31.into() + (*self.pending_word_len).into() + } + + // Returns the byte at the given index, or None if the index is out of bounds. + fn at(self: @ByteArray, index: usize) -> Option { + let (word_index, index_in_word) = DivRem::div_rem( + index, BYTES_IN_BYTES31.try_into().unwrap() + ); + + let data_len = self.data.len(); + if word_index == data_len { + // Index is in pending word. + if index_in_word >= *self.pending_word_len { + return Option::None; + } + // index_in_word is from MSB, we need index from LSB. + let index_from_lsb = *self.pending_word_len - 1 - index_in_word; + let pending_bytes31: bytes31 = (*self.pending_word).try_into().unwrap(); + return Option::Some(pending_bytes31.at(index_from_lsb)); + } + + if word_index > data_len { + return Option::None; + } + + // index_in_word is from MSB, we need index from LSB. + let index_from_lsb = BYTES_IN_BYTES31 - 1 - index_in_word; + Option::Some(self.data.at(word_index).at(index_from_lsb)) + } + + /// Returns a ByteArray with the reverse order of `self`. + fn rev(self: @ByteArray) -> ByteArray { + let mut result = Default::default(); + + result.append_word_rev(*self.pending_word, *self.pending_word_len); + + let mut data = self.data.span(); + loop { + match data.pop_back() { + Option::Some(current_word) => { + result.append_word_rev((*current_word).into(), BYTES_IN_BYTES31); + }, + Option::None => { + break; + } + }; + }; + result + } + + /// Appends the reverse of the given word to the end `self`. + /// Assumptions: + /// 1. len < 31 + /// 2. word is validly convertible to bytes31 of length `len`. + fn append_word_rev(ref self: ByteArray, word: felt252, len: usize) { + let mut index = 0; + + let u256{low, high } = word.into(); + let low_part_limit = min(len, BYTES_IN_U128); + loop { + if index == low_part_limit { + break; + } + let curr_byte_as_u128 = (low / one_shift_left_bytes_u128(index)) % POW_2_8; + + self.append_byte(curr_byte_as_u128.try_into().unwrap()); + index += 1; + }; + if low_part_limit == BYTES_IN_U128 { + let mut index_in_high_part = 0; + let high_part_len = len - BYTES_IN_U128; + loop { + if index_in_high_part == high_part_len { + break; + } + let curr_byte_as_u128 = (high + / one_shift_left_bytes_u128(index_in_high_part)) % POW_2_8; + + self.append_byte(curr_byte_as_u128.try_into().unwrap()); + index_in_high_part += 1; + } + } + } + + // === Helpers === + + // Appends a single word of `len` bytes to the end of the ByteArray, assuming there + // is enough space in the pending word (`self.pending_word_len + len < BYTES_IN_BYTES31`). + // + // `word` is of type felt252 but actually represents a bytes31. + // It is represented as a felt252 to improve performance of building the byte array. + #[inline] + fn append_word_fits_into_pending(ref self: ByteArray, word: felt252, len: usize) { + if self.pending_word_len == 0 { + // len < BYTES_IN_BYTES31 + self.pending_word = word; + self.pending_word_len = len; + return; + } + + self.pending_word = word + self.pending_word * one_shift_left_bytes_felt252(len); + self.pending_word_len += len; + } + + // Appends a single word to the end of `self`, given that `0 < split_index < 16`. + // + // `split_index` is the number of bytes left in `self.pending_word` after this function. This is + // the index of the split (LSB's index is 0). + // + // Note: this function doesn't update the new pending length of self. It's the caller's + // responsibility. + #[inline] + fn append_split_index_lt_16(ref self: ByteArray, word: felt252, split_index: usize) { + let u256{low, high } = word.into(); + + let (low_quotient, low_remainder) = u128_safe_divmod( + low, one_shift_left_bytes_u128(split_index).try_into().unwrap() + ); + let left = high.into() * one_shift_left_bytes_u128(BYTES_IN_U128 - split_index).into() + + low_quotient.into(); + + self.append_split(left, low_remainder.into()); + } + + // Appends a single word to the end of `self`, given that the index of splitting `word` is + // exactly 16. + // + // `split_index` is the number of bytes left in `self.pending_word` after this function. This is + // the index of the split (LSB's index is 0). + // + // Note: this function doesn't update the new pending length of self. It's the caller's + // responsibility. + #[inline] + fn append_split_index_16(ref self: ByteArray, word: felt252) { + let u256{low, high } = word.into(); + self.append_split(high.into(), low.into()); + } + + // Appends a single word to the end of `self`, given that the index of splitting `word` is > 16. + // + // `split_index` is the number of bytes left in `self.pending_word` after this function. This is + // the index of the split (LSB's index is 0). + // + // Note: this function doesn't update the new pending length of self. It's the caller's + // responsibility. + #[inline] + fn append_split_index_gt_16(ref self: ByteArray, word: felt252, split_index: usize) { + let u256{low, high } = word.into(); + + let (high_quotient, high_remainder) = u128_safe_divmod( + high, one_shift_left_bytes_u128(split_index - BYTES_IN_U128).try_into().unwrap() + ); + let right = high_remainder.into() * POW_2_128 + low.into(); + + self.append_split(high_quotient.into(), right); + } + + // A helper function to append a remainder to self, by: + // 1. completing `self.pending_word` to a full word using `complete_full_word`, assuming it's + // validly convertible to a `bytes31` of length exactly `BYTES_IN_BYTES31 - + // self.pending_word_len`. + // 2. Setting `self.pending_word` to `new_pending`. + // + // Note: this function doesn't update the new pending length of self. It's the caller's + // responsibility. + #[inline] + fn append_split(ref self: ByteArray, complete_full_word: felt252, new_pending: felt252) { + let to_append = complete_full_word + + self.pending_word + * one_shift_left_bytes_felt252(BYTES_IN_BYTES31 - self.pending_word_len); + self.data.append(to_append.try_into().unwrap()); + self.pending_word = new_pending; + } +} + +impl U128Add of Add { + #[inline] + fn add(lhs: ByteArray, rhs: ByteArray) -> ByteArray { + ByteArrayTrait::concat(@lhs, @rhs) + } +} +impl ByteArrayAddEq of AddEq { + #[inline] + fn add_eq(ref self: ByteArray, other: ByteArray) { + self.append(@other); + } +} + +impl ByteArrayIndexView of IndexView { + fn index(self: @ByteArray, index: usize) -> u8 { + self.at(index).expect('Index out of bounds') + } +} diff --git a/corelib/src/bytes_31.cairo b/corelib/src/bytes_31.cairo new file mode 100644 index 000000000..d4a913b26 --- /dev/null +++ b/corelib/src/bytes_31.cairo @@ -0,0 +1,201 @@ +use traits::{Into, TryInto}; +use option::OptionTrait; +use integer::{u256_from_felt252, u128_safe_divmod, u128_to_felt252}; + +const BYTES_IN_BYTES31: usize = 31; +const BYTES_IN_U128: usize = 16; +const POW_2_128: felt252 = 0x100000000000000000000000000000000; +const POW_2_8: u128 = 0x100; + +#[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; + +#[generate_trait] +impl Bytes31Impl of Bytes31Trait { + // Gets the byte at the given index (LSB's index is 0), assuming that + // `index < BYTES_IN_BYTES31`. If the assumption is not met, the behavior is undefined. + fn at(self: @bytes31, index: usize) -> u8 { + let u256{low, high } = (*self).into(); + let res_u128 = if index < BYTES_IN_U128 { + (low / one_shift_left_bytes_u128(index)) % POW_2_8 + } else { + (high / one_shift_left_bytes_u128(index - BYTES_IN_U128)) % POW_2_8 + }; + res_u128.try_into().unwrap() + } +} + +impl Bytes31IndexView of IndexView { + fn index(self: @bytes31, index: usize) -> u8 { + self.at(index) + } +} + +impl Bytes31IntoFelt252 of Into { + fn into(self: bytes31) -> felt252 { + bytes31_to_felt252(self) + } +} + +impl Bytes31IntoU256 of Into { + fn into(self: bytes31) -> u256 { + let as_felt: felt252 = self.into(); + as_felt.into() + } +} + +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() + } +} + +// Splits a bytes31 into two bytes31s at the given index (LSB's index is 0). +// The bytes31s are represented using felt252s to improve performance. +// Note: this function assumes that: +// 1. `word` is validly convertible to a bytes31 which has no more than `len` bytes of data. +// 2. index <= len. +// 3. len <= BYTES_IN_BYTES31. +// If these assumptions are not met, it can corrupt the ByteArray. Thus, this should be a +// private function. We could add masking/assertions but it would be more expansive. +fn split_bytes31(word: felt252, len: usize, index: usize) -> (felt252, felt252) { + if index == 0 { + return (0, word); + } + if index == len { + return (word, 0); + } + + let u256{low, high } = word.into(); + + if index == BYTES_IN_U128 { + return (low.into(), high.into()); + } + + if len <= BYTES_IN_U128 { + let (quotient, remainder) = u128_safe_divmod( + low, one_shift_left_bytes_u128(index).try_into().unwrap() + ); + return (remainder.into(), quotient.into()); + } + + // len > BYTES_IN_U128 + if index < BYTES_IN_U128 { + let (low_quotient, low_remainder) = u128_safe_divmod( + low, one_shift_left_bytes_u128(index).try_into().unwrap() + ); + let right = high.into() * one_shift_left_bytes_u128(BYTES_IN_U128 - index).into() + + low_quotient.into(); + return (low_remainder.into(), right); + } + + // len > BYTES_IN_U128 && index > BYTES_IN_U128 + let (high_quotient, high_remainder) = u128_safe_divmod( + high, one_shift_left_bytes_u128(index - BYTES_IN_U128).try_into().unwrap() + ); + let left = high_remainder.into() * POW_2_128 + low.into(); + return (left, high_quotient.into()); +} + + +// Returns 1 << (8 * `n_bytes`) as felt252, assuming that `n_bytes < BYTES_IN_BYTES31`. +// +// Note: if `n_bytes >= BYTES_IN_BYTES31`, the behavior is undefined. If one wants to assert that in +// the callsite, it's sufficient to assert that `n_bytes != BYTES_IN_BYTES31` because if +// `n_bytes > 31` then `n_bytes - 16 > 15` and `one_shift_left_bytes_u128` would panic. +fn one_shift_left_bytes_felt252(n_bytes: usize) -> felt252 { + if n_bytes < BYTES_IN_U128 { + one_shift_left_bytes_u128(n_bytes).into() + } else { + one_shift_left_bytes_u128(n_bytes - BYTES_IN_U128).into() * POW_2_128 + } +} + +// Returns 1 << (8 * `n_bytes`) as u128, where `n_bytes` must be < BYTES_IN_U128. +// +// Panics if `n_bytes >= BYTES_IN_U128`. +fn one_shift_left_bytes_u128(n_bytes: usize) -> u128 { + // TODO(yuval): change to match once it's supported for integers. + if n_bytes == 0 { + 0x1_u128 + } else if n_bytes == 1 { + 0x100_u128 + } else if n_bytes == 2 { + 0x10000_u128 + } else if n_bytes == 3 { + 0x1000000_u128 + } else if n_bytes == 4 { + 0x100000000_u128 + } else if n_bytes == 5 { + 0x10000000000_u128 + } else if n_bytes == 6 { + 0x1000000000000_u128 + } else if n_bytes == 7 { + 0x100000000000000_u128 + } else if n_bytes == 8 { + 0x10000000000000000_u128 + } else if n_bytes == 9 { + 0x1000000000000000000_u128 + } else if n_bytes == 10 { + 0x100000000000000000000_u128 + } else if n_bytes == 11 { + 0x10000000000000000000000_u128 + } else if n_bytes == 12 { + 0x1000000000000000000000000_u128 + } else if n_bytes == 13 { + 0x100000000000000000000000000_u128 + } else if n_bytes == 14 { + 0x10000000000000000000000000000_u128 + } else if n_bytes == 15 { + 0x1000000000000000000000000000000_u128 + } else { + panic_with_felt252('n_bytes too big') + } +} + +impl Bytes31PartialEq of PartialEq { + fn eq(lhs: @bytes31, rhs: @bytes31) -> bool { + let lhs_as_felt252: felt252 = (*lhs).into(); + let rhs_as_felt252: felt252 = (*rhs).into(); + lhs_as_felt252 == rhs_as_felt252 + } + fn ne(lhs: @bytes31, rhs: @bytes31) -> bool { + !(lhs == rhs) + } +} diff --git a/corelib/src/debug.cairo b/corelib/src/debug.cairo index 8ef0b3e58..e5ce405b7 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 = array![]; // 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 588d2a695..ba6f2948b 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 8362f1f30..46c4da940 100644 --- a/corelib/src/ec.cairo +++ b/corelib/src/ec.cairo @@ -2,10 +2,10 @@ //! curve. use array::ArrayTrait; +use traits::{Into, TryInto}; 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`. @@ -26,45 +26,26 @@ type NonZeroEcPoint = NonZero; /// Returns the zero point of the curve ("the point at infinity"). extern fn ec_point_zero() -> EcPoint nopanic; /// Constructs a non-zero point from its (x, y) coordinates. -/// -/// * `ec_point_try_new_nz` returns `None` if the point (x, y) is not on the curve. -/// * `ec_point_new_nz` panics in that case. -#[panic_with('not on EC', ec_point_new_nz)] +/// Returns `None` if the point (x, y) is not on the curve. extern fn ec_point_try_new_nz(x: felt252, y: felt252) -> Option nopanic; - -#[inline(always)] -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(()), - } -} - -fn ec_point_new(x: felt252, y: felt252) -> EcPoint { - ec_point_new_nz(:x, :y).into() -} - +/// Constructs a non-zero point from its x coordinate. +/// Returns `None` if no point of form (x, _) is on the curve. extern fn ec_point_from_x_nz(x: felt252) -> Option implicits(RangeCheck) nopanic; - -#[inline(always)] -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(()), - } -} - +/// Unwraps a non-zero point into its (x, y) coordinates. extern fn ec_point_unwrap(p: NonZeroEcPoint) -> (felt252, felt252) nopanic; /// Computes the negation of an elliptic curve point (-p). extern fn ec_neg(p: EcPoint) -> EcPoint nopanic; /// Checks whether the given `EcPoint` is the zero point. 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::NonZero(p_nz) => p_nz, +/// Converts `EcPoint` to `NonZeroEcPoint`. +impl EcPointTryIntoNonZero of TryInto { + #[inline(always)] + fn try_into(self: EcPoint) -> Option { + match ec_point_is_zero(self) { + IsZeroResult::Zero => Option::None, + IsZeroResult::NonZero(p_nz) => Option::Some(p_nz), + } } } @@ -76,55 +57,105 @@ extern type EcState; /// Initializes an EC computation with the zero point. extern fn ec_state_init() -> EcState nopanic; + /// Adds a point to the computation. extern fn ec_state_add(ref s: EcState, p: NonZeroEcPoint) nopanic; +/// Adds the product p * scalar to the state. +extern fn ec_state_add_mul( + ref s: EcState, scalar: felt252, p: NonZeroEcPoint +) implicits(EcOp) nopanic; /// Finalizes the EC computation and returns the result (returns `None` if the result is the /// zero point). extern fn ec_state_try_finalize_nz(s: EcState) -> Option nopanic; -/// Adds the product p * m to the state. -extern fn ec_state_add_mul(ref s: EcState, m: felt252, p: NonZeroEcPoint) implicits(EcOp) nopanic; - -/// Finalizes the EC computation and returns the result. -#[inline(always)] -fn ec_state_finalize(s: EcState) -> EcPoint { - match ec_state_try_finalize_nz(s) { - Option::Some(pt) => pt.into(), - Option::None(()) => ec_point_zero(), + +#[generate_trait] +impl EcStateImpl of EcStateTrait { + /// Initializes an EC computation with the zero point. + fn init() -> EcState { + ec_state_init() + } + /// Adds a point to the computation. + #[inline(always)] + fn add(ref self: EcState, p: NonZeroEcPoint) { + ec_state_add(ref self, :p); + } + /// Adds the product p * scalar to the state. + #[inline(always)] + fn add_mul(ref self: EcState, scalar: felt252, p: NonZeroEcPoint) { + ec_state_add_mul(ref self, :scalar, :p); + } + /// Finalizes the EC computation and returns the result (returns `None` if the result is the + /// zero point). + #[inline(always)] + fn finalize_nz(self: EcState) -> Option { + ec_state_try_finalize_nz(self) + } + /// Finalizes the EC computation and returns the result. + #[inline(always)] + fn finalize(self: EcState) -> EcPoint { + match self.finalize_nz() { + Option::Some(p_nz) => p_nz.into(), + 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::NonZero(p_nz) => { - let mut state = ec_state_init(); - ec_state_add_mul(ref state, m, p_nz); - ec_state_finalize(state) +#[generate_trait] +impl EcPointImpl of EcPointTrait { + /// Creates a new EC point from its (x, y) coordinates. + #[inline(always)] + fn new(x: felt252, y: felt252) -> Option { + Option::Some(ec_point_try_new_nz(:x, :y)?.into()) + } + /// Creates a new EC point from its x coordinate. + #[inline(always)] + fn new_from_x(x: felt252) -> Option { + Option::Some(ec_point_from_x_nz(:x)?.into()) + } + /// Returns the coordinates of the EC point. + #[inline(always)] + fn coordinates(self: NonZeroEcPoint) -> (felt252, felt252) { + ec_point_unwrap(self) + } + /// Computes the product of an EC point `p` by the given scalar `scalar`. + fn mul(self: EcPoint, scalar: felt252) -> EcPoint { + match self.try_into() { + Option::Some(self_nz) => { + let mut state = EcStateTrait::init(); + state.add_mul(scalar, self_nz); + state.finalize() + }, + Option::None => self, } } } +impl EcPointNeg of Neg { + fn neg(a: EcPoint) -> EcPoint { + ec_neg(a) + } +} + impl EcPointAdd of Add { /// Computes the sum of two points on the curve. // 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(()) => { + let lhs_nz = match lhs.try_into() { + Option::Some(pt) => pt, + Option::None => { return rhs; }, - IsZeroResult::NonZero(pt) => pt, }; - let rhs_nz = match ec_point_is_zero(rhs) { - IsZeroResult::Zero(()) => { + let rhs_nz = match rhs.try_into() { + Option::Some(pt) => pt, + Option::None => { return lhs; }, - IsZeroResult::NonZero(pt) => pt, }; let mut state = ec_state_init(); - ec_state_add(ref state, lhs_nz); - ec_state_add(ref state, rhs_nz); - ec_state_finalize(state) + state.add(lhs_nz); + state.add(rhs_nz); + state.finalize() } } @@ -138,15 +169,15 @@ impl EcPointAddEq of AddEq { 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(()) => { + match rhs.try_into() { + Option::Some(_) => {}, + Option::None => { // lhs - 0 = lhs. return lhs; }, - IsZeroResult::NonZero(_) => {}, }; // lhs - rhs = lhs + (-rhs). - lhs + ec_neg(rhs) + lhs + (-rhs) } } diff --git a/corelib/src/ecdsa.cairo b/corelib/src/ecdsa.cairo index cf87d568d..6037b307b 100644 --- a/corelib/src/ecdsa.cairo +++ b/corelib/src/ecdsa.cairo @@ -1,10 +1,7 @@ +use ec::EcPointTrait; +use option::OptionTrait; +use traits::{Into, TryInto}; use zeroable::IsZeroResult; -use ec::{ - ec_mul, ec_neg, ec_point_from_x, ec_point_from_x_nz, ec_point_is_zero, ec_point_new, - ec_point_new_nz, ec_point_non_zero, ec_point_try_new, ec_point_try_new_nz, ec_point_unwrap, - ec_point_zero, ec_state_add_mul, ec_state_add, ec_state_finalize, ec_state_init, - ec_state_try_finalize_nz -}; // Checks if (`signature_r`, `signature_s`) is a valid ECDSA signature for the given `public_key` // on the given `message`. @@ -13,7 +10,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. @@ -27,40 +24,39 @@ use ec::{ fn check_ecdsa_signature( message_hash: felt252, public_key: felt252, signature_r: felt252, signature_s: felt252 ) -> bool { - // TODO(lior): Change to || once short circuiting is supported. - - // Check that s != 0 (mod StarkCurve.ORDER). - if (signature_s == 0) { + // TODO(orizi): Change to || once it does not prevent `a == 0` comparison optimization. + // 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) { + let public_key_point = match ec::EcPointTrait::new_from_x(public_key) { Option::Some(point) => point, - Option::None(()) => { + Option::None => { return false; }, }; // Check that `r` is the x coordinate of a point on the curve and get such a point. // Note that this ensures that `r != 0`. - let signature_r_point = match ec::ec_point_from_x(signature_r) { + let signature_r_point = match EcPointTrait::new_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 EcPointTrait::new(ec::stark_curve::GEN_X, ec::stark_curve::GEN_Y) { Option::Some(point) => point, - Option::None(()) => { + Option::None => { return false; }, }; @@ -72,39 +68,79 @@ fn check_ecdsa_signature( // and check that: // zG +/- rQ = +/- sR, or more efficiently that: // (zG +/- rQ).x = sR.x. - - let sR: EcPoint = ec_mul(signature_r_point, signature_s); - let sR_x = match ec_point_is_zero(sR) { - IsZeroResult::Zero(()) => { - return false; - }, - IsZeroResult::NonZero(pt) => { - let (x, y) = ec_point_unwrap(pt); + let sR: EcPoint = signature_r_point.mul(signature_s); + let sR_x = match sR.try_into() { + Option::Some(pt) => { + let (x, _) = ec::ec_point_unwrap(pt); x }, + Option::None => { + return false; + }, }; - 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::NonZero(pt) => { - let (x, y) = ec_point_unwrap(pt); + let zG: EcPoint = gen_point.mul(message_hash); + let rQ: EcPoint = public_key_point.mul(signature_r); + match (zG + rQ).try_into() { + Option::Some(pt) => { + let (x, _) = ec::ec_point_unwrap(pt); if (x == sR_x) { return true; } }, + Option::None => {}, }; - match ec_point_is_zero(zG - rQ) { - IsZeroResult::Zero(()) => {}, - IsZeroResult::NonZero(pt) => { - let (x, y) = ec_point_unwrap(pt); + match (zG - rQ).try_into() { + Option::Some(pt) => { + let (x, _) = ec::ec_point_unwrap(pt); if (x == sR_x) { return true; } }, + Option::None => {}, }; return false; } + +/// Receives a signature and the signed message hash. +/// Returns the public key associated with the signer. +fn recover_public_key( + message_hash: felt252, signature_r: felt252, signature_s: felt252, y_parity: bool +) -> Option { + let mut signature_r_point = EcPointTrait::new_from_x(signature_r)?; + let (_, y) = signature_r_point.try_into()?.coordinates(); + let y: u256 = y.into(); + // If the actual the parity of the actual y is different than requested, flip the parity. + if (y.low & 1 == 1) != y_parity { + signature_r_point = -signature_r_point; + } + + // Retrieve the generator point. + let gen_point = EcPointTrait::new(ec::stark_curve::GEN_X, ec::stark_curve::GEN_Y)?; + + // a Valid signature should satisfy: + // zG + rQ = sR. + // Where: + // zG = z * G, z is the message and G is a generator of the EC. + // rQ = r * Q, Q.x = public_key. + // sR = s * R, where R.x = r and R.y is determined by y_parity. + // + // Hence: + // rQ = sR - zG. + // Q = (s/r)R - (z/r)G + // and we can recover the public key using: + // Q.x = ((s/r)R - (z/r)G).x. + let r_nz: u256 = signature_r.into(); + let r_nz = r_nz.try_into()?; + let ord_nz: u256 = ec::stark_curve::ORDER.into(); + let ord_nz = ord_nz.try_into()?; + let r_inv = math::inv_mod(r_nz, ord_nz)?; + let s_div_r: felt252 = math::u256_mul_mod_n(signature_s.into(), r_inv, ord_nz).try_into()?; + let z_div_r: felt252 = math::u256_mul_mod_n(message_hash.into(), r_inv, ord_nz).try_into()?; + let s_div_rR: EcPoint = signature_r_point.mul(s_div_r); + let z_div_rG: EcPoint = gen_point.mul(z_div_r); + let (x, _) = (s_div_rR - z_div_rG).try_into()?.coordinates(); + Option::Some(x) +} diff --git a/corelib/src/hash.cairo b/corelib/src/hash.cairo index 0bde0914a..c9b7790f9 100644 --- a/corelib/src/hash.cairo +++ b/corelib/src/hash.cairo @@ -1,143 +1,200 @@ use traits::Into; use starknet::ContractAddress; -extern type Pedersen; +/// A trait for hash state accumulators. +trait HashStateTrait { + fn update(self: S, value: felt252) -> S; + fn finalize(self: S) -> felt252; +} -extern fn pedersen(a: felt252, b: felt252) -> felt252 implicits(Pedersen) nopanic; +/// A trait for values that can be hashed. +trait Hash> { + /// Updates the hash state with the given value. + fn update_state(state: S, value: T) -> S; +} +/// Trait for hashing values. +/// Used for backwards compatibility. +/// NOTE: Implement `Hash` instead of this trait if possible. trait LegacyHash { fn hash(state: felt252, value: T) -> felt252; } -impl LegacyHashFelt252 of LegacyHash { - fn hash(state: felt252, value: felt252) -> felt252 { - pedersen(state, value) +/// Implementation of `LegacyHash` for types that have `Hash` for backwards compatibility. +impl LegacyHashForHash< + T, impl THash: Hash +> of LegacyHash { + #[inline(always)] + fn hash(state: felt252, value: T) -> felt252 { + THash::update_state(pedersen::HashState { state }, value).state + } +} + +/// Extension trait for hash state accumulators. +trait HashStateExTrait { + /// Updates the hash state with the given value. + fn update_with(self: S, value: T) -> S; +} + +impl HashStateEx< + S, impl SHashState: HashStateTrait, T, impl THash: Hash +> of HashStateExTrait { + #[inline(always)] + fn update_with(self: S, value: T) -> S { + THash::update_state(self, value) } } -impl LegacyHashBool of LegacyHash { - fn hash(state: felt252, value: bool) -> felt252 { - LegacyHash::::hash(state, if value { - 1 - } else { - 0 - }) +impl HashFelt252> of Hash { + #[inline(always)] + fn update_state(state: S, value: felt252) -> S { + state.update(value) } } -impl LegacyHashU8 of LegacyHash { - fn hash(state: felt252, value: u8) -> felt252 { - LegacyHash::::hash(state, value.into()) +impl HashBool< + S, impl SHashState: HashStateTrait, impl SDrop: Drop +> of Hash { + #[inline(always)] + fn update_state(state: S, value: bool) -> S { + state.update(value.into()) } } -impl LegacyHashU16 of LegacyHash { - fn hash(state: felt252, value: u16) -> felt252 { - LegacyHash::::hash(state, value.into()) +impl HashU8, impl SDrop: Drop> of Hash { + #[inline(always)] + fn update_state(state: S, value: u8) -> S { + state.update(value.into()) } } -impl LegacyHashU32 of LegacyHash { - fn hash(state: felt252, value: u32) -> felt252 { - LegacyHash::::hash(state, value.into()) +impl HashU16< + S, impl SHashState: HashStateTrait, impl SDrop: Drop +> of Hash { + #[inline(always)] + fn update_state(state: S, value: u16) -> S { + state.update(value.into()) } } -impl LegacyHashU64 of LegacyHash { - fn hash(state: felt252, value: u64) -> felt252 { - LegacyHash::::hash(state, value.into()) +impl HashU32< + S, impl SHashState: HashStateTrait, impl SDrop: Drop +> of Hash { + #[inline(always)] + fn update_state(state: S, value: u32) -> S { + state.update(value.into()) } } -impl LegacyHashU128 of LegacyHash { - fn hash(state: felt252, value: u128) -> felt252 { - LegacyHash::::hash(state, value.into()) +impl HashU64< + S, impl SHashState: HashStateTrait, impl SDrop: Drop +> of Hash { + #[inline(always)] + fn update_state(state: S, value: u64) -> S { + state.update(value.into()) } } -impl LegacyHashU256 of LegacyHash { - fn hash(state: felt252, value: u256) -> felt252 { - let state = LegacyHash::::hash(state, value.low); - LegacyHash::::hash(state, value.high) +impl HashU128< + S, impl SHashState: HashStateTrait, impl SDrop: Drop +> of Hash { + #[inline(always)] + fn update_state(state: S, value: u128) -> S { + state.update(value.into()) } } -impl LegacyHashContractAddress of LegacyHash { - fn hash(state: felt252, value: starknet::ContractAddress) -> felt252 { - LegacyHash::::hash(state, value.into()) +impl HashU256< + S, impl SHashState: HashStateTrait, impl SDrop: Drop +> of Hash { + #[inline(always)] + fn update_state(state: S, value: u256) -> S { + state.update_with(value.low).update_with(value.high) } } -impl TupleSize0LegacyHash of LegacyHash<()> { - fn hash(state: felt252, value: ()) -> felt252 { +impl HashContractAddress< + S, impl SHashState: HashStateTrait, impl SDrop: Drop +> of Hash { + #[inline(always)] + fn update_state(state: S, value: ContractAddress) -> S { + state.update(value.into()) + } +} + +impl TupleSize0Hash> of Hash<(), S, SHashState> { + #[inline(always)] + fn update_state(state: S, value: ()) -> S { state } } -impl TupleSize1LegacyHash> of LegacyHash<(E0, )> { - fn hash(state: felt252, value: (E0, )) -> felt252 { - let (e0, ) = value; - E0LegacyHash::hash(state, e0) +impl TupleSize1Hash< + E0, S, impl E0Hash: Hash, impl SHashState: HashStateTrait +> of Hash<(E0,), S, SHashState> { + #[inline(always)] + fn update_state(state: S, value: (E0,)) -> S { + let (e0,) = value; + state.update_with(e0) } } -impl TupleSize2LegacyHash< +impl TupleSize2Hash< E0, E1, - impl E0LegacyHash: LegacyHash, - impl E1LegacyHash: LegacyHash, + S, + impl SHashState: HashStateTrait, + impl E0Hash: Hash, + impl E1Hash: Hash, impl E0Drop: Drop, - impl E1Drop: Drop -> of LegacyHash<(E0, E1)> { - fn hash(state: felt252, value: (E0, E1, )) -> felt252 { + impl E1Drop: Drop, +> of Hash<(E0, E1), S, SHashState> { + #[inline(always)] + fn update_state(state: S, value: (E0, E1,)) -> S { let (e0, e1) = value; - let state = E0LegacyHash::hash(state, e0); - E1LegacyHash::hash(state, e1) + state.update_with(e0).update_with(e1) } } -impl TupleSize3LegacyHash< +impl TupleSize3Hash< E0, E1, E2, - impl E0LegacyHash: LegacyHash, - impl E1LegacyHash: LegacyHash, - impl E2LegacyHash: LegacyHash, + S, + impl SHashState: HashStateTrait, + impl E0Hash: Hash, + impl E1Hash: Hash, + impl E2Hash: Hash, impl E0Drop: Drop, impl E1Drop: Drop, impl E2Drop: Drop, -> of LegacyHash<(E0, E1, E2)> { - fn hash(state: felt252, value: (E0, E1, E2)) -> felt252 { +> of Hash<(E0, E1, E2), S, SHashState> { + #[inline(always)] + fn update_state(state: S, value: (E0, E1, E2)) -> S { let (e0, e1, e2) = value; - let state = E0LegacyHash::hash(state, e0); - let state = E1LegacyHash::hash(state, e1); - E2LegacyHash::hash(state, e2) + state.update_with(e0).update_with(e1).update_with(e2) } } -impl TupleSize4LegacyHash< +impl TupleSize4Hash< E0, E1, E2, E3, - impl E0LegacyHash: LegacyHash, - impl E1LegacyHash: LegacyHash, - impl E2LegacyHash: LegacyHash, - impl E3LegacyHash: LegacyHash, + S, + impl SHashState: HashStateTrait, + impl E0Hash: Hash, + impl E1Hash: Hash, + impl E2Hash: Hash, + impl E3Hash: Hash, impl E0Drop: Drop, impl E1Drop: Drop, impl E2Drop: Drop, impl E3Drop: Drop, -> of LegacyHash<(E0, E1, E2, E3)> { - fn hash(state: felt252, value: (E0, E1, E2, E3)) -> felt252 { +> of Hash<(E0, E1, E2, E3), S, SHashState> { + #[inline(always)] + fn update_state(state: S, value: (E0, E1, E2, E3)) -> S { let (e0, e1, e2, e3) = value; - let state = E0LegacyHash::hash(state, e0); - let state = E1LegacyHash::hash(state, e1); - let state = E2LegacyHash::hash(state, e2); - E3LegacyHash::hash(state, e3) + state.update_with(e0).update_with(e1).update_with(e2).update_with(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 9172c69fa..48acbefd7 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,56 @@ impl U256TryIntoFelt252 of TryInto { ) } } +impl Felt252TryIntoI8 of TryInto { + fn try_into(self: felt252) -> Option { + i8_try_from_felt252(self) + } +} +impl I8IntoFelt252 of Into { + fn into(self: i8) -> felt252 { + i8_to_felt252(self) + } +} +impl Felt252TryIntoI16 of TryInto { + fn try_into(self: felt252) -> Option { + i16_try_from_felt252(self) + } +} +impl I16IntoFelt252 of Into { + fn into(self: i16) -> felt252 { + i16_to_felt252(self) + } +} +impl Felt252TryIntoI32 of TryInto { + fn try_into(self: felt252) -> Option { + i32_try_from_felt252(self) + } +} +impl I32IntoFelt252 of Into { + fn into(self: i32) -> felt252 { + i32_to_felt252(self) + } +} +impl Felt252TryIntoI64 of TryInto { + fn try_into(self: felt252) -> Option { + i64_try_from_felt252(self) + } +} +impl I64IntoFelt252 of Into { + fn into(self: i64) -> felt252 { + i64_to_felt252(self) + } +} +impl Felt252TryIntoI128 of TryInto { + fn try_into(self: felt252) -> Option { + i128_try_from_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 +1564,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 +1673,682 @@ 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) +enum SignedIntegerResult { + InRange: T, + Underflow: T, + Overflow: T, +} +impl SignedIntegerResultDrop> of Drop>; + +#[derive(Copy, Drop)] +extern type i8; +impl NumericLiterali8 of NumericLiteral; +extern fn i8_const() -> i8 nopanic; +extern fn i8_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) 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) +extern fn i8_overflowing_add_impl( + lhs: i8, rhs: i8 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i8_overflowing_sub_impl( + lhs: i8, rhs: i8 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I8Add of Add { + fn add(lhs: i8, rhs: i8) -> i8 { + match i8_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i8_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i8_add Overflow'), + } + } +} +impl I8AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i8, other: i8) { + self = Add::add(self, other); + } +} +impl I8Sub of Sub { + fn sub(lhs: i8, rhs: i8) -> i8 { + match i8_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i8_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i8_sub Overflow'), + } + } +} +impl I8SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i8, other: i8) { + self = Sub::sub(self, other); + } +} + +impl I8Neg of Neg { + #[inline(always)] + fn neg(a: i8) -> i8 { + 0 - a + } +} + +extern fn i8_wide_mul(lhs: i8, rhs: i8) -> i16 implicits() nopanic; +impl I8Mul of Mul { + fn mul(lhs: i8, rhs: i8) -> i8 { + i8_try_from_felt252(i16_to_felt252(i8_wide_mul(lhs, rhs))).expect('i8_mul Overflow') + } +} +impl I8MulEq of MulEq { + #[inline(always)] + fn mul_eq(ref self: i8, other: i8) { + self = Mul::mul(self, other); + } +} + +/// If `lhs` >= `rhs` returns `Ok(lhs - rhs)` else returns `Err(2**8 + lhs - rhs)`. +extern fn i8_diff(lhs: i8, rhs: i8) -> Result implicits(RangeCheck) nopanic; +impl I8PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i8, rhs: i8) -> bool { + i8_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i8, rhs: i8) -> bool { + i8_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i8, rhs: i8) -> bool { + i8_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i8, rhs: i8) -> bool { + i8_diff(rhs, lhs).into_is_err() + } +} + +#[derive(Copy, Drop)] +extern type i16; +impl NumericLiterali16 of NumericLiteral; +extern fn i16_const() -> i16 nopanic; +extern fn i16_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) 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) + } +} + +extern fn i16_overflowing_add_impl( + lhs: i16, rhs: i16 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i16_overflowing_sub_impl( + lhs: i16, rhs: i16 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I16Add of Add { + fn add(lhs: i16, rhs: i16) -> i16 { + match i16_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i16_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i16_add Overflow'), + } + } +} +impl I16AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i16, other: i16) { + self = Add::add(self, other); + } +} +impl I16Sub of Sub { + fn sub(lhs: i16, rhs: i16) -> i16 { + match i16_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i16_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i16_sub Overflow'), + } + } +} +impl I16SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i16, other: i16) { + self = Sub::sub(self, other); + } +} + +impl I16Neg of Neg { + #[inline(always)] + fn neg(a: i16) -> i16 { + 0 - a + } +} + +extern fn i16_wide_mul(lhs: i16, rhs: i16) -> i32 implicits() nopanic; +impl I16Mul of Mul { + fn mul(lhs: i16, rhs: i16) -> i16 { + i16_try_from_felt252(i32_to_felt252(i16_wide_mul(lhs, rhs))).expect('i16_mul Overflow') + } +} +impl I16MulEq of MulEq { + #[inline(always)] + fn mul_eq(ref self: i16, other: i16) { + self = Mul::mul(self, other); + } +} + +/// If `lhs` >= `rhs` returns `Ok(lhs - rhs)` else returns `Err(2**16 + lhs - rhs)`. +extern fn i16_diff(lhs: i16, rhs: i16) -> Result implicits(RangeCheck) nopanic; +impl I16PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i16, rhs: i16) -> bool { + i16_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i16, rhs: i16) -> bool { + i16_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i16, rhs: i16) -> bool { + i16_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i16, rhs: i16) -> bool { + i16_diff(rhs, lhs).into_is_err() + } +} + +#[derive(Copy, Drop)] +extern type i32; +impl NumericLiterali32 of NumericLiteral; +extern fn i32_const() -> i32 nopanic; +extern fn i32_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) 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) + } +} + +extern fn i32_overflowing_add_impl( + lhs: i32, rhs: i32 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i32_overflowing_sub_impl( + lhs: i32, rhs: i32 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I32Add of Add { + fn add(lhs: i32, rhs: i32) -> i32 { + match i32_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i32_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i32_add Overflow'), + } + } +} +impl I32AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i32, other: i32) { + self = Add::add(self, other); + } +} +impl I32Sub of Sub { + fn sub(lhs: i32, rhs: i32) -> i32 { + match i32_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i32_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i32_sub Overflow'), + } + } +} +impl I32SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i32, other: i32) { + self = Sub::sub(self, other); + } +} + +impl I32Neg of Neg { + #[inline(always)] + fn neg(a: i32) -> i32 { + 0 - a + } +} + +extern fn i32_wide_mul(lhs: i32, rhs: i32) -> i64 implicits() nopanic; +impl I32Mul of Mul { + fn mul(lhs: i32, rhs: i32) -> i32 { + i32_try_from_felt252(i64_to_felt252(i32_wide_mul(lhs, rhs))).expect('i32_mul Overflow') + } +} +impl I32MulEq of MulEq { + #[inline(always)] + fn mul_eq(ref self: i32, other: i32) { + self = Mul::mul(self, other); + } +} + +/// If `lhs` >= `rhs` returns `Ok(lhs - rhs)` else returns `Err(2**32 + lhs - rhs)`. +extern fn i32_diff(lhs: i32, rhs: i32) -> Result implicits(RangeCheck) nopanic; +impl I32PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i32, rhs: i32) -> bool { + i32_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i32, rhs: i32) -> bool { + i32_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i32, rhs: i32) -> bool { + i32_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i32, rhs: i32) -> bool { + i32_diff(rhs, lhs).into_is_err() + } +} + +#[derive(Copy, Drop)] +extern type i64; +impl NumericLiterali64 of NumericLiteral; +extern fn i64_const() -> i64 nopanic; +extern fn i64_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) 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) + } +} + +extern fn i64_overflowing_add_impl( + lhs: i64, rhs: i64 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i64_overflowing_sub_impl( + lhs: i64, rhs: i64 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I64Add of Add { + fn add(lhs: i64, rhs: i64) -> i64 { + match i64_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i64_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i64_add Overflow'), + } + } +} +impl I64AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i64, other: i64) { + self = Add::add(self, other); + } +} +impl I64Sub of Sub { + fn sub(lhs: i64, rhs: i64) -> i64 { + match i64_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i64_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i64_sub Overflow'), + } + } +} +impl I64SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i64, other: i64) { + self = Sub::sub(self, other); + } +} + +impl I64Neg of Neg { + #[inline(always)] + fn neg(a: i64) -> i64 { + 0 - a + } +} + +extern fn i64_wide_mul(lhs: i64, rhs: i64) -> i128 implicits() nopanic; +impl I64Mul of Mul { + fn mul(lhs: i64, rhs: i64) -> i64 { + i64_try_from_felt252(i128_to_felt252(i64_wide_mul(lhs, rhs))).expect('i64_mul Overflow') + } +} +impl I64MulEq of MulEq { + #[inline(always)] + fn mul_eq(ref self: i64, other: i64) { + self = Mul::mul(self, other); + } +} + +/// If `lhs` >= `rhs` returns `Ok(lhs - rhs)` else returns `Err(2**64 + lhs - rhs)`. +extern fn i64_diff(lhs: i64, rhs: i64) -> Result implicits(RangeCheck) nopanic; +impl I64PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i64, rhs: i64) -> bool { + i64_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i64, rhs: i64) -> bool { + i64_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i64, rhs: i64) -> bool { + i64_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i64, rhs: i64) -> bool { + i64_diff(rhs, lhs).into_is_err() + } +} + +#[derive(Copy, Drop)] +extern type i128; +impl NumericLiterali128 of NumericLiteral; +extern fn i128_const() -> i128 nopanic; +extern fn i128_try_from_felt252(a: felt252) -> Option implicits(RangeCheck) 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) + } +} + +extern fn i128_overflowing_add_impl( + lhs: i128, rhs: i128 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +extern fn i128_overflowing_sub_impl( + lhs: i128, rhs: i128 +) -> SignedIntegerResult implicits(RangeCheck) nopanic; +impl I128Add of Add { + fn add(lhs: i128, rhs: i128) -> i128 { + match i128_overflowing_add_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i128_add Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i128_add Overflow'), + } + } +} +impl I128AddEq of AddEq { + #[inline(always)] + fn add_eq(ref self: i128, other: i128) { + self = Add::add(self, other); + } +} +impl I128Sub of Sub { + fn sub(lhs: i128, rhs: i128) -> i128 { + match i128_overflowing_sub_impl(lhs, rhs) { + SignedIntegerResult::InRange(result) => result, + SignedIntegerResult::Underflow(_) => panic_with_felt252('i128_sub Underflow'), + SignedIntegerResult::Overflow(_) => panic_with_felt252('i128_sub Overflow'), + } + } +} +impl I128SubEq of SubEq { + #[inline(always)] + fn sub_eq(ref self: i128, other: i128) { + self = Sub::sub(self, other); + } +} + +impl I128Neg of Neg { + #[inline(always)] + fn neg(a: i128) -> i128 { + 0 - a + } +} + +/// If `lhs` >= `rhs` returns `Ok(lhs - rhs)` else returns `Err(2**128 + lhs - rhs)`. +extern fn i128_diff(lhs: i128, rhs: i128) -> Result implicits(RangeCheck) nopanic; +impl I128PartialOrd of PartialOrd { + #[inline(always)] + fn le(lhs: i128, rhs: i128) -> bool { + i128_diff(rhs, lhs).into_is_ok() + } + #[inline(always)] + fn ge(lhs: i128, rhs: i128) -> bool { + i128_diff(lhs, rhs).into_is_ok() + } + #[inline(always)] + fn lt(lhs: i128, rhs: i128) -> bool { + i128_diff(lhs, rhs).into_is_err() + } + #[inline(always)] + fn gt(lhs: i128, rhs: i128) -> bool { + i128_diff(rhs, lhs).into_is_err() } } diff --git a/corelib/src/internal.cairo b/corelib/src/internal.cairo index b63ad5745..49a4ee6a5 100644 --- a/corelib/src/internal.cairo +++ b/corelib/src/internal.cairo @@ -1 +1,6 @@ extern fn revoke_ap_tracking() implicits() nopanic; + +/// Function to enforce that `Implicit` is used by a function calling it. +/// Note: This extern function is not mapped to a Sierra function, and all usages of it are removed +/// during compilation. +extern fn require_implicit() implicits(Implicit) nopanic; diff --git a/corelib/src/keccak.cairo b/corelib/src/keccak.cairo index 598bbce21..080fffc0e 100644 --- a/corelib/src/keccak.cairo +++ b/corelib/src/keccak.cairo @@ -1,9 +1,11 @@ -use array::{Span, ArrayTrait, SpanTrait, ArrayDrop}; +use array::{Span, ArrayTrait, SpanTrait}; 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 fb46dd11d..3cdab3347 100644 --- a/corelib/src/lib.cairo +++ b/corelib/src/lib.cairo @@ -1,64 +1,83 @@ mod traits; use traits::{ Add, AddEq, BitAnd, BitNot, BitOr, BitXor, Copy, Div, DivEq, DivRem, Drop, Mul, MulEq, - 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 + PartialEq, PartialOrd, Rem, RemEq, Sub, SubEq, TupleSize0Copy, TupleSize0Drop, Not, Neg, Into, + TryInto, Index, IndexView, Destruct, Default, Felt252DictValue, PanicDestruct }; +use serde::Serde; + +type usize = u32; #[derive(Copy, Drop)] enum bool { - False: (), - True: (), + False, + True, } -extern fn bool_and_impl(lhs: bool, rhs: bool) -> (bool, ) implicits() nopanic; +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; impl BoolBitAnd of BitAnd { #[inline(always)] fn bitand(lhs: bool, rhs: bool) -> bool { - let (r, ) = bool_and_impl(lhs, rhs); + let (r,) = bool_and_impl(lhs, rhs); r } } -extern fn bool_or_impl(lhs: bool, rhs: bool) -> (bool, ) implicits() nopanic; +extern fn bool_or_impl(lhs: bool, rhs: bool) -> (bool,) implicits() nopanic; impl BoolBitOr of BitOr { #[inline(always)] fn bitor(lhs: bool, rhs: bool) -> bool { - let (r, ) = bool_or_impl(lhs, rhs); + let (r,) = bool_or_impl(lhs, rhs); r } } -extern fn bool_not_impl(a: bool) -> (bool, ) implicits() nopanic; +extern fn bool_not_impl(a: bool) -> (bool,) implicits() nopanic; #[inline(always)] impl BoolNot of Not { #[inline(always)] fn not(a: bool) -> bool implicits() nopanic { - let (r, ) = bool_not_impl(a); + let (r,) = bool_not_impl(a); r } } -extern fn bool_xor_impl(lhs: bool, rhs: bool) -> (bool, ) implicits() nopanic; +extern fn bool_xor_impl(lhs: bool, rhs: bool) -> (bool,) implicits() nopanic; impl BoolBitXor of BitXor { #[inline(always)] fn bitxor(lhs: bool, rhs: bool) -> bool { - let (r, ) = bool_xor_impl(lhs, rhs); + let (r,) = bool_xor_impl(lhs, rhs); r } } -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 +90,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 +106,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 { @@ -127,7 +161,7 @@ extern fn felt252_mul(lhs: felt252, rhs: felt252) -> felt252 nopanic; impl Felt252Neg of Neg { #[inline(always)] fn neg(a: felt252) -> felt252 { - a * felt252_const::<-1>() + a * -1 } } @@ -135,20 +169,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 { @@ -173,16 +216,14 @@ use box::{Box, BoxTrait}; // Nullable mod nullable; -use nullable::{Nullable, match_nullable, null, nullable_from_box}; +use nullable::{Nullable, NullableTrait, match_nullable, null, nullable_from_box}; -// Arrays. +// Array. mod array; use array::{Array, ArrayTrait}; -type usize = u32; // Span. -use array::Span; - +use array::{Span, SpanTrait}; // Dictionary. mod dict; @@ -192,11 +233,11 @@ use dict::{ // Result. mod result; -use result::Result; +use result::{Result, ResultTrait}; // Option. mod option; -use option::Option; +use option::{Option, OptionTrait}; // Clone. mod clone; @@ -211,13 +252,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 +272,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)] @@ -252,10 +294,13 @@ mod serde; // Hash functions. mod hash; -use hash::{pedersen, Pedersen}; mod keccak; +// Pedersen +mod pedersen; +use pedersen::Pedersen; + // Poseidon mod poseidon; use poseidon::Poseidon; @@ -274,6 +319,17 @@ mod internal; mod zeroable; use zeroable::{Zeroable, NonZero}; +// bytes31. +mod bytes_31; +use bytes_31::{ + bytes31, bytes31_const, Bytes31IndexView, Bytes31IntoFelt252, Bytes31Trait, + Felt252TryIntoBytes31 +}; + +// BytesArray. +mod byte_array; +use byte_array::{ByteArray, ByteArrayIndexView, ByteArrayTrait}; + #[cfg(test)] mod test; diff --git a/corelib/src/math.cairo b/corelib/src/math.cairo new file mode 100644 index 000000000..708222ec1 --- /dev/null +++ b/corelib/src/math.cairo @@ -0,0 +1,184 @@ +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 { + Option::Some(u256_mul_mod_n(a, inv_mod(b, n)?, n)) +} + +/// Returns `a * b (mod n)`. +fn u256_mul_mod_n(a: u256, b: u256, n: NonZero) -> u256 { + let (_, r) = u512_safe_div_rem_by_u256(u256_wide_mul(a, b), n); + r +} + +// === 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 4ed6d30b7..a7a35fd22 100644 --- a/corelib/src/nullable.cairo +++ b/corelib/src/nullable.cairo @@ -2,10 +2,11 @@ use box::BoxTrait; use traits::Default; use traits::Felt252DictValue; +#[derive(Copy, Drop)] extern type Nullable; enum FromNullableResult { - Null: (), + Null, NotNull: Box, } @@ -15,21 +16,21 @@ extern fn match_nullable(value: Nullable) -> FromNullableResult nopanic trait NullableTrait { fn deref(self: Nullable) -> T; + fn new(value: T) -> Nullable; } 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(), } } + fn new(value: T) -> Nullable { + nullable_from_box(BoxTrait::new(value)) + } } -// Impls for generic types -impl NullableCopy> of Copy>; -impl NullableDrop> of Drop>; - impl NullableDefault of Default> { #[inline(always)] fn default() -> Nullable nopanic { diff --git a/corelib/src/option.cairo b/corelib/src/option.cairo index fc282f215..79672c593 100644 --- a/corelib/src/option.cairo +++ b/corelib/src/option.cairo @@ -1,9 +1,13 @@ use array::ArrayTrait; +use serde::Serde; +use array::SpanTrait; +#[derive(Copy, Drop, Serde, PartialEq)] enum Option { Some: T, - None: (), + None, } + trait OptionTrait { /// If `val` is `Option::Some(x)`, returns `x`. Otherwise, panics with `err`. fn expect(self: Option, err: felt252) -> T; @@ -19,7 +23,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,18 +34,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, } } } - -// Impls for generic types. -impl OptionCopy> of Copy>; -impl OptionDrop> of Drop>; diff --git a/corelib/src/panics.cairo b/corelib/src/panics.cairo new file mode 100644 index 000000000..1541a81ec --- /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/pedersen.cairo b/corelib/src/pedersen.cairo new file mode 100644 index 000000000..11f912900 --- /dev/null +++ b/corelib/src/pedersen.cairo @@ -0,0 +1,32 @@ +extern type Pedersen; + +extern fn pedersen(a: felt252, b: felt252) -> felt252 implicits(Pedersen) nopanic; + + +/// State for Pedersen hash. +#[derive(Copy, Drop)] +struct HashState { + state: felt252, +} + +#[generate_trait] +impl PedersenImpl of PedersenTrait { + /// Creates a state from a base value. + #[inline(always)] + fn new(base: felt252) -> HashState { + HashState { state: base } + } +} + +impl HashStateImpl of hash::HashStateTrait { + #[inline(always)] + fn update(self: HashState, value: felt252) -> HashState { + HashState { state: pedersen(self.state, value) } + } + + #[inline(always)] + fn finalize(self: HashState) -> felt252 { + self.state + } +} + diff --git a/corelib/src/poseidon.cairo b/corelib/src/poseidon.cairo index b1ab40d95..360e8f888 100644 --- a/corelib/src/poseidon.cairo +++ b/corelib/src/poseidon.cairo @@ -1,6 +1,7 @@ use array::Span; use array::SpanTrait; use option::OptionTrait; +use hash::HashStateTrait; extern type Poseidon; @@ -8,15 +9,52 @@ extern fn hades_permutation( s0: felt252, s1: felt252, s2: felt252 ) -> (felt252, felt252, felt252) implicits(Poseidon) nopanic; - -// Represents a Poseidon state. +/// State for Poseidon hash. #[derive(Copy, Drop)] -struct PoseidonBuiltinState { +struct HashState { s0: felt252, s1: felt252, s2: felt252, + odd: bool, +} + +#[generate_trait] +impl PoseidonImpl of PoseidonTrait { + /// Creates an initial state. + #[inline(always)] + fn new() -> HashState { + HashState { s0: 0, s1: 0, s2: 0, odd: false } + } +} + +impl HashStateDefault of Default { + fn default() -> HashState { + PoseidonTrait::new() + } } +impl HashStateImpl of HashStateTrait { + #[inline(always)] + fn update(self: HashState, value: felt252) -> HashState { + if self.odd { + let (s0, s1, s2) = hades_permutation(self.s0, self.s1 + value, self.s2); + HashState { s0, s1, s2, odd: false } + } else { + HashState { s0: self.s0 + value, s1: self.s1, s2: self.s2, odd: true } + } + } + + #[inline(always)] + fn finalize(self: HashState) -> felt252 { + if self.odd { + let (r, _, _) = hades_permutation(self.s0, self.s1 + 1, self.s2); + r + } else { + let (r, _, _) = hades_permutation(self.s0 + 1, self.s1, self.s2); + r + } + } +} /// Computes the Poseidon hash on the given input. /// @@ -25,32 +63,27 @@ struct PoseidonBuiltinState { /// To distinguish between different input sizes always pads with 1, and possibly with another 0 to /// complete to an even-sized input. fn poseidon_hash_span(mut span: Span) -> felt252 { - let builtin_costs = get_builtin_costs(); - _poseidon_hash_span_inner(builtin_costs, PoseidonBuiltinState { s0: 0, s1: 0, s2: 0 }, ref span) + _poseidon_hash_span_inner(get_builtin_costs(), (0, 0, 0), ref span) } /// Helper function for poseidon_hash_span. fn _poseidon_hash_span_inner( - builtin_costs: gas::BuiltinCosts, state: PoseidonBuiltinState, ref span: Span + builtin_costs: gas::BuiltinCosts, state: (felt252, felt252, felt252), ref span: Span ) -> felt252 { - let x = match span.pop_front() { + let (s0, s1, s2) = state; + let x = *match span.pop_front() { Option::Some(x) => x, - Option::None(()) => { - // Pad input with [1, 0]. - let (s0, s1, s2) = hades_permutation(state.s0 + 1, state.s1, state.s2); - return s0; + Option::None => { + return HashState { s0, s1, s2, odd: false }.finalize(); }, }; - let y = match span.pop_front() { + let y = *match span.pop_front() { Option::Some(y) => y, - Option::None(()) => { - // Add x and pad with [0]. - let (s0, s1, s2) = hades_permutation(state.s0 + *x, state.s1 + 1, state.s2); - return s0; + Option::None => { + return HashState { s0: s0 + x, s1, s2, odd: true }.finalize(); }, }; - - let (s0, s1, s2) = hades_permutation(state.s0 + *x, state.s1 + *y, state.s2); + let next_state = hades_permutation(s0 + x, s1 + y, s2); gas::withdraw_gas_all(builtin_costs).expect('Out of gas'); - _poseidon_hash_span_inner(builtin_costs, PoseidonBuiltinState { s0, s1, s2 }, ref span) + _poseidon_hash_span_inner(builtin_costs, next_state, ref span) } diff --git a/corelib/src/result.cairo b/corelib/src/result.cairo index 3f0c5a701..6c0b5c846 100644 --- a/corelib/src/result.cairo +++ b/corelib/src/result.cairo @@ -1,45 +1,38 @@ use array::ArrayTrait; +use serde::Serde; +use array::SpanTrait; + +#[derive(Copy, Drop, Serde, PartialEq)] enum Result { Ok: T, Err: E, } -trait ResultTrait { - /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics with `err`. - fn expect>(self: Result, err: felt252) -> T; - /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics. - fn unwrap>(self: Result) -> T; - /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics with `err`. - fn expect_err>(self: Result, err: felt252) -> E; - /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics. - fn unwrap_err>(self: Result) -> E; - /// Returns `true` if the `Result` is `Result::Ok`. - fn is_ok(self: @Result) -> bool; - /// Returns `true` if the `Result` is `Result::Err`. - fn is_err(self: @Result) -> bool; - /// Returns `true` if the `Result` is `Result::Ok`, and consumes the value. - fn into_is_err, impl EDrop: Drop>(self: Result) -> bool; - /// Returns `true` if the `Result` is `Result::Err`, and consumes the value. - fn into_is_ok, impl EDrop: Drop>(self: Result) -> bool; -} + +#[generate_trait] impl ResultTraitImpl of ResultTrait { + /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics with `err`. fn expect>(self: Result, err: felt252) -> T { match self { Result::Ok(x) => x, Result::Err(_) => panic_with_felt252(err), } } + /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics. fn unwrap>(self: Result) -> T { self.expect('Result::unwrap failed.') } + /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics with `err`. fn expect_err>(self: Result, err: felt252) -> E { match self { Result::Ok(_) => panic_with_felt252(err), Result::Err(x) => x, } } + /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics. fn unwrap_err>(self: Result) -> E { self.expect_err('Result::unwrap_err failed.') } + /// Returns `true` if the `Result` is `Result::Ok`. #[inline] fn is_ok(self: @Result) -> bool { match self { @@ -47,6 +40,7 @@ impl ResultTraitImpl of ResultTrait { Result::Err(_) => false, } } + /// Returns `true` if the `Result` is `Result::Err`. #[inline] fn is_err(self: @Result) -> bool { match self { @@ -54,6 +48,7 @@ impl ResultTraitImpl of ResultTrait { Result::Err(_) => true, } } + /// Returns `true` if the `Result` is `Result::Ok`, and consumes the value. #[inline] fn into_is_err, impl EDrop: Drop>(self: Result) -> bool { match self { @@ -61,6 +56,7 @@ impl ResultTraitImpl of ResultTrait { Result::Err(_) => true, } } + /// Returns `true` if the `Result` is `Result::Err`, and consumes the value. #[inline] fn into_is_ok, impl EDrop: Drop>(self: Result) -> bool { match self { @@ -69,7 +65,3 @@ impl ResultTraitImpl of ResultTrait { } } } - -// Impls for generic types. -impl ResultCopy, impl ECopy: Copy> of Copy>; -impl ResultDrop, impl EDrop: Drop> of Drop>; diff --git a/corelib/src/serde.cairo b/corelib/src/serde.cairo index f72a14525..9d1891ac2 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<()> { @@ -139,13 +15,13 @@ impl TupleSize0Serde of Serde<()> { } } -impl TupleSize1Serde> of Serde<(E0, )> { - fn serialize(self: @(E0, ), ref output: Array) { - let (e0, ) = self; +impl TupleSize1Serde> of Serde<(E0,)> { + fn serialize(self: @(E0,), ref output: Array) { + let (e0,) = self; e0.serialize(ref output) } - fn deserialize(ref serialized: Span) -> Option<(E0, )> { - Option::Some((E0Serde::deserialize(ref serialized)?, )) + fn deserialize(ref serialized: Span) -> Option<(E0,)> { + Option::Some((E0Serde::deserialize(ref serialized)?,)) } } diff --git a/corelib/src/starknet.cairo b/corelib/src/starknet.cairo index 3a339b5d9..21d3791be 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 000000000..4765a71ca --- /dev/null +++ b/corelib/src/starknet/account.cairo @@ -0,0 +1,15 @@ +use starknet::ContractAddress; + +#[derive(Drop, Serde)] +struct Call { + to: ContractAddress, + selector: felt252, + calldata: Array +} + +#[starknet::interface] +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 d3628fa1f..5d52693b6 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 f12ff7d1c..1e36eb1a8 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 b80595c08..857bd4a01 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, PartialEq)] struct EthAddress { - address: felt252, + 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,9 @@ impl EthAddressZeroable of Zeroable { !self.is_zero() } } -impl ContractAddressPartialEq of PartialEq { - #[inline(always)] - fn eq(lhs: EthAddress, rhs: EthAddress) -> bool { - lhs.address == rhs.address - } - #[inline(always)] - 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 4e6bdfda0..3689932c5 100644 --- a/corelib/src/starknet/event.cairo +++ b/corelib/src/starknet/event.cairo @@ -1,6 +1,8 @@ -use serde::Serde; - 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/info.cairo b/corelib/src/starknet/info.cairo index b149d7ff8..cd8a8887c 100644 --- a/corelib/src/starknet/info.cairo +++ b/corelib/src/starknet/info.cairo @@ -13,14 +13,14 @@ struct ExecutionInfo { entry_point_selector: felt252, } -#[derive(Copy, Drop)] +#[derive(Copy, Drop, Serde)] struct BlockInfo { block_number: u64, block_timestamp: u64, sequencer_address: ContractAddress, } -#[derive(Copy, Drop)] +#[derive(Copy, Drop, Serde)] struct TxInfo { // The version of the transaction. It is fixed (currently, 1) in the OS, and should be // signed by the account contract. diff --git a/corelib/src/starknet/secp256_trait.cairo b/corelib/src/starknet/secp256_trait.cairo new file mode 100644 index 000000000..72db7e77d --- /dev/null +++ b/corelib/src/starknet/secp256_trait.cairo @@ -0,0 +1,166 @@ +use array::ArrayTrait; +use keccak::keccak_u256s_be_inputs; +use math::{u256_mul_mod_n, inv_mod}; +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. +/// `v` is the sum of an odd number and the parity of the y coordinate of the ec point whose x +/// coordinate is `r`. +/// See https://eips.ethereum.org/EIPS/eip-155 for more details. +fn signature_from_vrs(v: u32, r: u256, s: u256) -> Signature { + Signature { r, s, y_parity: v % 2 == 0 } +} + +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_inv = inv_mod(r.try_into().unwrap(), n_nz).unwrap(); + + let u1 = u256_mul_mod_n(msg_hash, r_inv, n_nz); + let minus_u1 = secp256_ec_negate_scalar::(u1); + let u2 = u256_mul_mod_n(s, r_inv, n_nz); + + 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 +} + + +/// Checks 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. +/// Returns a Result with an error string if the signature is invalid. +fn is_eth_signature_valid< + Secp256Point, + impl Secp256PointDrop: Drop, + impl Secp256Impl: Secp256Trait, + impl Secp256PointImpl: Secp256PointTrait +>( + msg_hash: u256, signature: Signature, eth_address: EthAddress +) -> Result<(), felt252> { + if !is_signature_entry_valid::(signature.r) { + return Result::Err('Signature out of range'); + } + if !is_signature_entry_valid::(signature.s) { + return Result::Err('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); + if eth_address != calculated_eth_address { + return Result::Err('Invalid signature'); + } + Result::Ok(()) +} + +/// Asserts that a Secp256 ECDSA signature is valid. +/// 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 +) { + match is_eth_signature_valid::(:msg_hash, :signature, :eth_address) { + Result::Ok(()) => {}, + Result::Err(err) => panic_with_felt252(err), + } +} + +/// 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 0aa62b512..7030844f6 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 000000000..65fdfcc86 --- /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 70cc1578f..8cbae6393 100644 --- a/corelib/src/starknet/storage_access.cairo +++ b/corelib/src/starknet/storage_access.cairo @@ -1,3 +1,4 @@ +use core::array::ArrayTrait; use traits::{Into, TryInto}; use option::OptionTrait; use starknet::{ @@ -52,12 +53,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 +108,680 @@ 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()) } -} - -impl StorageAccessU128 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( - StorageAccess::::read(address_domain, base)? + Store::::read_at_offset(address_domain, base, offset)? .try_into() - .expect('StorageAccessU128 - non u128') + .expect('StoreU64 - non u64') ) } #[inline(always)] - fn write(address_domain: u32, base: StorageBaseAddress, value: u128) -> SyscallResult<()> { - StorageAccess::::write(address_domain, base, value.into()) + 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 StorageAccessU256 of StorageAccess { - fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult { +impl StoreU128 of Store { + fn read(address_domain: u32, base: StorageBaseAddress) -> 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') - } + Store::::read(address_domain, base)?.try_into().expect('StoreU128 - non u128') ) } - 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() + #[inline(always)] + fn write(address_domain: u32, base: StorageBaseAddress, value: u128) -> SyscallResult<()> { + 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('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() + } +} + + +impl ResultStore< + T, E, impl TStore: Store, impl EStore: Store, impl TDrop: Drop, impl EDrop: Drop, +> of Store> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult> { + let idx = Store::::read(address_domain, base)?; + if idx == 0 { + starknet::SyscallResult::Ok( + Result::Ok(Store::read_at_offset(address_domain, base, 1_u8)?) + ) + } else if idx == 1 { + starknet::SyscallResult::Ok( + Result::Err(Store::read_at_offset(address_domain, base, 1_u8)?) + ) + } else { + starknet::SyscallResult::Err(array!['Incorrect index:']) + } + } + #[inline(always)] + fn write( + address_domain: u32, base: StorageBaseAddress, value: Result + ) -> SyscallResult<()> { + match value { + Result::Ok(x) => { + Store::write(address_domain, base, 0)?; + Store::write_at_offset(address_domain, base, 1_u8, x)?; + }, + Result::Err(x) => { + Store::write(address_domain, base, 1)?; + Store::write_at_offset(address_domain, base, 1_u8, x)?; + } + }; + starknet::SyscallResult::Ok(()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult> { + let idx = Store::::read_at_offset(address_domain, base, offset)?; + if idx == 0 { + starknet::SyscallResult::Ok( + Result::Ok(Store::read_at_offset(address_domain, base, offset + 1_u8)?) + ) + } else if idx == 1 { + starknet::SyscallResult::Ok( + Result::Err(Store::read_at_offset(address_domain, base, offset + 1_u8)?) + ) + } else { + starknet::SyscallResult::Err(array!['Incorrect index:']) + } + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: Result + ) -> SyscallResult<()> { + match value { + Result::Ok(x) => { + Store::write(address_domain, base, 0)?; + Store::write_at_offset(address_domain, base, 1_u8, x)?; + }, + Result::Err(x) => { + Store::write(address_domain, base, 1)?; + Store::write_at_offset(address_domain, base, 1_u8, x)?; + } + }; + starknet::SyscallResult::Ok(()) + } + #[inline(always)] + fn size() -> u8 { + 1 + cmp::max(Store::::size(), Store::::size()) + } +} + +impl OptionStore, impl TDrop: Drop,> of Store> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult> { + let idx = Store::::read(address_domain, base)?; + if idx == 1 { + starknet::SyscallResult::Ok( + Option::Some(Store::read_at_offset(address_domain, base, 1_u8)?) + ) + } else if idx == 0 { + starknet::SyscallResult::Ok(Option::None(())) + } else { + starknet::SyscallResult::Err(array!['Incorrect index:']) + } + } + #[inline(always)] + fn write(address_domain: u32, base: StorageBaseAddress, value: Option) -> SyscallResult<()> { + match value { + Option::Some(x) => { + Store::write(address_domain, base, 1)?; + Store::write_at_offset(address_domain, base, 1_u8, x)?; + }, + Option::None(_) => { + Store::write(address_domain, base, 0)?; + } + }; + starknet::SyscallResult::Ok(()) + } + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult> { + let idx = Store::::read_at_offset(address_domain, base, offset)?; + if idx == 1 { + starknet::SyscallResult::Ok( + Option::Some(Store::read_at_offset(address_domain, base, offset + 1_u8)?) + ) + } else if idx == 0 { + starknet::SyscallResult::Ok(Option::None(())) + } else { + starknet::SyscallResult::Err(array!['Incorrect index:']) + } + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: Option + ) -> SyscallResult<()> { + match value { + Option::Some(x) => { + Store::write(address_domain, base, 1)?; + Store::write_at_offset(address_domain, base, 1_u8, x)?; + }, + Option::None(x) => { + Store::write(address_domain, base, 0)?; + } + }; + starknet::SyscallResult::Ok(()) + } + #[inline(always)] + fn size() -> u8 { + 1 + Store::::size() } } diff --git a/corelib/src/starknet/syscalls.cairo b/corelib/src/starknet/syscalls.cairo index 02a28bb43..4f1254ada 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 @@ -58,7 +63,7 @@ extern fn send_message_to_l1_syscall( // guarantees. // `address` - The address of the storage key to read. extern fn storage_read_syscall( - address_domain: u32, address: StorageAddress, + address_domain: u32, address: StorageAddress, ) -> SyscallResult implicits(GasBuiltin, System) nopanic; // Sets the value of a key in the storage of the calling contract. diff --git a/corelib/src/starknet/testing.cairo b/corelib/src/starknet/testing.cairo index b04dfc163..ed9b2c504 100644 --- a/corelib/src/starknet/testing.cairo +++ b/corelib/src/starknet/testing.cairo @@ -1,14 +1,95 @@ 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) +} + +// TODO(Ilya): Decide if we limit the type of `to_address`. +// Pop the earliest unpopped l2 to l1 message for the contract. +fn pop_l2_to_l1_message(address: ContractAddress) -> Option<(felt252, Span)> { + let mut l2_to_l1_message = cheatcode::<'pop_l2_to_l1_message'>(array![address.into()].span()); + Option::Some( + ( + serde::Serde::deserialize(ref l2_to_l1_message)?, + serde::Serde::deserialize(ref l2_to_l1_message)?, + ) + ) +} diff --git a/corelib/src/test.cairo b/corelib/src/test.cairo index d8cefdeca..6f41b0f0b 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 0873d759e..deb35401d 100644 --- a/corelib/src/test/array_test.cairo +++ b/corelib/src/test/array_test.cairo @@ -1,87 +1,102 @@ -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); +} + +#[test] +#[available_gas(10000000)] +fn test_equality() { + let arr1 = array![]; + let arr2 = array![10, 11, 12]; + let arr3 = array![10, 11, 13]; + let arr4 = array![10, 11]; + let arr5 = array![10, 11, 12, 13]; + + assert(arr1 == arr1, 'arr1 != arr1'); + assert(arr2 == arr2, 'arr2 != arr2'); + assert(arr3 == arr3, 'arr3 != arr3'); + assert(arr4 == arr4, 'arr4 != arr4'); + assert(arr5 == arr5, 'arr5 != arr5'); + + assert(arr1 != arr2, 'arr1 == arr2'); + assert(arr1 != arr3, 'arr1 == arr3'); + assert(arr1 != arr4, 'arr1 == arr4'); + assert(arr1 != arr5, 'arr1 == arr5'); + assert(arr2 != arr3, 'arr2 == arr3'); + assert(arr2 != arr4, 'arr2 == arr4'); + assert(arr2 != arr5, 'arr2 == arr5'); + assert(arr3 != arr4, 'arr3 == arr4'); + assert(arr3 != arr5, 'arr3 == arr5'); + assert(arr4 != arr5, 'arr4 == arr5'); } diff --git a/corelib/src/test/bool_test.cairo b/corelib/src/test/bool_test.cairo index 0dcb3d189..881efb2b1 100644 --- a/corelib/src/test/bool_test.cairo +++ b/corelib/src/test/bool_test.cairo @@ -1,11 +1,13 @@ +use test::test_utils::{assert_eq, assert_ne}; + #[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 +21,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 266b9b953..72349518c 100644 --- a/corelib/src/test/box_test.cairo +++ b/corelib/src/test/box_test.cairo @@ -1,4 +1,4 @@ -use box::BoxTrait; +use test::test_utils::{assert_eq, assert_ne}; #[test] fn test_box_unbox_felt252s() { @@ -6,8 +6,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 +17,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/byte_array_test.cairo b/corelib/src/test/byte_array_test.cairo new file mode 100644 index 000000000..64558faae --- /dev/null +++ b/corelib/src/test/byte_array_test.cairo @@ -0,0 +1,569 @@ +use test::test_utils::{assert_eq, assert_ne}; + +#[test] +#[available_gas(1000000)] +fn test_append_byte() { + let mut ba = Default::default(); + let mut c = 1_u8; + loop { + if c == 34 { + break; + } + ba.append_byte(c); + c += 1; + }; + + let expected_data = array![0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f]; + compare_byte_array(@ba, expected_data.span(), 2, 0x2021); +} + +#[test] +#[available_gas(10000000)] +fn test_append_word() { + let mut ba = Default::default(); + + ba.append_word(0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e, 30); + compare_byte_array( + @ba, array![].span(), 30, 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ); + + ba.append_word(0x1f2021, 3); + let expected_data = array![0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f]; + compare_byte_array(@ba, expected_data.span(), 2, 0x2021); + + ba.append_word(0x2223, 2); + compare_byte_array(@ba, expected_data.span(), 4, 0x20212223); + + // Length is 0, so nothing is actually appended. + ba.append_word(0xffee, 0); + compare_byte_array(@ba, expected_data.span(), 4, 0x20212223); + + ba.append_word(0x2425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e, 27); + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e + ]; + compare_byte_array(@ba, expected_data.span(), 0, 0); + + ba.append_word(0x3f, 1); + compare_byte_array(@ba, expected_data.span(), 1, 0x3f); +} + +#[test] +#[available_gas(1000000)] +fn test_append() { + let mut ba1 = test_byte_array_32(); + let ba2 = test_byte_array_32(); + + ba1.append(@ba2); + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x200102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ]; + compare_byte_array(@ba1, expected_data.span(), 2, 0x1f20); +} + +// Same as test_append, but with `+=` instead of `append`. +#[test] +#[available_gas(1000000)] +fn test_add_eq() { + let mut ba1 = test_byte_array_32(); + let ba2 = test_byte_array_32(); + + ba1 += ba2; + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x200102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ]; + compare_byte_array(@ba1, expected_data.span(), 2, 0x1f20); +} + +#[test] +#[available_gas(1000000)] +fn test_concat() { + let ba1 = test_byte_array_32(); + let ba2 = test_byte_array_32(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x200102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ]; + compare_byte_array(@ba3, expected_data.span(), 2, 0x1f20); +} + +// Same as test_concat, but with `+` instead of `concat`. +#[test] +#[available_gas(1000000)] +fn test_add() { + let ba1 = test_byte_array_32(); + let ba2 = test_byte_array_32(); + + let ba3 = ba1 + ba2; + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x200102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ]; + compare_byte_array(@ba3, expected_data.span(), 2, 0x1f20); +} + +// Test concat/append, first byte array empty. +#[test] +#[available_gas(1000000)] +fn test_concat_first_empty() { + let ba1 = Default::default(); + let ba2 = test_byte_array_32(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f]; + compare_byte_array(@ba3, expected_data.span(), 1, 0x20); +} + +// Test concat/append, second byte array empty. +#[test] +#[available_gas(1000000)] +fn test_concat_second_empty() { + let ba1 = test_byte_array_32(); + let ba2 = Default::default(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f]; + compare_byte_array(@ba3, expected_data.span(), 1, 0x20); +} + +// Test concat/append, first byte array pending word is empty. +#[test] +#[available_gas(1000000)] +fn test_concat_first_pending_0() { + let ba1 = test_byte_array_31(); + let ba2 = test_byte_array_32(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f + ]; + compare_byte_array(@ba3, expected_data.span(), 1, 0x20); +} + +// Test concat/append, second byte array pending word is empty. +#[test] +#[available_gas(1000000)] +fn test_concat_second_pending_0() { + let ba1 = test_byte_array_32(); + let ba2 = test_byte_array_31(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x200102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ]; + compare_byte_array(@ba3, expected_data.span(), 1, 0x1f); +} + +// Test concat/append, split index of the words of the second byte array is 16. +#[test] +#[available_gas(1000000)] +fn test_concat_split_index_16() { + let ba1 = test_byte_array_16(); + let ba2 = test_byte_array_32(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![0x0102030405060708091a0b0c0d0e0f100102030405060708091a0b0c0d0e0f]; + compare_byte_array(@ba3, expected_data.span(), 17, 0x101112131415161718191a1b1c1d1e1f20); +} + +// Test concat/append, split index of the words of the second byte array is < 16, specifically 1. +#[test] +#[available_gas(1000000)] +fn test_concat_split_index_lt_16() { + let ba1 = test_byte_array_1(); + let ba2 = test_byte_array_32(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![0x010102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e]; + compare_byte_array(@ba3, expected_data.span(), 2, 0x1f20); +} + +// Test concat/append, split index of the words of the second byte array is > 16, specifically 30. +#[test] +#[available_gas(1000000)] +fn test_concat_split_index_gt_16() { + let ba1 = test_byte_array_30(); + let ba2 = test_byte_array_33(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e01, + 0x02030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 + ]; + compare_byte_array(@ba3, expected_data.span(), 1, 0x21); +} + +// Sum of the lengths of the pending words of both byte arrays is 31 (a full word). +#[test] +#[available_gas(1000000)] +fn test_concat_pending_sum_up_to_full() { + let ba1 = test_byte_array_32(); + let ba2 = test_byte_array_30(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![ + 0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, + 0x200102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ]; + compare_byte_array(@ba3, expected_data.span(), 0, 0); +} + +// Sum of the lengths of the pending words of both byte arrays is 31+16. +// That is, the pending words aggregate to a full word, and the last split index is 16. +#[test] +#[available_gas(1000000)] +fn test_concat_pending_sum_up_to_more_than_word_16() { + let ba1 = test_byte_array_17(); + let ba2 = test_byte_array_30(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![0x0102030405060708091a0b0c0d0e0f10110102030405060708091a0b0c0d0e]; + compare_byte_array(@ba3, expected_data.span(), 16, 0x0f101112131415161718191a1b1c1d1e); +} + +// Sum of the lengths of the pending words of both byte arrays is in [32, 31+15]. +// That is, the pending words aggregate to a full word, and the last split index is <16. +#[test] +#[available_gas(1000000)] +fn test_concat_pending_sum_up_to_more_than_word_lt16() { + let ba1 = test_byte_array_2(); + let ba2 = test_byte_array_30(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![0x01020102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d]; + compare_byte_array(@ba3, expected_data.span(), 1, 0x1e); +} + +// Sum of the lengths of the pending words of both byte arrays is >31+15 +// That is, the pending words aggregate to a full word, and the last split index is >16. +#[test] +#[available_gas(1000000)] +fn test_concat_pending_sum_up_to_more_than_word_gt16() { + let ba1 = test_byte_array_30(); + let ba2 = test_byte_array_30(); + + let ba3 = ByteArrayTrait::concat(@ba1, @ba2); + + let expected_data = array![0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e01]; + compare_byte_array( + @ba3, expected_data.span(), 29, 0x02030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e + ); +} + +#[test] +#[available_gas(1000000)] +fn test_len() { + let ba: ByteArray = Default::default(); + assert(ba.len() == 0, 'wrong ByteArray len'); + + let mut ba = test_byte_array_33(); + assert(ba.len() == 33, 'wrong ByteArray len'); + + ba.append(@test_byte_array_30()); + assert(ba.len() == 63, 'wrong ByteArray len'); +} + +#[test] +#[available_gas(100000000)] +fn test_at_empty() { + let ba: ByteArray = Default::default(); + + assert(ba.at(0) == Option::None, 'index 0 is not out of bounds'); + assert(ba.at(1) == Option::None, 'index 1 is not out of bounds'); + assert(ba.at(30) == Option::None, 'index 30 is not out of bounds'); + assert(ba.at(31) == Option::None, 'index 31 is not out of bounds'); +} + +#[test] +#[available_gas(100000000)] +fn test_at() { + let mut ba = test_byte_array_31(); + ba.append(@test_byte_array_31()); + ba.append(@test_byte_array_17()); + + assert(ba.at(0) == Option::Some(0x01), 'wrong byte at index 0'); + assert(ba.at(1) == Option::Some(0x02), 'wrong byte at index 1'); + assert(ba.at(2) == Option::Some(0x03), 'wrong byte at index 2'); + assert(ba.at(14) == Option::Some(0x0f), 'wrong byte at index 14'); + assert(ba.at(15) == Option::Some(0x10), 'wrong byte at index 15'); + assert(ba.at(16) == Option::Some(0x11), 'wrong byte at index 16'); + assert(ba.at(17) == Option::Some(0x12), 'wrong byte at index 17'); + assert(ba.at(29) == Option::Some(0x1e), 'wrong byte at index 29'); + assert(ba.at(30) == Option::Some(0x1f), 'wrong byte at index 30'); + assert(ba.at(31) == Option::Some(0x01), 'wrong byte at index 31'); + assert(ba.at(32) == Option::Some(0x02), 'wrong byte at index 32'); + assert(ba.at(61) == Option::Some(0x1f), 'wrong byte at index 61'); + assert(ba.at(62) == Option::Some(0x01), 'wrong byte at index 62'); + assert(ba.at(63) == Option::Some(0x02), 'wrong byte at index 63'); + assert(ba.at(76) == Option::Some(0x0f), 'wrong byte at index 76'); + assert(ba.at(77) == Option::Some(0x10), 'wrong byte at index 77'); + assert(ba.at(78) == Option::Some(0x11), 'wrong byte at index 78'); + assert(ba.at(79) == Option::None, 'index 79 is not out of bounds'); +} + +// Same as the previous test, but with [] instead of .at() (and without the out-of-bounds case). +#[test] +#[available_gas(100000000)] +fn test_index_view() { + let mut ba = test_byte_array_31(); + ba.append(@test_byte_array_31()); + ba.append(@test_byte_array_17()); + + assert(ba[0] == 0x01, 'wrong byte at index 0'); + assert(ba[1] == 0x02, 'wrong byte at index 1'); + assert(ba[2] == 0x03, 'wrong byte at index 2'); + assert(ba[14] == 0x0f, 'wrong byte at index 14'); + assert(ba[15] == 0x10, 'wrong byte at index 15'); + assert(ba[16] == 0x11, 'wrong byte at index 16'); + assert(ba[17] == 0x12, 'wrong byte at index 17'); + assert(ba[29] == 0x1e, 'wrong byte at index 29'); + assert(ba[30] == 0x1f, 'wrong byte at index 30'); + assert(ba[31] == 0x01, 'wrong byte at index 31'); + assert(ba[32] == 0x02, 'wrong byte at index 32'); + assert(ba[61] == 0x1f, 'wrong byte at index 61'); + assert(ba[62] == 0x01, 'wrong byte at index 62'); + assert(ba[63] == 0x02, 'wrong byte at index 63'); + assert(ba[76] == 0x0f, 'wrong byte at index 76'); + assert(ba[77] == 0x10, 'wrong byte at index 77'); + assert(ba[78] == 0x11, 'wrong byte at index 78'); +} + +// Test panic with [] in case of out-of-bounds +#[test] +#[should_panic(expected: ('Index out of bounds',))] +#[available_gas(100000000)] +fn test_index_view_out_of_bounds() { + let mut ba = test_byte_array_31(); + ba.append(@test_byte_array_31()); + ba.append(@test_byte_array_17()); + + let x = ba[79]; +} + +#[test] +fn test_string_literals() { + let ba: ByteArray = "12345"; // len < 16 + let ba: ByteArray = "1234567890123456"; // len == 16 + let ba: ByteArray = "123456789012345678"; // 16 < len < 31 + let ba: ByteArray = "1234567890123456789012345678901"; // len == 31 + let ba: ByteArray = "123456789012345678901234567890123"; // 31 < len < 47 + let ba: ByteArray = "12345678901234567890123456789012345678901234567"; // len == 47 + let ba: ByteArray = "123456789012345678901234567890123456789012345678"; // len > 47 +} + +#[test] +#[available_gas(100000000)] +fn test_equality() { + let byte_array: ByteArray = "a"; + assert(@byte_array == @"a", 'Same strings are not equal'); + assert(@byte_array != @"b", 'Different strings are equal'); + + let mut ba1 = test_byte_array_2(); + ba1.append(@test_byte_array_31()); + let ba2 = test_byte_array_33(); + let ba3 = test_byte_array_32(); + let mut ba4 = test_byte_array_32(); + ba4.append(@test_byte_array_1()); + + assert(@ba1 == @ba1, 'Same ByteArrays are not equal'); + assert(@ba2 == @ba2, 'Same ByteArrays are not equal'); + assert(@ba3 == @ba3, 'Same ByteArrays are not equal'); + assert(@ba4 == @ba4, 'Same ByteArrays are not equal'); + + // Different data + assert(@ba1 != @ba2, 'Different ByteArrays are equal'); + + // Different pending word length + assert(@ba2 != @ba3, 'Different ByteArrays are equal'); + + // Different pending word + assert(@ba2 != @ba4, 'Different ByteArrays are equal'); +} + +#[test] +#[available_gas(100000000)] +fn test_reverse() { + // Arrays of length < 16 + let ba: ByteArray = "abc"; + let ba_rev: ByteArray = "cba"; + let palindrome: ByteArray = "rotator"; + assert_ne(@ba, @ba.rev(), 'ba == ba.rev()'); + assert_ne(@ba_rev, @ba_rev.rev(), 'ba_rev == ba_rev.rev()'); + assert_eq(@ba, @ba_rev.rev(), 'ba != ba_rev.rev()'); + assert_eq(@palindrome, @palindrome.rev(), 'palindrome is not a palindrome'); + + // Arrays of length 16 + let ba: ByteArray = "my length is 16."; + let ba_rev: ByteArray = ".61 si htgnel ym"; + let palindrome: ByteArray = "nolemon nomelon"; + assert_ne(@ba, @ba.rev(), 'ba == ba.rev()'); + assert_ne(@ba_rev, @ba_rev.rev(), 'ba_rev == ba_rev.rev()'); + assert_eq(@ba, @ba_rev.rev(), 'ba != ba_rev.rev()'); + assert_eq(@palindrome, @palindrome.rev(), 'palindrome is not a palindrome'); + + // Arrays of 16 < length < 31 + let ba: ByteArray = "I am a medium byte array"; + let ba_rev: ByteArray = "yarra etyb muidem a ma I"; + let palindrome: ByteArray = "nolemon nomelon"; + assert_ne(@ba, @ba.rev(), 'ba == ba.rev()'); + assert_ne(@ba_rev, @ba_rev.rev(), 'ba_rev == ba_rev.rev()'); + assert_eq(@ba, @ba_rev.rev(), 'ba != ba_rev.rev()'); + assert_eq(@palindrome, @palindrome.rev(), 'palindrome is not a palindrome'); + + // Arrays of length 31 + let ba: ByteArray = "I didn't find a good palindrome"; + let ba_rev: ByteArray = "emordnilap doog a dnif t'ndid I"; + let palindrome: ByteArray = "kayak level rotator level kayak"; + assert_ne(@ba, @ba.rev(), 'ba == ba.rev()'); + assert_ne(@ba_rev, @ba_rev.rev(), 'ba_rev == ba_rev.rev()'); + assert_eq(@ba, @ba_rev.rev(), 'ba != ba_rev.rev()'); + assert_eq(@palindrome, @palindrome.rev(), 'palindrome is not a palindrome'); + + // Arrays of 31 < length < 47 (31+16) + let ba: ByteArray = "This time I did find a good palindrome!"; + let ba_rev: ByteArray = "!emordnilap doog a dnif did I emit sihT"; + let palindrome: ByteArray = "noitneverpropagatesifisetagaporprevention"; + assert_ne(@ba, @ba.rev(), 'ba == ba.rev()'); + assert_ne(@ba_rev, @ba_rev.rev(), 'ba_rev == ba_rev.rev()'); + assert_eq(@ba, @ba_rev.rev(), 'ba != ba_rev.rev()'); + assert_eq(@palindrome, @palindrome.rev(), 'palindrome is not a palindrome'); + + // Arrays of length 47 (31+16) + let ba: ByteArray = "I have found a palindrome, exactly 47 in length"; + let ba_rev: ByteArray = "htgnel ni 74 yltcaxe ,emordnilap a dnuof evah I"; + let palindrome: ByteArray = "onacloverifaliveeruptsavastpureevilafirevolcano"; + assert_ne(@ba, @ba.rev(), 'ba == ba.rev()'); + assert_ne(@ba_rev, @ba_rev.rev(), 'ba_rev == ba_rev.rev()'); + assert_eq(@ba, @ba_rev.rev(), 'ba != ba_rev.rev()'); + assert_eq(@palindrome, @palindrome.rev(), 'palindrome is not a palindrome'); + + // Arrays of length > 47 (31+16) + let ba: ByteArray = "This palindrome is not as good, but at least it's long enough"; + let ba_rev: ByteArray = "hguone gnol s'ti tsael ta tub ,doog sa ton si emordnilap sihT"; + let palindrome: ByteArray = "docnoteidissentafastneverpreventsafatnessidietoncod"; + assert_ne(@ba, @ba.rev(), 'ba == ba.rev()'); + assert_ne(@ba_rev, @ba_rev.rev(), 'ba_rev == ba_rev.rev()'); + assert_eq(@ba, @ba_rev.rev(), 'ba != ba_rev.rev()'); + assert_eq(@palindrome, @palindrome.rev(), 'palindrome is not a palindrome'); +} + +// ========= Test helper functions ========= + +use debug::PrintTrait; +fn compare_byte_array( + mut ba: @ByteArray, mut data: Span, pending_word_len: usize, pending_word: felt252 +) { + assert(ba.data.len() == data.len(), 'wrong data len'); + let mut ba_data = ba.data.span(); + + let mut data_index = 0; + loop { + match ba_data.pop_front() { + Option::Some(x) => { + let actual_word = (*x).into(); + let expected_word = *data.pop_front().unwrap(); + if actual_word != expected_word { + 'data_index:'.print(); + data_index.print(); + 'expected word:'.print(); + expected_word.print(); + 'actual word:'.print(); + actual_word.print(); + + panic_with_felt252('wrong data'); + } + }, + Option::None(_) => { + break; + } + } + data_index += 1; + }; + + if *ba.pending_word_len != pending_word_len { + 'expected pending word len:'.print(); + pending_word_len.print(); + 'actual pending word len:'.print(); + (*ba.pending_word_len).print(); + panic_with_felt252('wrong pending_word_len'); + } + let ba_pending_word_felt: felt252 = (*ba.pending_word).into(); + if ba_pending_word_felt != pending_word { + 'expected pending word:'.print(); + pending_word.print(); + 'actual pending word:'.print(); + ba_pending_word_felt.print(); + panic_with_felt252('wrong pending_word'); + } +} + +fn test_byte_array_1() -> ByteArray { + let mut ba1 = Default::default(); + ba1.append_word(0x01, 1); + ba1 +} + +fn test_byte_array_2() -> ByteArray { + let mut ba1 = Default::default(); + ba1.append_word(0x0102, 2); + ba1 +} + +fn test_byte_array_16() -> ByteArray { + let mut ba1 = Default::default(); + ba1.append_word(0x0102030405060708091a0b0c0d0e0f10, 16); + ba1 +} + +fn test_byte_array_17() -> ByteArray { + let mut ba1 = Default::default(); + ba1.append_word(0x0102030405060708091a0b0c0d0e0f1011, 17); + ba1 +} + +fn test_byte_array_30() -> ByteArray { + let mut ba1 = Default::default(); + ba1.append_word(0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e, 30); + ba1 +} + +fn test_byte_array_31() -> ByteArray { + let mut ba1 = Default::default(); + ba1.append_word(0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, 31); + ba1 +} + +fn test_byte_array_32() -> ByteArray { + let mut ba1 = Default::default(); + ba1.append_word(0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, 31); + ba1.append_word(0x20, 1); + ba1 +} + +fn test_byte_array_33() -> ByteArray { + let mut ba2 = Default::default(); + ba2.append_word(0x0102030405060708091a0b0c0d0e0f101112131415161718191a1b1c1d1e1f, 31); + ba2.append_word(0x2021, 2); + ba2 +} diff --git a/corelib/src/test/bytes31_test.cairo b/corelib/src/test/bytes31_test.cairo new file mode 100644 index 000000000..825300cea --- /dev/null +++ b/corelib/src/test/bytes31_test.cairo @@ -0,0 +1,192 @@ +use bytes_31::split_bytes31; + +const POW_2_248: felt252 = 0x100000000000000000000000000000000000000000000000000000000000000; + +#[test] +fn test_at() { + let b1 = bytes31_const::<0x01>(); + assert(b1.at(0) == 0x01, 'wrong byte at index 0'); + + let b17 = bytes31_const::<0x0102030405060708090a0b0c0d0e0f1011>(); + assert(b17.at(0) == 0x11, 'wrong byte at index 0'); + assert(b17.at(1) == 0x10, 'wrong byte at index 1'); + assert(b17.at(2) == 0x0f, 'wrong byte at index 2'); + assert(b17.at(14) == 0x03, 'wrong byte at index 14'); + assert(b17.at(15) == 0x02, 'wrong byte at index 15'); + assert(b17.at(16) == 0x01, 'wrong byte at index 16'); + + let b31 = bytes31_const::<0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f>(); + assert(b31.at(0) == 0x1f, 'wrong byte at index 0'); + assert(b31.at(1) == 0x1e, 'wrong byte at index 1'); + assert(b31.at(2) == 0x1d, 'wrong byte at index 2'); + assert(b31.at(14) == 0x11, 'wrong byte at index 14'); + assert(b31.at(15) == 0x10, 'wrong byte at index 15'); + assert(b31.at(16) == 0x0f, 'wrong byte at index 16'); + assert(b31.at(17) == 0x0e, 'wrong byte at index 17'); + assert(b31.at(29) == 0x02, 'wrong byte at index 29'); + assert(b31.at(30) == 0x01, 'wrong byte at index 30'); +} + +// Same as the previous test, but with [] instead of .at() +#[test] +fn test_index_view() { + let b1 = bytes31_const::<0x01>(); + assert(b1[0] == 0x01, 'wrong byte at index 0'); + + let b17 = bytes31_const::<0x0102030405060708090a0b0c0d0e0f1011>(); + assert(b17[0] == 0x11, 'wrong byte at index 0'); + assert(b17[1] == 0x10, 'wrong byte at index 1'); + assert(b17[2] == 0x0f, 'wrong byte at index 2'); + assert(b17[14] == 0x03, 'wrong byte at index 14'); + assert(b17[15] == 0x02, 'wrong byte at index 15'); + assert(b17[16] == 0x01, 'wrong byte at index 16'); + + let b31 = bytes31_const::<0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f>(); + assert(b31[0] == 0x1f, 'wrong byte at index 0'); + assert(b31[1] == 0x1e, 'wrong byte at index 1'); + assert(b31[2] == 0x1d, 'wrong byte at index 2'); + assert(b31[14] == 0x11, 'wrong byte at index 14'); + assert(b31[15] == 0x10, 'wrong byte at index 15'); + assert(b31[16] == 0x0f, 'wrong byte at index 16'); + assert(b31[17] == 0x0e, 'wrong byte at index 17'); + assert(b31[29] == 0x02, 'wrong byte at index 29'); + assert(b31[30] == 0x01, 'wrong byte at index 30'); +} + + +#[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' + ); +} + +#[test] +fn test_split_bytes31() { + let (left, right) = split_bytes31(0x1122, 2, 1); + assert(left == 0x22, 'bad split (2, 1) left'); + assert(right == 0x11, 'bad split (2, 1) right'); + + let x = 0x112233445566778899aabbccddeeff00112233; + let (left, right) = split_bytes31(x, 19, 0); + assert(left == 0, 'bad split (19, 0) left'); + assert(right == 0x112233445566778899aabbccddeeff00112233, 'bad split (19, 0) right'); + + let (left, right) = split_bytes31(x, 19, 1); + assert(left == 0x33, 'bad split (19, 1) left'); + assert(right == 0x112233445566778899aabbccddeeff001122, 'bad split (19, 1) right'); + + let (left, right) = split_bytes31(x, 19, 15); + assert(left == 0x5566778899aabbccddeeff00112233, 'bad split (19, 15) left'); + assert(right == 0x11223344, 'bad split (19, 15) right'); + + let (left, right) = split_bytes31(x, 19, 16); + assert(left == 0x445566778899aabbccddeeff00112233, 'bad split (19, 16) left'); + assert(right == 0x112233, 'bad split (19, 16) right'); + + let (left, right) = split_bytes31(x, 19, 18); + assert(left == 0x2233445566778899aabbccddeeff00112233, 'bad split (19, 18) left'); + assert(right == 0x11, 'bad split (19, 18) right'); + + let (left, right) = split_bytes31(x, 19, 19); + assert(left == 0x112233445566778899aabbccddeeff00112233, 'bad split (19, 19) left'); + assert(right == 0, 'bad split (19, 19) right'); +} + +#[test] +fn test_equality() { + let b1 = bytes31_const::<0x01>(); + let b2 = bytes31_const::<0x0102>(); + let b3 = bytes31_const::<0x0201>(); + assert(b1 == b1, 'b1 != b1'); + assert(b2 == b2, 'b2 != b2'); + assert(b3 == b3, 'b3 != b3'); + assert(b1 != b2, 'b1 == b2'); + assert(b1 != b3, 'b1 == b3'); + assert(b2 != b3, 'b2 == b3'); + + let b4 = bytes31_const::<0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f>(); + let b5 = bytes31_const::<0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1e>(); + let b6 = bytes31_const::<0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e>(); + + assert(b4 == b4, 'b4 != b4'); + assert(b5 == b5, 'b5 != b5'); + assert(b6 == b6, 'b6 != b6'); + assert(b4 != b5, 'b4 == b5'); + assert(b4 != b6, 'b4 == b6'); + assert(b5 != b6, 'b5 == b6'); +} diff --git a/corelib/src/test/cmp_test.cairo b/corelib/src/test/cmp_test.cairo index bcfed59d8..91e964aa2 100644 --- a/corelib/src/test/cmp_test.cairo +++ b/corelib/src/test/cmp_test.cairo @@ -1,191 +1,203 @@ -use cmp::min; -use cmp::max; +use cmp::{max, min}; +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 +209,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 +226,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 +265,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 +278,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 f70118e54..ff3c1eb0c 100644 --- a/corelib/src/test/dict_test.cairo +++ b/corelib/src/test/dict_test.cairo @@ -1,72 +1,68 @@ -use box::BoxTrait; -use dict::{Felt252DictTrait, Felt252DictEntryTrait}; -use nullable::NullableTrait; -use traits::Index; +use dict::Felt252DictEntryTrait; +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: Felt252Dict = 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'); + let val10 = dict[10]; + let val11 = dict[11]; + let val12 = dict[12]; + 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 = 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 = 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 = 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 +71,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 = Default::default(); dict.insert(10, 110); let (entry, value) = dict.entry(10); } @@ -93,7 +89,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); @@ -101,28 +97,26 @@ fn test_dict_big_keys() { dict.insert(KEY4, 4); 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[KEY1], @1, 'KEY1'); + assert_eq(@dict[KEY2], @2, 'KEY2'); + assert_eq(@dict[KEY3], @3, 'KEY3'); + assert_eq(@dict[KEY4], @4, 'KEY4'); + assert_eq(@dict[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'); + let val10 = dict[10].deref(); + let val11 = dict[11].deref(); + let val12 = dict[12]; + 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 f4076bfa6..b5a57cf35 100644 --- a/corelib/src/test/ec_test.cairo +++ b/corelib/src/test/ec_test.cairo @@ -1,78 +1,75 @@ -use core::traits::Into; +use ec::{EcPointTrait, EcStateTrait}; use option::OptionTrait; -use ec::{ - ec_mul, ec_neg, ec_point_from_x, ec_point_from_x_nz, ec_point_is_zero, ec_point_new, - ec_point_new_nz, ec_point_non_zero, ec_point_try_new, ec_point_try_new_nz, ec_point_unwrap, - 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}; +use traits::{Into, TryInto}; #[test] #[should_panic] fn test_ec_from_zero() { - ec_point_from_x_nz(0).expect('Not on curve.'); + EcPointTrait::new_from_x(0).expect('Not on curve.'); } #[test] fn test_ec_operations() { // Beta + 2 is a square, and for x = 1 and alpha = 1, x^3 + alpha * x + beta = beta + 2. let beta_p2_root = 2487829544412206244690656897973144572467842667075005257202960243805141046681; - 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'); + let p = EcPointTrait::new_from_x(1).unwrap(); + let p_nz = p.try_into().unwrap(); + let (x, y) = p_nz.coordinates(); + 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'); + let mut state = EcStateTrait::init(); + state.add(p_nz); + let q = state.finalize_nz().expect('zero point'); + let (qx, qy) = q.coordinates(); + 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'); + let mut state = EcStateTrait::init(); + state.add_mul(1, p_nz); + let q3 = state.finalize_nz().expect('zero point'); + let (qx, qy) = q3.coordinates(); + 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 double_p = p.mul(2); + let (double_x, double_y) = double_p.try_into().unwrap().coordinates(); 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'); + let (sub_x, sub_y) = (double_p - p).try_into().unwrap().coordinates(); + 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.'); + assert((p - p).try_into().is_none(), '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'); + let (sub2_x, sub2_y) = (-p - p).try_into().unwrap().coordinates(); + assert_eq(@sub2_x, @double_x, 'bad x for (-p) - p'); + assert_eq(@sub2_y, @-double_y, 'bad y for (-p) - p'); } #[test] #[should_panic] fn test_bad_ec_point_creation() { - ec_point_new(0, 0); + EcPointTrait::new(0, 0).unwrap(); } #[test] fn test_ec_point_finalization_zero() { - let state = ec_state_init(); - let point_at_infinity = ec_state_try_finalize_nz(state); + let state = EcStateTrait::init(); + let point_at_infinity = state.finalize_nz(); assert(point_at_infinity.is_none(), 'Wrong point'); } @@ -112,21 +109,50 @@ fn test_ecdsa() { ); } +#[test] +#[available_gas(100000000)] +fn test_ecdsa_recover_public_key() { + let message_hash = 0x503f4bea29baee10b22a7f10bdc82dda071c977c1f25b8f3973d34e6b03b2c; + let signature_r = 0xbe96d72eb4f94078192c2e84d5230cde2a70f4b45c8797e2c907acff5060bb; + let signature_s = 0x677ae6bba6daf00d2631fab14c8acf24be6579f9d9e98f67aa7f2770e57a1f5; + assert_eq( + @ecdsa::recover_public_key(:message_hash, :signature_r, :signature_s, y_parity: false) + .unwrap(), + @0x7b7454acbe7845da996377f85eb0892044d75ae95d04d3325a391951f35d2ec, + 'recover_ecdsa_public_key failed' + ); + assert( + ecdsa::check_ecdsa_signature( + :message_hash, + public_key: ecdsa::recover_public_key( + :message_hash, :signature_r, :signature_s, y_parity: true + ) + .unwrap(), + :signature_r, + :signature_s + ), + 'ecdsa returned false' + ); +} + #[test] fn test_ec_mul() { - let p = ec_point_new( + let p = EcPointTrait::new( x: 336742005567258698661916498343089167447076063081786685068305785816009957563, y: 1706004133033694959518200210163451614294041810778629639790706933324248611779, - ); + ) + .unwrap(); let m = 2713877091499598330239944961141122840311015265600950719674787125185463975936; - let (x, y) = ec_point_unwrap(ec_point_non_zero(ec_mul(p, m))); + let (x, y) = p.mul(m).try_into().unwrap().coordinates(); - 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 d466179c7..52b65af34 100644 --- a/corelib/src/test/felt_test.cairo +++ b/corelib/src/test/felt_test.cairo @@ -1,19 +1,19 @@ -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 6c6ae2ad7..cc7a7a1a3 100644 --- a/corelib/src/test/hash_test.cairo +++ b/corelib/src/test/hash_test.cairo @@ -1,11 +1,10 @@ -use array::ArrayTrait; +use test::test_utils::{assert_eq, assert_ne}; #[test] fn test_pedersen_hash() { - assert( - hash::pedersen( - 1, 2 - ) == 2592987851775965742543459319508348457290966253241455514226127639100457844774, + assert_eq( + @pedersen::pedersen(1, 2), + @2592987851775965742543459319508348457290966253241455514226127639100457844774, 'Wrong hash value' ); } @@ -13,16 +12,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 +32,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 ae4d522ea..7f3ce08c0 100644 --- a/corelib/src/test/integer_test.cairo +++ b/corelib/src/test/integer_test.cairo @@ -1,38 +1,45 @@ -use core::traits::Into; -use traits::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 +110,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 +213,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 +316,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 +421,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 +504,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 +575,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 +586,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 +638,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 +740,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 +768,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 +800,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 +816,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 +865,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 +947,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 +968,19 @@ 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 { - low: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, high: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - }.into() == u256 { - high: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, low: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - }, + assert_eq( + @u256 { low: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, high: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF } + .into(), + @u256 { high: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, low: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF }, 'u256 into u256' ); } @@ -1059,10 +1110,533 @@ 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'); + assert_eq(@0x7f_felt252.try_into().unwrap(), @0x7f_i8, '0x7f is not i8'); + let v: Option = 0x80_felt252.try_into(); + assert(v.is_none(), '0x80 is i8'); + assert_eq(@(-0x80_felt252).try_into().unwrap(), @-0x80_i8, '-0x80 is not i8'); + let v: Option = (-0x81_felt252).try_into(); + assert(v.is_none(), '-0x81 is i8'); + assert_eq(@(1_i8 + 3_i8), @4_i8, '1 + 3 == 4'); + assert_eq(@(3_i8 + 6_i8), @9_i8, '3 + 6 == 9'); + assert_eq(@(3_i8 - 1_i8), @2_i8, '3 - 1 == 2'); + assert_eq(@(121_i8 - 21_i8), @100_i8, '121-21=100'); + assert_eq(@(-1_i8 + -3_i8), @-4_i8, '-1 + -3 == -4'); + assert_eq(@(-3_i8 + -6_i8), @-9_i8, '-3 + -6 == -9'); + assert_eq(@(-3_i8 - -1_i8), @-2_i8, '-3 - -1 == -2'); + assert_eq(@(-121_i8 - -21_i8), @-100_i8, '-121--21=-100'); + assert_eq(@(1_i8 * 3_i8), @3_i8, '1 * 3 == 3'); + assert_eq(@(2_i8 * 4_i8), @8_i8, '2 * 4 == 8'); + assert_eq(@(-1_i8 * 3_i8), @-3_i8, '-1 * 3 == 3'); + assert_eq(@(-2_i8 * 4_i8), @-8_i8, '-2 * 4 == 8'); + assert_eq(@(1_i8 * -3_i8), @-3_i8, '1 * -3 == -3'); + assert_eq(@(2_i8 * -4_i8), @-8_i8, '2 * -4 == -8'); + assert_eq(@(-1_i8 * -3_i8), @3_i8, '-1 * -3 == 3'); + assert_eq(@(-2_i8 * -4_i8), @8_i8, '-2 * -4 == 8'); + assert_lt(1_i8, 4_i8, '1 < 4'); + assert_le(1_i8, 4_i8, '1 <= 4'); + assert(!(4_i8 < 4_i8), '!(4 < 4)'); + assert_le(5_i8, 5_i8, '5 <= 5'); + assert(!(5_i8 <= 4_i8), '!(5 <= 8)'); + assert_gt(5_i8, 2_i8, '5 > 2'); + assert_ge(5_i8, 2_i8, '5 >= 2'); + assert(!(3_i8 > 3_i8), '!(3 > 3)'); + assert_ge(3_i8, 3_i8, '3 >= 3'); +} + +#[test] +#[should_panic(expected: ('i8_sub Underflow',))] +fn test_i8_sub_underflow_1() { + -0x80_i8 - 1_i8; +} + +#[test] +#[should_panic(expected: ('i8_sub Underflow',))] +fn test_i8_sub_underflow_2() { + -0x80_i8 - 3_i8; +} + +#[test] +#[should_panic(expected: ('i8_sub Underflow',))] +fn test_i8_sub_underflow_3() { + -0x7f_i8 - 3_i8; +} + +#[test] +#[should_panic(expected: ('i8_sub Underflow',))] +fn test_i8_sub_underflow_4() { + -0x32_i8 - 0x7d_i8; +} + +#[test] +#[should_panic(expected: ('i8_sub Overflow',))] +fn test_i8_sub_overflow() { + 0x32_i8 - -0x7d_i8; +} + +#[test] +#[should_panic(expected: ('i8_add Overflow',))] +fn test_i8_add_overflow_1() { + 0x40_i8 + 0x40_i8; +} + +#[test] +#[should_panic(expected: ('i8_add Overflow',))] +fn test_i8_add_overflow_2() { + 0x64_i8 + 0x1e_i8; +} + +#[test] +#[should_panic(expected: ('i8_add Underflow',))] +fn test_i8_add_underflow() { + -0x64_i8 + -0x1e_i8; +} + +#[test] +#[should_panic] +fn test_i8_mul_overflow_1() { + 0x10_i8 * 0x10_i8; +} + +#[test] +#[should_panic] +fn test_i8_mul_overflow_2() { + 0x11_i8 * 0x10_i8; +} + +#[test] +#[should_panic] +fn test_i8_mul_overflow_3() { + 2_i8 * 0x40_i8; +} + +#[test] +fn test_i16_operators() { + assert_eq(@1_i16, @1_i16, '1 == 1'); + assert_ne(@1_i16, @2_i16, '1 != 2'); + assert_eq(@0x7fff_felt252.try_into().unwrap(), @0x7fff_i16, '0x7fff is not i16'); + let v: Option = 0x8000_felt252.try_into(); + assert(v.is_none(), '0x8000 is i16'); + assert_eq(@(-0x8000_felt252).try_into().unwrap(), @-0x8000_i16, '-0x8000 is not i16'); + let v: Option = (-0x8001_felt252).try_into(); + assert(v.is_none(), '-0x8001 is i16'); + assert_eq(@(1_i16 + 3_i16), @4_i16, '1 + 3 == 4'); + assert_eq(@(3_i16 + 6_i16), @9_i16, '3 + 6 == 9'); + assert_eq(@(3_i16 - 1_i16), @2_i16, '3 - 1 == 2'); + assert_eq(@(231_i16 - 131_i16), @100_i16, '231-131=100'); + assert_eq(@(-1_i16 + -3_i16), @-4_i16, '-1 + -3 == -4'); + assert_eq(@(-3_i16 + -6_i16), @-9_i16, '-3 + -6 == -9'); + assert_eq(@(-3_i16 - -1_i16), @-2_i16, '-3 - -1 == -2'); + assert_eq(@(-231_i16 - -131_i16), @-100_i16, '-231--131=-100'); + assert_eq(@(1_i16 * 3_i16), @3_i16, '1 * 3 == 3'); + assert_eq(@(2_i16 * 4_i16), @8_i16, '2 * 4 == 8'); + assert_eq(@(-1_i16 * 3_i16), @-3_i16, '-1 * 3 == 3'); + assert_eq(@(-2_i16 * 4_i16), @-8_i16, '-2 * 4 == 8'); + assert_eq(@(1_i16 * -3_i16), @-3_i16, '1 * -3 == -3'); + assert_eq(@(2_i16 * -4_i16), @-8_i16, '2 * -4 == -8'); + assert_eq(@(-1_i16 * -3_i16), @3_i16, '-1 * -3 == 3'); + assert_eq(@(-2_i16 * -4_i16), @8_i16, '-2 * -4 == 8'); + assert_lt(1_i16, 4_i16, '1 < 4'); + assert_le(1_i16, 4_i16, '1 <= 4'); + assert(!(4_i16 < 4_i16), '!(4 < 4)'); + assert_le(5_i16, 5_i16, '5 <= 5'); + assert(!(5_i16 <= 4_i16), '!(5 <= 8)'); + assert_gt(5_i16, 2_i16, '5 > 2'); + assert_ge(5_i16, 2_i16, '5 >= 2'); + assert(!(3_i16 > 3_i16), '!(3 > 3)'); + assert_ge(3_i16, 3_i16, '3 >= 3'); +} + +#[test] +#[should_panic(expected: ('i16_sub Underflow',))] +fn test_i16_sub_underflow_1() { + -0x8000_i16 - 1_i16; +} + +#[test] +#[should_panic(expected: ('i16_sub Underflow',))] +fn test_i16_sub_underflow_2() { + -0x8000_i16 - 3_i16; +} + +#[test] +#[should_panic(expected: ('i16_sub Underflow',))] +fn test_i16_sub_underflow_3() { + -0x7fff_i16 - 3_i16; +} + +#[test] +#[should_panic(expected: ('i16_sub Underflow',))] +fn test_i16_sub_underflow_4() { + -0x3200_i16 - 0x7d00_i16; +} + +#[test] +#[should_panic(expected: ('i16_sub Overflow',))] +fn test_i16_sub_overflow() { + 0x3200_i16 - -0x7d00_i16; +} + +#[test] +#[should_panic(expected: ('i16_add Overflow',))] +fn test_i16_add_overflow_1() { + 0x4000_i16 + 0x4000_i16; +} + +#[test] +#[should_panic(expected: ('i16_add Overflow',))] +fn test_i16_add_overflow_2() { + 0x6400_i16 + 0x1e00_i16; +} + +#[test] +#[should_panic(expected: ('i16_add Underflow',))] +fn test_i16_add_underflow() { + -0x6400_i16 + -0x1e00_i16; +} + +#[test] +#[should_panic] +fn test_i16_mul_overflow_1() { + 0x1000_i16 * 0x1000_i16; +} + +#[test] +#[should_panic] +fn test_i16_mul_overflow_2() { + 0x1100_i16 * 0x1000_i16; +} + +#[test] +#[should_panic] +fn test_i16_mul_overflow_3() { + 2_i16 * 0x4000_i16; +} + +#[test] +fn test_i32_operators() { + assert_eq(@1_i32, @1_i32, '1 == 1'); + assert_ne(@1_i32, @2_i32, '1 != 2'); + assert_eq(@0x7fffffff_felt252.try_into().unwrap(), @0x7fffffff_i32, '0x7fffffff is not i32'); + let v: Option = 0x80000000_felt252.try_into(); + assert(v.is_none(), '0x80000000 is i32'); + assert_eq(@(-0x80000000_felt252).try_into().unwrap(), @-0x80000000_i32, '-0x8000 is not i32'); + let v: Option = (-0x80000001_felt252).try_into(); + assert(v.is_none(), '-0x80000001 is i32'); + assert_eq(@(1_i32 + 3_i32), @4_i32, '1 + 3 == 4'); + assert_eq(@(3_i32 + 6_i32), @9_i32, '3 + 6 == 9'); + assert_eq(@(3_i32 - 1_i32), @2_i32, '3 - 1 == 2'); + assert_eq(@(231_i32 - 131_i32), @100_i32, '231-131=100'); + assert_eq(@(-1_i32 + -3_i32), @-4_i32, '-1 + -3 == -4'); + assert_eq(@(-3_i32 + -6_i32), @-9_i32, '-3 + -6 == -9'); + assert_eq(@(-3_i32 - -1_i32), @-2_i32, '-3 - -1 == -2'); + assert_eq(@(-231_i32 - -131_i32), @-100_i32, '-231--131=-100'); + assert_eq(@(1_i32 * 3_i32), @3_i32, '1 * 3 == 3'); + assert_eq(@(2_i32 * 4_i32), @8_i32, '2 * 4 == 8'); + assert_eq(@(-1_i32 * 3_i32), @-3_i32, '-1 * 3 == 3'); + assert_eq(@(-2_i32 * 4_i32), @-8_i32, '-2 * 4 == 8'); + assert_eq(@(1_i32 * -3_i32), @-3_i32, '1 * -3 == -3'); + assert_eq(@(2_i32 * -4_i32), @-8_i32, '2 * -4 == -8'); + assert_eq(@(-1_i32 * -3_i32), @3_i32, '-1 * -3 == 3'); + assert_eq(@(-2_i32 * -4_i32), @8_i32, '-2 * -4 == 8'); + assert_lt(1_i32, 4_i32, '1 < 4'); + assert_le(1_i32, 4_i32, '1 <= 4'); + assert(!(4_i32 < 4_i32), '!(4 < 4)'); + assert_le(5_i32, 5_i32, '5 <= 5'); + assert(!(5_i32 <= 4_i32), '!(5 <= 8)'); + assert_gt(5_i32, 2_i32, '5 > 2'); + assert_ge(5_i32, 2_i32, '5 >= 2'); + assert(!(3_i32 > 3_i32), '!(3 > 3)'); + assert_ge(3_i32, 3_i32, '3 >= 3'); +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow',))] +fn test_i32_sub_underflow_1() { + -0x80000000_i32 - 1_i32; +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow',))] +fn test_i32_sub_underflow_2() { + -0x80000000_i32 - 3_i32; +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow',))] +fn test_i32_sub_underflow_3() { + -0x7fffffff_i32 - 3_i32; +} + +#[test] +#[should_panic(expected: ('i32_sub Underflow',))] +fn test_i32_sub_underflow_4() { + -0x32000000_i32 - 0x7d000000_i32; +} + +#[test] +#[should_panic(expected: ('i32_sub Overflow',))] +fn test_i32_sub_overflow() { + 0x32000000_i32 - -0x7d000000_i32; +} + +#[test] +#[should_panic(expected: ('i32_add Overflow',))] +fn test_i32_add_overflow_1() { + 0x40000000_i32 + 0x40000000_i32; +} + +#[test] +#[should_panic(expected: ('i32_add Overflow',))] +fn test_i32_add_overflow_2() { + 0x64000000_i32 + 0x1e000000_i32; +} + +#[test] +#[should_panic(expected: ('i32_add Underflow',))] +fn test_i32_add_underflow() { + -0x64000000_i32 + -0x1e000000_i32; +} + +#[test] +#[should_panic] +fn test_i32_mul_overflow_1() { + 0x10000000_i32 * 0x10000000_i32; +} + +#[test] +#[should_panic] +fn test_i32_mul_overflow_2() { + 0x11000000_i32 * 0x10000000_i32; +} + +#[test] +#[should_panic] +fn test_i32_mul_overflow_3() { + 2_i32 * 0x40000000_i32; +} + +#[test] +fn test_i64_operators() { + assert_eq(@1_i64, @1_i64, '1 == 1'); + assert_ne(@1_i64, @2_i64, '1 != 2'); + assert_eq( + @0x7fffffffffffffff_felt252.try_into().unwrap(), + @0x7fffffffffffffff_i64, + '0x7fffffffffffffff is not i64' + ); + let v: Option = 0x8000000000000000_felt252.try_into(); + assert(v.is_none(), '0x8000000000000000 is i64'); + assert_eq( + @(-0x8000000000000000_felt252).try_into().unwrap(), + @-0x8000000000000000_i64, + '-0x8000000000000000 is not i64' + ); + let v: Option = (-0x8000000000000001_felt252).try_into(); + assert(v.is_none(), '-0x8000000000000001 is i64'); + assert_eq(@(1_i64 + 3_i64), @4_i64, '1 + 3 == 4'); + assert_eq(@(3_i64 + 6_i64), @9_i64, '3 + 6 == 9'); + assert_eq(@(3_i64 - 1_i64), @2_i64, '3 - 1 == 2'); + assert_eq(@(231_i64 - 131_i64), @100_i64, '231-131=100'); + assert_eq(@(-1_i64 + -3_i64), @-4_i64, '-1 + -3 == -4'); + assert_eq(@(-3_i64 + -6_i64), @-9_i64, '-3 + -6 == -9'); + assert_eq(@(-3_i64 - -1_i64), @-2_i64, '-3 - -1 == -2'); + assert_eq(@(-231_i64 - -131_i64), @-100_i64, '-231--131=-100'); + assert_eq(@(1_i64 * 3_i64), @3_i64, '1 * 3 == 3'); + assert_eq(@(2_i64 * 4_i64), @8_i64, '2 * 4 == 8'); + assert_eq(@(-1_i64 * 3_i64), @-3_i64, '-1 * 3 == 3'); + assert_eq(@(-2_i64 * 4_i64), @-8_i64, '-2 * 4 == 8'); + assert_eq(@(1_i64 * -3_i64), @-3_i64, '1 * -3 == -3'); + assert_eq(@(2_i64 * -4_i64), @-8_i64, '2 * -4 == -8'); + assert_eq(@(-1_i64 * -3_i64), @3_i64, '-1 * -3 == 3'); + assert_eq(@(-2_i64 * -4_i64), @8_i64, '-2 * -4 == 8'); + assert_lt(1_i64, 4_i64, '1 < 4'); + assert_le(1_i64, 4_i64, '1 <= 4'); + assert(!(4_i64 < 4_i64), '!(4 < 4)'); + assert_le(5_i64, 5_i64, '5 <= 5'); + assert(!(5_i64 <= 4_i64), '!(5 <= 8)'); + assert_gt(5_i64, 2_i64, '5 > 2'); + assert_ge(5_i64, 2_i64, '5 >= 2'); + assert(!(3_i64 > 3_i64), '!(3 > 3)'); + assert_ge(3_i64, 3_i64, '3 >= 3'); +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow',))] +fn test_i64_sub_underflow_1() { + -0x8000000000000000_i64 - 1_i64; +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow',))] +fn test_i64_sub_underflow_2() { + -0x8000000000000000_i64 - 3_i64; +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow',))] +fn test_i64_sub_underflow_3() { + -0x7fffffffffffffff_i64 - 3_i64; +} + +#[test] +#[should_panic(expected: ('i64_sub Underflow',))] +fn test_i64_sub_underflow_4() { + -0x3200000000000000_i64 - 0x7d00000000000000_i64; +} + +#[test] +#[should_panic(expected: ('i64_sub Overflow',))] +fn test_i64_sub_overflow() { + 0x3200000000000000_i64 - -0x7d00000000000000_i64; +} + +#[test] +#[should_panic(expected: ('i64_add Overflow',))] +fn test_i64_add_overflow_1() { + 0x4000000000000000_i64 + 0x4000000000000000_i64; +} + +#[test] +#[should_panic(expected: ('i64_add Overflow',))] +fn test_i64_add_overflow_2() { + 0x6400000000000000_i64 + 0x1e00000000000000_i64; +} + +#[test] +#[should_panic(expected: ('i64_add Underflow',))] +fn test_i64_add_underflow() { + -0x6400000000000000_i64 + -0x1e00000000000000_i64; +} + +#[test] +#[should_panic] +fn test_i64_mul_overflow_1() { + 0x1000000000000000_i64 * 0x1000000000000000_i64; +} + +#[test] +#[should_panic] +fn test_i64_mul_overflow_2() { + 0x1100000000000000_i64 * 0x1000000000000000_i64; +} + +#[test] +#[should_panic] +fn test_i64_mul_overflow_3() { + 2_i64 * 0x4000000000000000_i64; +} + +#[test] +fn test_i128_operators() { + assert_eq(@1_i128, @1_i128, '1 == 1'); + assert_ne(@1_i128, @2_i128, '1 != 2'); + assert_eq( + @0x7fffffffffffffffffffffffffffffff_felt252.try_into().unwrap(), + @0x7fffffffffffffffffffffffffffffff_i128, + '0x7f..f is not i128' + ); + let v: Option = 0x80000000000000000000000000000000_felt252.try_into(); + assert(v.is_none(), '0x80..0 is i128'); + assert_eq( + @(-0x80000000000000000000000000000000_felt252).try_into().unwrap(), + @-0x80000000000000000000000000000000_i128, + '-0x80..0 is not i128' + ); + let v: Option = (-0x80000000000000000000000000000001_felt252).try_into(); + assert(v.is_none(), '-0x80..01 is i128'); + assert_eq(@(1_i128 + 3_i128), @4_i128, '1 + 3 == 4'); + assert_eq(@(3_i128 + 6_i128), @9_i128, '3 + 6 == 9'); + assert_eq(@(3_i128 - 1_i128), @2_i128, '3 - 1 == 2'); + assert_eq(@(231_i128 - 131_i128), @100_i128, '231-131=100'); + assert_eq(@(-1_i128 + -3_i128), @-4_i128, '-1 + -3 == -4'); + assert_eq(@(-3_i128 + -6_i128), @-9_i128, '-3 + -6 == -9'); + assert_eq(@(-3_i128 - -1_i128), @-2_i128, '-3 - -1 == -2'); + assert_eq(@(-231_i128 - -131_i128), @-100_i128, '-231--131=-100'); + assert_lt(1_i128, 4_i128, '1 < 4'); + assert_le(1_i128, 4_i128, '1 <= 4'); + assert(!(4_i128 < 4_i128), '!(4 < 4)'); + assert_le(5_i128, 5_i128, '5 <= 5'); + assert(!(5_i128 <= 4_i128), '!(5 <= 8)'); + assert_gt(5_i128, 2_i128, '5 > 2'); + assert_ge(5_i128, 2_i128, '5 >= 2'); + assert(!(3_i128 > 3_i128), '!(3 > 3)'); + assert_ge(3_i128, 3_i128, '3 >= 3'); +} + +#[test] +#[should_panic] +fn test_i128_sub_underflow_1() { + -0x80000000000000000000000000000000_i128 - 1_i128; +} + +#[test] +#[should_panic(expected: ('i128_sub Underflow',))] +fn test_i128_sub_underflow_2() { + -0x80000000000000000000000000000000_i128 - 3_i128; +} + +#[test] +#[should_panic(expected: ('i128_sub Underflow',))] +fn test_i128_sub_underflow_3() { + -0x7fffffffffffffffffffffffffffffff_i128 - 3_i128; +} + +#[test] +#[should_panic(expected: ('i128_sub Underflow',))] +fn test_i128_sub_underflow_4() { + -0x32000000000000000000000000000000_i128 - 0x7d000000000000000000000000000000_i128; +} + +#[test] +#[should_panic(expected: ('i128_sub Overflow',))] +fn test_i128_sub_overflow() { + 0x32000000000000000000000000000000_i128 - -0x7d000000000000000000000000000000_i128; +} + +#[test] +#[should_panic(expected: ('i128_add Overflow',))] +fn test_i128_add_overflow_1() { + 0x40000000000000000000000000000000_i128 + 0x40000000000000000000000000000000_i128; +} + +#[test] +#[should_panic(expected: ('i128_add Overflow',))] +fn test_i128_add_overflow_2() { + 0x64000000000000000000000000000000_i128 + 0x1e000000000000000000000000000000_i128; +} + +#[test] +#[should_panic(expected: ('i128_add Underflow',))] +fn test_i128_add_underflow() { + -0x64000000000000000000000000000000_i128 + -0x1e000000000000000000000000000000_i128; +} + +#[test] +fn test_signed_int_diff() { + assert_eq(@integer::i8_diff(3, 3).unwrap(), @0, 'i8: 3 - 3 == 0'); + assert_eq(@integer::i8_diff(4, 3).unwrap(), @1, 'i8: 4 - 3 == 1'); + assert_eq(@integer::i8_diff(3, 5).unwrap_err(), @~(2 - 1), 'i8: 3 - 5 == -2'); + assert_eq(@integer::i16_diff(3, 3).unwrap(), @0, 'i16: 3 - 3 == 0'); + assert_eq(@integer::i16_diff(4, 3).unwrap(), @1, 'i16: 4 - 3 == 1'); + assert_eq(@integer::i16_diff(3, 5).unwrap_err(), @~(2 - 1), 'i16: 3 - 5 == -2'); + assert_eq(@integer::i32_diff(3, 3).unwrap(), @0, 'i32: 3 - 3 == 0'); + assert_eq(@integer::i32_diff(4, 3).unwrap(), @1, 'i32: 4 - 3 == 1'); + assert_eq(@integer::i32_diff(3, 5).unwrap_err(), @~(2 - 1), 'i32: 3 - 5 == -2'); + assert_eq(@integer::i64_diff(3, 3).unwrap(), @0, 'i64: 3 - 3 == 0'); + assert_eq(@integer::i64_diff(4, 3).unwrap(), @1, 'i64: 4 - 3 == 1'); + assert_eq(@integer::i64_diff(3, 5).unwrap_err(), @~(2 - 1), 'i64: 3 - 5 == -2'); + assert_eq(@integer::i128_diff(3, 3).unwrap(), @0, 'i128: 3 - 3 == 0'); + assert_eq(@integer::i128_diff(4, 3).unwrap(), @1, 'i128: 4 - 3 == 1'); + assert_eq(@integer::i128_diff(3, 5).unwrap_err(), @~(2 - 1), 'i128: 3 - 5 == -2'); +} diff --git a/corelib/src/test/keccak_test.cairo b/corelib/src/test/keccak_test.cairo index 09fc6d4d3..ad4a36811 100644 --- a/corelib/src/test/keccak_test.cairo +++ b/corelib/src/test/keccak_test.cairo @@ -1,32 +1,13 @@ -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 +15,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 000000000..ef3342878 --- /dev/null +++ b/corelib/src/test/math_test.cairo @@ -0,0 +1,95 @@ +#[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 f50d6e39e..11173e7a4 100644 --- a/corelib/src/test/plugins_test.cairo +++ b/corelib/src/test/plugins_test.cairo @@ -1,11 +1,8 @@ -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 +12,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 000000000..537cd9bff --- /dev/null +++ b/corelib/src/test/secp256k1_test.cairo @@ -0,0 +1,113 @@ +use starknet::{ + eth_address::U256IntoEthAddress, EthAddress, secp256k1::Secp256k1Impl, SyscallResultTrait +}; +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 = 27; + 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 000000000..ef1bdb3ae --- /dev/null +++ b/corelib/src/test/secp256r1_test.cairo @@ -0,0 +1,93 @@ +use starknet::{ + eth_address::U256IntoEthAddress, EthAddress, secp256r1::Secp256r1Impl, SyscallResultTrait +}; +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 000000000..bd001de83 --- /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 117331bf1..7d80eb6d5 100644 --- a/corelib/src/test/testing_test.cairo +++ b/corelib/src/test/testing_test.cairo @@ -1,12 +1,14 @@ +use test::test_utils::{assert_eq, assert_ne, assert_gt}; + #[test] -#[should_panic(expected: ('panic_with_felt252()', ))] +#[should_panic(expected: ('panic_with_felt252()',))] fn test_panic_with_felt252() { // No semicolon here: Missing implementation for core::traits::Drop:: panic_with_felt252('panic_with_felt252()') } #[test] -#[should_panic(expected: ('assert(false)', ))] +#[should_panic(expected: ('assert(false)',))] fn test_assert_false() { assert(false, 'assert(false)'); } @@ -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 7e07f6810..1ae21135e 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. @@ -140,7 +166,7 @@ trait Felt252DictValue { // Tuple Copy impls. impl TupleSize0Copy of Copy<()>; -impl TupleSize1Copy> of Copy<(E0, )>; +impl TupleSize1Copy> of Copy<(E0,)>; impl TupleSize2Copy, impl E1Copy: Copy> of Copy<(E0, E1)>; @@ -162,7 +188,7 @@ impl TupleSize4Copy< // Tuple Drop impls. impl TupleSize0Drop of Drop<()>; -impl TupleSize1Drop> of Drop<(E0, )>; +impl TupleSize1Drop> of Drop<(E0,)>; impl TupleSize2Drop, impl E1Drop: Drop> of Drop<(E0, E1)>; @@ -184,24 +210,135 @@ 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, )> { +impl TupleSize1PartialEq> of PartialEq<(E0,)> { #[inline(always)] - fn eq(lhs: (E0, ), rhs: (E0, )) -> bool { - let (lhs, ) = lhs; - let (rhs, ) = rhs; + 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) + } +} + +// Tuple Default impls. +impl TupleSize0Default of Default<()> { + fn default() -> () { + () + } +} + +impl TupleSize1Default> of Default<(E0,)> { + fn default() -> (E0,) { + (E0Default::default(),) + } +} + +impl TupleSize2Default< + E0, E1, impl E0Default: Default, impl E0Drop: Drop, impl E1Default: Default +> of Default<(E0, E1)> { + fn default() -> (E0, E1) { + (E0Default::default(), E1Default::default()) + } +} + +impl TupleSize3Default< + E0, + E1, + E2, + impl E0Default: Default, + impl E0Drop: Drop, + impl E1Default: Default, + impl E1Drop: Drop, + impl E2Default: Default +> of Default<(E0, E1, E2)> { + fn default() -> (E0, E1, E2) { + (E0Default::default(), E1Default::default(), E2Default::default()) + } +} + +impl TupleSize4Default< + E0, + E1, + E2, + E3, + impl E0Default: Default, + impl E0Drop: Drop, + impl E1Default: Default, + impl E1Drop: Drop, + impl E2Default: Default, + impl E2Drop: Drop, + impl E3Default: Default +> of Default<(E0, E1, E2, E3)> { + fn default() -> (E0, E1, E2, E3) { + (E0Default::default(), E1Default::default(), E2Default::default(), E3Default::default()) + } +} diff --git a/corelib/src/zeroable.cairo b/corelib/src/zeroable.cairo index 9f78eae68..5f70e85d7 100644 --- a/corelib/src/zeroable.cairo +++ b/corelib/src/zeroable.cairo @@ -25,11 +25,11 @@ impl Felt252Zeroable of Zeroable { // === NonZero === +#[derive(Copy, Drop)] 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/exercises/arrays/arrays2.cairo b/exercises/arrays/arrays2.cairo index a8418fa46..5c724df2d 100644 --- a/exercises/arrays/arrays2.cairo +++ b/exercises/arrays/arrays2.cairo @@ -15,8 +15,9 @@ fn create_array() -> Array { a } -fn remove_element_from_array(ref a: Array){ - //TODO something to do here...Is there an array method I can use? +fn remove_element_from_array( + ref a: Array +) { //TODO something to do here...Is there an array method I can use? } #[test] diff --git a/exercises/enums/enums1.cairo b/exercises/enums/enums1.cairo index dae3e0423..f584b1109 100644 --- a/exercises/enums/enums1.cairo +++ b/exercises/enums/enums1.cairo @@ -15,7 +15,7 @@ fn main() { Message::ChangeColor(()).print(); } -impl MessagePrintImpl of PrintTrait:: { +impl MessagePrintImpl of PrintTrait { fn print(self: Message) { match self { Message::Quit(()) => ('Quit').print(), diff --git a/exercises/enums/enums2.cairo b/exercises/enums/enums2.cairo index aaba09ddd..5fca254b6 100644 --- a/exercises/enums/enums2.cairo +++ b/exercises/enums/enums2.cairo @@ -28,7 +28,7 @@ trait MessageTrait { fn call(self: T); } -impl MessageImpl of MessageTrait:: { +impl MessageImpl of MessageTrait { fn call(self: Message) { self.print() } @@ -52,7 +52,7 @@ fn print_messages_recursive(messages: Array, index: u32) { } -impl MessagePrintImpl of PrintTrait:: { +impl MessagePrintImpl of PrintTrait { fn print(self: Message) { ('___MESSAGE BEGINS___').print(); match self { @@ -62,9 +62,7 @@ impl MessagePrintImpl of PrintTrait:: { a.print(); b.print(); }, - Message::ChangeColor(( - red, green, blue - )) => { + Message::ChangeColor((red, green, blue)) => { red.print(); green.print(); blue.print(); diff --git a/exercises/enums/enums3.cairo b/exercises/enums/enums3.cairo index 79915f2d8..05962ed76 100644 --- a/exercises/enums/enums3.cairo +++ b/exercises/enums/enums3.cairo @@ -59,12 +59,10 @@ impl StateImpl of StateTrait { #[test] fn test_match_message_call() { - let mut state = State { - quit: false, position: Point { x: 0, y: 0}, color: (0, 0, 0), - }; + let mut state = State { quit: false, position: Point { x: 0, y: 0 }, color: (0, 0, 0), }; state.process(Message::ChangeColor((255, 0, 255))); state.process(Message::Echo('hello world')); - state.process(Message::Move(Point { x: 10, y: 15})); + state.process(Message::Move(Point { x: 10, y: 15 })); state.process(Message::Quit(())); assert(state.color == (255, 0, 255), 'wrong color'); diff --git a/exercises/if/if1.cairo b/exercises/if/if1.cairo index 091f4d639..981f4a0a9 100644 --- a/exercises/if/if1.cairo +++ b/exercises/if/if1.cairo @@ -3,11 +3,10 @@ // I AM NOT DONE -fn bigger(a: usize, b: usize) -> usize { - // Complete this function to return the bigger number! - // Do not use: - // - another function call - // - additional variables +fn bigger(a: usize, b: usize) -> usize { // Complete this function to return the bigger number! +// Do not use: +// - another function call +// - additional variables } // Don't mind this for now :) diff --git a/exercises/intro/intro2.cairo b/exercises/intro/intro2.cairo index fb147fa3d..29604faa8 100644 --- a/exercises/intro/intro2.cairo +++ b/exercises/intro/intro2.cairo @@ -1,2 +1,4 @@ // I AM NOT DONE // This exercise won't compile... Can you make it compile? + + diff --git a/exercises/loops/loops2.cairo b/exercises/loops/loops2.cairo index 612ecf706..b2f1bdc82 100644 --- a/exercises/loops/loops2.cairo +++ b/exercises/loops/loops2.cairo @@ -9,7 +9,8 @@ fn test_loop() { let mut counter = 0; let result = loop { - if counter == 5 {//TODO return a value from the loop + if counter == 5 { + //TODO return a value from the loop } counter += 1; } diff --git a/exercises/operations/operations2.cairo b/exercises/operations/operations2.cairo index 7072ec082..9754ed59b 100644 --- a/exercises/operations/operations2.cairo +++ b/exercises/operations/operations2.cairo @@ -5,8 +5,7 @@ // I AM NOT DONE - -fn modulus(x : u8, y: u8) -> u8 { +fn modulus(x: u8, y: u8) -> u8 { // calculate the modulus of x and y // FILL ME res @@ -27,30 +26,30 @@ fn multiplication(x: u64, y: u64) -> u64 { // Do not change the tests #[test] -fn test_modulus(){ +fn test_modulus() { let res = modulus(16, 2); - assert(res==0, 'Error message'); + assert(res == 0, 'Error message'); let res = modulus(17, 3); - assert(res==2, 'Error message'); + assert(res == 2, 'Error message'); } #[test] -fn test_floor_division(){ +fn test_floor_division() { let res = floor_division(160, 2); - assert(res==80, 'Error message'); + assert(res == 80, 'Error message'); let res = floor_division(21, 4); - assert(res==5, 'Error message'); + assert(res == 5, 'Error message'); } #[test] -fn test_mul(){ +fn test_mul() { let res = multiplication(16, 2); - assert(res==32, 'Error message'); + assert(res == 32, 'Error message'); let res = multiplication(21, 4); - assert(res==84, 'Error message'); + assert(res == 84, 'Error message'); } #[test] diff --git a/exercises/options/options1.cairo b/exercises/options/options1.cairo index c9fdeb640..f9f0b4909 100644 --- a/exercises/options/options1.cairo +++ b/exercises/options/options1.cairo @@ -8,10 +8,11 @@ use option::OptionTrait; // This function returns how much icecream there is left in the fridge. // If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them // all, so there'll be no more left :( -fn maybe_icecream(time_of_day: usize) -> Option { - // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a value of 0 - // The Option output should gracefully handle cases where time_of_day > 23. - // TODO: Complete the function body - remember to return an Option! +fn maybe_icecream( + time_of_day: usize +) -> Option { // We use the 24-hour system here, so 10PM is a value of 22 and 12AM is a value of 0 +// The Option output should gracefully handle cases where time_of_day > 23. +// TODO: Complete the function body - remember to return an Option! } @@ -29,4 +30,4 @@ fn raw_value() { // TODO: Fix this test. How do you get at the value contained in the Option? let icecreams = maybe_icecream(12); assert(icecreams == 5, 'err_6'); -} \ No newline at end of file +} diff --git a/exercises/options/options3.cairo b/exercises/options/options3.cairo index 0a87dda0e..d50bda2c1 100644 --- a/exercises/options/options3.cairo +++ b/exercises/options/options3.cairo @@ -26,7 +26,7 @@ fn display_grades(student: @Student, index: usize) { }, } - if index == 0{ + if index == 0 { let mut msg = ArrayTrait::new(); msg.append(*student.name); msg.append('\'s grades:'); diff --git a/exercises/primitive_types/primitive_types4.cairo b/exercises/primitive_types/primitive_types4.cairo index 867b874b1..bbefa6f47 100644 --- a/exercises/primitive_types/primitive_types4.cairo +++ b/exercises/primitive_types/primitive_types4.cairo @@ -18,12 +18,10 @@ fn sum_big_numbers(x: u8, y: u8) -> u8 { x + y } -fn convert_to_felt(x: u8) -> felt252 { - //TODO return x as a felt252. +fn convert_to_felt(x: u8) -> felt252 { //TODO return x as a felt252. } -fn convert_felt_to_u8(x: felt252) -> u8 { - //TODO return x as a u8. +fn convert_felt_to_u8(x: felt252) -> u8 { //TODO return x as a u8. } #[test] diff --git a/exercises/starknet/README.md b/exercises/starknet/README.md index 676d1b72c..f1c05de21 100644 --- a/exercises/starknet/README.md +++ b/exercises/starknet/README.md @@ -1,88 +1,13 @@ # Starknet Smart Contracts -A Starknet contract can be created by annotating a Cairo module with the `#[contract]` attribute +Writing smart contracts in Cairo involves using traits, impls and mods annotated with certain attributes to mark them as smart contract components. -Starknet contract don't require a `main` function. -Functions in the contract module may be annotated as a `#[view]` or an `#[external]` function. External/View functions can be called by the users of Starknet, and by other contracts. The functions without these annotations are internal and cannot be accessed by users nor by other contract. +[Introduction to smart-contracts](https://book.cairo-lang.org/ch99-01-01-introduction-to-smart-contracts.html) -## Contract annotations +[A simple contract](https://book.cairo-lang.org/ch99-01-02-a-simple-contract.html) -Here are the attributes/decorators available to annotate smart contracts. +[A deep dive into Starknet Contract](https://book.cairo-lang.org/ch99-01-03-00-a-deeper-dive-into-contracts.html) -| Annotation | Target | Description | -| ---------------- | ---------- | ----------------------------------------------- | -| `#[contract]` | `module` | Marks a module as a contract | -| `#[constructor]` | `function` | Contract constructor, runs (only) on deployment | -| `#[view]` | `function` | An endpoint that doesn't modify state | -| `#[external]` | `function` | An endpoint that modifies state | +[Cross-contract interactions](https://cairo-book.github.io/ch99-02-00-abis-and-cross-contract-interactions.html) -## Storage - -Contract storage is represented as a `struct` with name `Storage`. - -```rust -struct Storage { - store: felt252, // Can be any type - - // A u32 mapping to a bool - mapped: LegacyMap::, - - // Use `tuple`s to have multiple values mapping - // Here a `ContractAddress` and a `u32` mapping to a Job struct - multi_map: LegacyMap::<(ContractAddress, u32), Job>, -} -``` - -The keys of the struct are made into their own modules, these modules have `read` and `write` functions. - -```rust -#[external] -fn play { - store::write( 592 ); - let value = store::read(); // 592 -} -``` - -`LegacyMap` types require a parameter with type matching the first generic type of the map when `read`/`write`. - -```rust -#[external] -fn play { - mapped::write( 83_u32, true ); - let value = multi_map::read( 83_u32 ); // true -} -``` - -## `ContractAddress` type - -`ContractAddress` type is a semantic type for wallet/contract addresses. - -```rust -use starknet::ContractAddress; -use traits::TryInto; // Base TryInto trait -use starknet::Felt252TryIntoContractAddress; // felt > ContractAddress impl -use starknet::contract_address_to_felt252; -use option::OptionTrait; // To unwrap -use starknet::get_caller_address; // Gets caller address - -use debug::PrintTrait; // Just for printing -#[test] -#[available_gas(2000000000)] -fn play() { - let owner_felt: felt252 = 0x0390595E0f30299328F610C689fcFf5B0ee48eE971f0742b5568e5Dd1DE6e324; - - // Felt to ContractAddress - let owner_addr: ContractAddress = owner_felt.try_into().unwrap(); - owner_addr.print(); - - let caller = get_caller_address(); - // ContractAddress to Felt - let owner_felt: felt252 = contract_address_to_felt252(caller); - owner_felt.print(); -} -``` - -## Further information - -- For more details, check out [The Structure of a Cairo Smart Contract](https://book.starknet.io/chapter_2/structure.html) in [the Starknet book](https://book.starknet.io). -- [Cross-contract interactions](https://cairo-book.github.io/ch99-02-00-abis-and-cross-contract-interactions.html) +[Development Proposal](https://community.starknet.io/t/cairo-1-contract-syntax-is-evolving/94794) diff --git a/exercises/starknet/basics/starknet1.cairo b/exercises/starknet/basics/starknet1.cairo index a962f8e2c..26c4dab69 100644 --- a/exercises/starknet/basics/starknet1.cairo +++ b/exercises/starknet/basics/starknet1.cairo @@ -5,18 +5,21 @@ // I AM NOT DONE -#[contract] +#[starknet::interface] +trait IJoesContract { + fn get_owner(self: @TContractState) -> felt252; +} + +#[starknet::contract] mod JoesContract { + #[storage] + struct Storage {} - fn get_owner() -> felt252 { - 'Joe' + impl IJoesContractImpl of super::IJoesContract { + fn get_owner(self: @ContractState) -> felt252 { + 'Joe' + } } - -} - -#[abi] -trait IJoesContract { - fn get_owner() -> felt252; } #[cfg(test)] @@ -32,19 +35,22 @@ mod test { use super::IJoesContractDispatcher; use super::IJoesContractDispatcherTrait; use starknet::ContractAddress; + use debug::PrintTrait; #[test] #[available_gas(2000000000)] fn test_contract_view() { let dispatcher = deploy_contract(); - assert( 'Joe' == dispatcher.get_owner(), 'Joe should be the owner.' ); + let owner = dispatcher.get_owner(); + assert('Joe' == dispatcher.get_owner(), 'Joe should be the owner.'); } fn deploy_contract() -> IJoesContractDispatcher { let mut calldata = ArrayTrait::new(); let (address0, _) = deploy_syscall( JoesContract::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false - ).unwrap(); + ) + .unwrap(); let contract0 = IJoesContractDispatcher { contract_address: address0 }; contract0 } diff --git a/exercises/starknet/basics/starknet2.cairo b/exercises/starknet/basics/starknet2.cairo index 065198089..a6518fb44 100644 --- a/exercises/starknet/basics/starknet2.cairo +++ b/exercises/starknet/basics/starknet2.cairo @@ -9,32 +9,31 @@ use starknet::ContractAddress; -#[contract] +#[starknet::contract] mod JillsContract { // This is required to use ContractAddress type use starknet::ContractAddress; - struct Storage { - // TODO: Add `contract_owner` storage, with ContractAddress type - + #[storage] + struct Storage { // TODO: Add `contract_owner` storage, with ContractAddress type } #[constructor] - fn constructor(owner: ContractAddress) { - // TODO: Write `owner` to contract_owner storage - + fn constructor( + ref self: ContractState, owner: ContractAddress + ) { // TODO: Write `owner` to contract_owner storage } - #[view] - fn get_owner() -> ContractAddress { - // TODO: Read contract_owner storage - + #[external(v0)] + impl IJillsContractImpl of super::IJillsContract { + fn get_owner(self: @ContractState) -> ContractAddress { // TODO: Read contract_owner storage + } } } -#[abi] -trait IJillsContract { - fn get_owner() -> ContractAddress; +#[starknet::interface] +trait IJillsContract { + fn get_owner(self: @TContractState) -> ContractAddress; } #[cfg(test)] @@ -56,16 +55,15 @@ mod test { #[test] #[available_gas(2000000000)] fn test_owner_setting() { - let owner: felt252 = 'Jill'; let mut calldata = ArrayTrait::new(); calldata.append('Jill'); let (address0, _) = deploy_syscall( JillsContract::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false - ).unwrap(); + ) + .unwrap(); let dispatcher = IJillsContractDispatcher { contract_address: address0 }; let owner = dispatcher.get_owner(); assert(owner == 'Jill'.try_into().unwrap(), 'Owner should be Jill'); } - } diff --git a/exercises/starknet/basics/starknet3.cairo b/exercises/starknet/basics/starknet3.cairo index 71d92e4aa..66c1a0807 100644 --- a/exercises/starknet/basics/starknet3.cairo +++ b/exercises/starknet/basics/starknet3.cairo @@ -7,11 +7,21 @@ // I AM NOT DONE -#[contract] +use starknet::ContractAddress; + +#[starknet::interface] +trait IProgressTracker { + fn set_progress(ref self: TContractState, user: ContractAddress, new_progress: u16); + fn get_progress(self: @TContractState, user: ContractAddress) -> u16; + fn get_contract_owner(self: @TContractState) -> ContractAddress; +} + +#[starknet::contract] mod ProgressTracker { use starknet::ContractAddress; use starknet::get_caller_address; // Required to use get_caller_address function + #[storage] struct Storage { contract_owner: ContractAddress, // TODO: Set types for LegacyMap @@ -19,17 +29,25 @@ mod ProgressTracker { } #[constructor] - fn constructor(owner: ContractAddress) { - contract_owner::write(owner); + fn constructor(ref self: ContractState, owner: ContractAddress) { + self.contract_owner.write(owner); } - #[external] - fn set_progress(user: ContractAddress, new_progress: u16) {// TODO: assert owner is calling - // TODO: set new_progress for user, - } - #[view] - fn get_progress(user: ContractAddress) -> u16 {// Get user progress + #[external(v0)] + impl ProgressTrackerImpl of super::IProgressTracker { + fn set_progress( + ref self: ContractState, user: ContractAddress, new_progress: u16 + ) { // TODO: assert owner is calling + // TODO: set new_progress for user, + } + + fn get_progress(self: @ContractState, user: ContractAddress) -> u16 { // Get user progress + } + + fn get_contract_owner(self: @ContractState) -> ContractAddress { + self.contract_owner.read() + } } } @@ -40,37 +58,37 @@ mod test { use array::SpanTrait; use debug::PrintTrait; use traits::TryInto; + use starknet::syscalls::deploy_syscall; + use core::result::ResultTrait; use starknet::Felt252TryIntoContractAddress; use option::OptionTrait; + use super::IProgressTrackerDispatcher; + use super::IProgressTrackerDispatcherTrait; use super::ProgressTracker; #[test] #[available_gas(2000000000)] fn test_owner() { - let owner: felt252 = 'Sensei'; - let owner: ContractAddress = owner.try_into().unwrap(); - ProgressTracker::constructor(owner); - - // Check that contract owner is set - let contract_owner = ProgressTracker::contract_owner::read(); - assert(contract_owner == owner, 'Mr. Sensei should be the owner'); + let owner: ContractAddress = 'Sensei'.try_into().unwrap(); + let dispatcher = deploy_contract(); + assert(owner == dispatcher.get_contract_owner(), 'Mr. Sensei should be the owner'); } #[test] #[available_gas(2000000000)] fn test_set_progress() { let owner = util_felt_addr('Sensei'); - ProgressTracker::constructor(owner); + let dispatcher = deploy_contract(); // Call contract as owner - starknet::testing::set_caller_address(owner); + starknet::testing::set_contract_address(owner); // Set progress - ProgressTracker::set_progress('Joe'.try_into().unwrap(), 20); - ProgressTracker::set_progress('Jill'.try_into().unwrap(), 25); + dispatcher.set_progress('Joe'.try_into().unwrap(), 20); + dispatcher.set_progress('Jill'.try_into().unwrap(), 25); - let joe_score = ProgressTracker::get_progress('Joe'.try_into().unwrap()); + let joe_score = dispatcher.get_progress('Joe'.try_into().unwrap()); assert(joe_score == 20, 'Joe\'s progress should be 20'); } @@ -78,18 +96,29 @@ mod test { #[should_panic] #[available_gas(2000000000)] fn test_set_progress_fail() { - let owner = util_felt_addr('Sensei'); - ProgressTracker::constructor(owner); + let dispatcher = deploy_contract(); let jon_doe = util_felt_addr('JonDoe'); // Caller not owner - starknet::testing::set_caller_address(jon_doe); + starknet::testing::set_contract_address(jon_doe); // Try to set progress, should panic to pass test! - ProgressTracker::set_progress('Joe'.try_into().unwrap(), 20); + dispatcher.set_progress('Joe'.try_into().unwrap(), 20); } fn util_felt_addr(addr_felt: felt252) -> ContractAddress { addr_felt.try_into().unwrap() } + + fn deploy_contract() -> IProgressTrackerDispatcher { + let owner: felt252 = 'Sensei'; + let mut calldata = ArrayTrait::new(); + calldata.append(owner); + let (address0, _) = deploy_syscall( + ProgressTracker::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false + ) + .unwrap(); + let contract0 = IProgressTrackerDispatcher { contract_address: address0 }; + contract0 + } } diff --git a/exercises/starknet/basics/starknet4.cairo b/exercises/starknet/basics/starknet4.cairo index 2a37aebb6..35edd69b9 100644 --- a/exercises/starknet/basics/starknet4.cairo +++ b/exercises/starknet/basics/starknet4.cairo @@ -6,48 +6,58 @@ // I AM NOT DONE -#[contract] +use starknet::ContractAddress; + +#[starknet::interface] +trait ILizInventory { + fn add_stock(ref self: TContractState, product: felt252, new_stock: u32); + fn purchase(ref self: TContractState, product: felt252, quantity: u32); + fn get_stock(self: @TContractState, product: felt252) -> u32; + fn get_owner(self: @TContractState) -> ContractAddress; +} + +#[starknet::contract] mod LizInventory { use starknet::ContractAddress; use starknet::get_caller_address; + #[storage] struct Storage { contract_owner: ContractAddress, // TODO: add storage inventory, that maps product (felt252) to stock quantity (u32) - } #[constructor] - fn constructor(owner: ContractAddress) { - contract_owner::write( owner ); - } - - #[external] - fn add_stock() { - // TODO: - // * takes product and new_stock - // * adds new_stock to stock in inventory - // * only owner can call this - + fn constructor(ref self: ContractState, owner: ContractAddress) { + self.contract_owner.write(owner); } - #[external] - fn purchase() { - // TODO: - // * takes product and quantity - // * subtracts quantity from stock in inventory - // * asserting stock > quantity isn't necessary, but nice to - // explicitly fail first and show that the case is covered - // * anybody can call this - - } - - #[view] - fn get_stock() { - // TODO: - // * takes product - // * returns product stock in inventory + #[external(v0)] + impl LizInventoryImpl of super::ILizInventory { + fn add_stock(ref self: ContractState, ) { + // TODO: + // * takes product and new_stock + // * adds new_stock to stock in inventory + // * only owner can call this + } + + fn purchase(ref self: ContractState, ) { + // TODO: + // * takes product and quantity + // * subtracts quantity from stock in inventory + // * anybody can call this + } + + fn get_stock(self: @ContractState, ) -> u32 { + // TODO: + // * takes product + // * returns product stock in inventory + } + + fn get_owner(self: @ContractState) -> ContractAddress { + self.contract_owner.read() + } } } @@ -58,86 +68,98 @@ mod test { use array::SpanTrait; use debug::PrintTrait; use traits::TryInto; + use starknet::syscalls::deploy_syscall; + use core::result::ResultTrait; use starknet::Felt252TryIntoContractAddress; use option::OptionTrait; use super::LizInventory; + use super::ILizInventoryDispatcher; + use super::ILizInventoryDispatcherTrait; #[test] #[available_gas(2000000000)] fn test_owner() { - - let owner: felt252 = 'Elizabeth'; - let owner: ContractAddress = owner.try_into().unwrap(); - LizInventory::constructor(owner); + let owner: ContractAddress = 'Elizabeth'.try_into().unwrap(); + let dispatcher = deploy_contract(); // Check that contract owner is set - let contract_owner = LizInventory::contract_owner::read(); + let contract_owner = dispatcher.get_owner(); assert(contract_owner == owner, 'Elizabeth should be the owner'); } #[test] #[available_gas(2000000000)] fn test_stock() { - let owner = util_felt_addr( 'Elizabeth' ); - LizInventory::constructor(owner); + let dispatcher = deploy_contract(); + let owner = util_felt_addr('Elizabeth'); // Call contract as owner - starknet::testing::set_caller_address( owner ); + starknet::testing::set_contract_address(owner); // Add stock - LizInventory::add_stock( 'Nano', 10); - let stock = LizInventory::get_stock( 'Nano' ); - assert( stock == 10, 'stock should be 10' ); + dispatcher.add_stock('Nano', 10); + let stock = dispatcher.get_stock('Nano'); + assert(stock == 10, 'stock should be 10'); - LizInventory::add_stock( 'Nano', 15); - let stock = LizInventory::get_stock( 'Nano' ); - assert( stock == 25, 'stock should be 25' ); + dispatcher.add_stock('Nano', 15); + let stock = dispatcher.get_stock('Nano'); + assert(stock == 25, 'stock should be 25'); } #[test] #[available_gas(2000000000)] fn test_stock_purchase() { - let owner = util_felt_addr( 'Elizabeth' ); - LizInventory::constructor(owner); - + let owner = util_felt_addr('Elizabeth'); + let dispatcher = deploy_contract(); + let result = dispatcher.get_owner(); // Call contract as owner - starknet::testing::set_caller_address( owner ); + starknet::testing::set_contract_address(owner); // Add stock - LizInventory::add_stock( 'Nano', 10); - let stock = LizInventory::get_stock( 'Nano' ); - assert( stock == 10, 'stock should be 10' ); + dispatcher.add_stock('Nano', 10); + let stock = dispatcher.get_stock('Nano'); + assert(stock == 10, 'stock should be 10'); // Call contract as owner - starknet::testing::set_caller_address( 0.try_into().unwrap() ); + starknet::testing::set_caller_address(0.try_into().unwrap()); - LizInventory::purchase( 'Nano', 2 ); - let stock = LizInventory::get_stock( 'Nano' ); - assert( stock == 8, 'stock should be 8' ); + dispatcher.purchase('Nano', 2); + let stock = dispatcher.get_stock('Nano'); + assert(stock == 8, 'stock should be 8'); } #[test] #[should_panic] #[available_gas(2000000000)] fn test_set_stock_fail() { - let owner = util_felt_addr( 'Elizabeth' ); - LizInventory::constructor(owner); + let dispatcher = deploy_contract(); // Try to add stock, should panic to pass test! - LizInventory::add_stock( 'Nano', 20); + dispatcher.add_stock('Nano', 20); } #[test] #[should_panic] #[available_gas(2000000000)] fn test_purchase_out_of_stock() { - let owner = util_felt_addr( 'Elizabeth' ); - LizInventory::constructor(owner); + let dispatcher = deploy_contract(); // Purchse out of stock - LizInventory::purchase( 'Nano', 2); + dispatcher.purchase('Nano', 2); } fn util_felt_addr(addr_felt: felt252) -> ContractAddress { addr_felt.try_into().unwrap() } + + fn deploy_contract() -> ILizInventoryDispatcher { + let owner: felt252 = 'Elizabeth'; + let mut calldata = ArrayTrait::new(); + calldata.append(owner); + let (address0, _) = deploy_syscall( + LizInventory::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false + ) + .unwrap(); + let contract0 = ILizInventoryDispatcher { contract_address: address0 }; + contract0 + } } diff --git a/exercises/starknet/interoperability/starknet5.cairo b/exercises/starknet/interoperability/starknet5.cairo index ee39a6740..4f588d688 100644 --- a/exercises/starknet/interoperability/starknet5.cairo +++ b/exercises/starknet/interoperability/starknet5.cairo @@ -3,6 +3,7 @@ // Execute `starklings hint starknet5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE + use core::traits::Into; use core::result::ResultTrait; use starknet::syscalls::deploy_syscall; @@ -11,14 +12,14 @@ use traits::TryInto; use option::OptionTrait; use starknet::class_hash::Felt252TryIntoClassHash; -#[abi] -trait IContractA { - fn set_value(_value: u128) -> bool; - fn get_value() -> u128; +#[starknet::interface] +trait IContractA { + fn set_value(ref self: TContractState, value: u128) -> bool; + fn get_value(self: @TContractState) -> u128; } -#[contract] +#[starknet::contract] mod ContractA { use traits::Into; use starknet::info::get_contract_address; @@ -26,58 +27,62 @@ mod ContractA { use super::IContractBDispatcher; use super::IContractBDispatcherTrait; use result::ResultTrait; + use debug::PrintTrait; + #[storage] struct Storage { contract_b: ContractAddress, value: u128, } #[constructor] - fn constructor(_contract_b: ContractAddress) { - contract_b::write(_contract_b) + fn constructor(ref self: ContractState, contract_b: ContractAddress) { + self.contract_b.write(contract_b) } - #[external] - fn set_value( - _value: u128 - ) -> bool { //TODO: check if contract_b is enabled. If it is, set the value and return true. Otherwise, return false. - } + #[external(v0)] + impl ContractAImpl of super::IContractA { + fn set_value(ref self: ContractState, value: u128) -> bool { + // TODO: check if contract_b is enabled. + // If it is, set the value and return true. Otherwise, return false. + } - #[view] - fn get_value() -> u128 { - value::read() + fn get_value(self: @ContractState) -> u128 { + self.value.read() + } } } -#[abi] -trait IContractB { - fn enable(); - fn disable(); - fn is_enabled() -> bool; +#[starknet::interface] +trait IContractB { + fn enable(ref self: TContractState); + fn disable(ref self: TContractState); + fn is_enabled(self: @TContractState) -> bool; } -#[contract] +#[starknet::contract] mod ContractB { + #[storage] struct Storage { enabled: bool } #[constructor] - fn constructor() {} + fn constructor(ref self: ContractState) {} - #[external] - fn enable() { - enabled::write(true); - } + #[external(v0)] + impl ContractBImpl of super::IContractB { + fn enable(ref self: ContractState) { + self.enabled.write(true); + } - #[external] - fn disable() { - enabled::write(false); - } + fn disable(ref self: ContractState) { + self.enabled.write(false); + } - #[view] - fn is_enabled() -> bool { - enabled::read() + fn is_enabled(self: @ContractState) -> bool { + self.enabled.read() + } } } @@ -106,14 +111,16 @@ mod test { // Deploy ContractB let (address_b, _) = deploy_syscall( ContractB::TEST_CLASS_HASH.try_into().unwrap(), 0, ArrayTrait::new().span(), false - ).unwrap(); + ) + .unwrap(); // Deploy ContractA let mut calldata = ArrayTrait::new(); calldata.append(address_b.into()); let (address_a, _) = deploy_syscall( ContractA::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false - ).unwrap(); + ) + .unwrap(); // contract_a is of type IContractADispatcher. Its methods are defined in IContractADispatcherTrait. let contract_a = IContractADispatcher { contract_address: address_a }; @@ -121,6 +128,7 @@ mod test { //TODO interact with contract_b to make the test pass. + // Tests assert(contract_a.set_value(300) == true, 'Could not set value'); assert(contract_a.get_value() == 300, 'Value was not set'); assert(contract_b.is_enabled() == true, 'Contract b is not enabled'); diff --git a/exercises/structs/structs1.cairo b/exercises/structs/structs1.cairo index 1d6f55aee..29eabc20d 100644 --- a/exercises/structs/structs1.cairo +++ b/exercises/structs/structs1.cairo @@ -4,9 +4,8 @@ // I AM NOT DONE #[derive(Copy, Drop)] -struct ColorStruct { - // TODO: Something goes here - // TODO: Your struct needs to have red, green, blue felts +struct ColorStruct { // TODO: Something goes here +// TODO: Your struct needs to have red, green, blue felts } diff --git a/exercises/structs/structs3.cairo b/exercises/structs/structs3.cairo index 67a0f159c..21d04b513 100644 --- a/exercises/structs/structs3.cairo +++ b/exercises/structs/structs3.cairo @@ -30,11 +30,13 @@ impl PackageImpl of PackageTrait { } fn is_international(ref self: Package) -> //??? - {/// Something goes here... + { + /// Something goes here... } fn get_fees(ref self: Package, cents_per_gram: usize) -> //??? - {/// Something goes here... + { + /// Something goes here... } } diff --git a/exercises/traits/traits1.cairo b/exercises/traits/traits1.cairo index 194a115e8..a3c831de9 100644 --- a/exercises/traits/traits1.cairo +++ b/exercises/traits/traits1.cairo @@ -20,7 +20,7 @@ trait AnimalTrait { fn make_noise(self: Animal) -> felt252; } -impl AnimalImpl of AnimalTrait {// TODO: implement the trait AnimalTrait for Animal +impl AnimalImpl of AnimalTrait { // TODO: implement the trait AnimalTrait for Animal } #[test] diff --git a/exercises/traits/traits2.cairo b/exercises/traits/traits2.cairo index 1a4ae57bf..94ab559bc 100644 --- a/exercises/traits/traits2.cairo +++ b/exercises/traits/traits2.cairo @@ -17,12 +17,12 @@ #[derive(Copy, Drop)] struct Cat { - noise: felt252, + noise: felt252, } #[derive(Copy, Drop)] struct Cow { - noise: felt252, + noise: felt252, } trait AnimalTrait { @@ -30,8 +30,7 @@ trait AnimalTrait { fn make_noise(self: T) -> felt252; } -impl CatImpl of AnimalTrait:: { - // TODO: implement the trait Animal for the type Cat +impl CatImpl of AnimalTrait { // TODO: implement the trait Animal for the type Cat } // TODO: implement the trait Animal for the type Cow diff --git a/exercises/traits/traits3.cairo b/exercises/traits/traits3.cairo index a26a73e94..6acf198d7 100644 --- a/exercises/traits/traits3.cairo +++ b/exercises/traits/traits3.cairo @@ -35,9 +35,9 @@ trait DogTrait { fn walk(ref self: Dog) -> (); } -impl AnimalFishImpl of AnimalTrait:: { +impl AnimalFishImpl of AnimalTrait { fn new() -> Fish { - Fish { noise: 'blub', distance: 0} + Fish { noise: 'blub', distance: 0 } } fn make_noise(self: Fish) -> felt252 { self.noise @@ -47,9 +47,9 @@ impl AnimalFishImpl of AnimalTrait:: { } } -impl AnimalDogImpl of AnimalTrait:: { +impl AnimalDogImpl of AnimalTrait { fn new() -> Dog { - Dog { noise: 'woof', distance: 0} + Dog { noise: 'woof', distance: 0 } } fn make_noise(self: Dog) -> felt252 { self.noise diff --git a/src/main.rs b/src/main.rs index e5225d547..06f5a0115 100755 --- a/src/main.rs +++ b/src/main.rs @@ -23,8 +23,8 @@ mod ui; mod exercise; mod project; mod run; -mod starklings_runner; -mod starklings_tester; +pub mod starklings_runner; +pub mod starklings_tester; mod verify; // In sync with crate version @@ -48,7 +48,6 @@ struct Args { enum Subcommands { Verify(VerifyArgs), Watch(WatchArgs), - CompileSolutions(CompileSolutionsArgs), Run(RunArgs), Reset(ResetArgs), Hint(HintArgs), @@ -69,13 +68,10 @@ struct WatchArgs { #[argh(positional)] // Start from this exercise start: Option, + #[argh(switch, short = 's', description = "use solutions directory")] + solutions: bool, } -#[derive(FromArgs, PartialEq, Debug)] -#[argh(subcommand, name = "compile_solutions")] -/// Reruns `verify` when files were edited -struct CompileSolutionsArgs {} - #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "run")] /// Runs/Tests a single exercise @@ -274,30 +270,10 @@ fn main() { } } - Subcommands::CompileSolutions(_subargs) => { - let exercises_base = PathBuf::from("exercises/"); - let solutions_base = PathBuf::from("solutions/"); - exercises.iter_mut().for_each(|mut ex| { - ex.path = solutions_base - .clone() - .join(ex.path.strip_prefix(&exercises_base).unwrap()); - }); - match watch(&exercises) { - Err(e) => { - println!("Error: {e:?}"); - std::process::exit(1); - } - Ok(WatchStatus::Finished) => { - let emoji = Emoji("🎉", "★"); - println!("{emoji} All solutions compile! {emoji}"); - } - Ok(WatchStatus::Unfinished) => { - println!("Solutions checking was stopped."); - } - } - } - Subcommands::Watch(subargs) => { + if subargs.solutions { + exercises = exercises_solution(exercises); + } let start = subargs.start; let watching = match start { @@ -330,12 +306,24 @@ fn main() { } } +fn exercises_solution(mut exercises: Vec) -> Vec { + let exercises_base = PathBuf::from("exercises/"); + let solutions_base = PathBuf::from("solutions/"); + exercises.iter_mut().for_each(|ex| { + ex.path = solutions_base + .clone() + .join(ex.path.strip_prefix(&exercises_base).unwrap()); + }); + + exercises +} + fn spawn_watch_shell( failed_exercise_hint: &Arc>>, should_quit: Arc, ) { let failed_exercise_hint = Arc::clone(failed_exercise_hint); - println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); + println!("\n\nWelcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); thread::spawn(move || loop { let mut input = String::new(); match io::stdin().read_line(&mut input) { diff --git a/src/run.rs b/src/run.rs index feb9fa2a4..a9510baad 100755 --- a/src/run.rs +++ b/src/run.rs @@ -39,10 +39,6 @@ fn run_cairo(exercise: &Exercise) -> Result<(), ()> { if let Some(error) = output.as_ref().err() { progress_bar.finish_and_clear(); - println!("Err"); - println!("{error}"); - - println!("Normal"); println!("{error}"); Err(()) } else { @@ -64,10 +60,6 @@ fn test_cairo(exercise: &Exercise) -> Result<(), ()> { if let Some(error) = output.as_ref().err() { progress_bar.finish_and_clear(); - println!("Err"); - println!("{error}"); - - println!("Normal"); println!("{error}"); Err(()) } else { diff --git a/src/starklings_runner.rs b/src/starklings_runner.rs index 2738320fe..327422bd6 100644 --- a/src/starklings_runner.rs +++ b/src/starklings_runner.rs @@ -1,7 +1,6 @@ //! Compiles and runs a Cairo program. use std::path::Path; -use std::sync::Arc; use anyhow::{Context, Ok}; use cairo_lang_compiler::db::RootDatabase; @@ -19,7 +18,7 @@ use cairo_lang_sierra::extensions::NamedLibfunc; use cairo_lang_sierra_generator::db::SierraGenGroup; use cairo_lang_sierra_generator::replace_ids::{DebugReplacer, SierraIdReplacer}; use cairo_lang_starknet::contract::get_contracts_info; -use cairo_lang_starknet::plugin::StarkNetPlugin; + use clap::Parser; const CORELIB_DIR_NAME: &str = "corelib/src"; @@ -30,7 +29,6 @@ const CORELIB_DIR_NAME: &str = "corelib/src"; #[clap(version, verbatim_doc_comment)] pub struct Args { /// The file to compile and run. - #[arg(short, long)] pub path: String, /// In cases where gas is available, the amount of provided gas. #[arg(long)] @@ -40,7 +38,7 @@ pub struct Args { pub print_full_memory: bool, } -fn main() -> anyhow::Result<()> { +pub fn main() -> anyhow::Result<()> { let args = Args::parse(); let res = run_cairo_program(&args); if let Err(e) = res { @@ -51,13 +49,7 @@ fn main() -> anyhow::Result<()> { } pub fn run_cairo_program(args: &Args) -> anyhow::Result { - let db = &mut { - let mut b = RootDatabase::builder(); - b.detect_corelib(); - b.with_semantic_plugin(Arc::new(StarkNetPlugin::default())); - - b.build()? - }; + let db = &mut RootDatabase::builder().detect_corelib().build()?; let mut corelib_dir = std::env::current_exe() .unwrap_or_else(|e| panic!("Problem getting the executable path: {e:?}")); corelib_dir.pop(); @@ -105,7 +97,7 @@ pub fn run_cairo_program(args: &Args) -> anyhow::Result { ) .with_context(|| "Failed setting up runner.")?; let result = runner - .run_function( + .run_function_with_starknet_context( runner.find_function("::main")?, &[], args.available_gas, diff --git a/src/starklings_tester.rs b/src/starklings_tester.rs index 813e6436b..67ccdd25c 100644 --- a/src/starklings_tester.rs +++ b/src/starklings_tester.rs @@ -1,9 +1,5 @@ //! Compiles and runs a Cairo program. -use std::collections::HashMap; -use std::path::Path; -use std::sync::{Arc, Mutex}; - use anyhow::{bail, Context, Result}; use cairo_felt::Felt252; use cairo_lang_compiler::db::RootDatabase; @@ -15,6 +11,9 @@ use cairo_lang_defs::plugin::{MacroPlugin, PluginDiagnostic, PluginResult}; use cairo_lang_diagnostics::ToOption; use cairo_lang_filesystem::cfg::{Cfg, CfgSet}; +use std::path::Path; +use std::sync::{Arc, Mutex}; + use cairo_lang_filesystem::ids::CrateId; use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId; @@ -23,7 +22,6 @@ use cairo_lang_runner::{RunResultValue, SierraCasmRunner}; use cairo_lang_semantic::db::SemanticGroup; use cairo_lang_semantic::items::functions::GenericFunctionId; -use cairo_lang_semantic::plugin::{AsDynMacroPlugin, SemanticPlugin}; use cairo_lang_semantic::{ConcreteFunction, FunctionLongId}; use cairo_lang_sierra::extensions::gas::CostTokenType; use cairo_lang_sierra::ids::FunctionId; @@ -32,7 +30,7 @@ use cairo_lang_sierra_generator::replace_ids::{DebugReplacer, SierraIdReplacer}; use cairo_lang_sierra_to_casm::metadata::MetadataComputationConfig; use cairo_lang_starknet::casm_contract_class::ENTRY_POINT_COST; use cairo_lang_starknet::contract::{ - find_contracts, get_contracts_info, get_module_functions, ContractInfo, + find_contracts, get_contracts_info, get_module_abi_functions, ContractInfo, }; use cairo_lang_starknet::plugin::consts::{CONSTRUCTOR_MODULE, EXTERNAL_MODULE, L1_HANDLER_MODULE}; use cairo_lang_starknet::plugin::StarkNetPlugin; @@ -41,7 +39,7 @@ use cairo_lang_syntax::attribute::structured::{ }; use cairo_lang_syntax::node::db::SyntaxGroup; -use cairo_lang_syntax::node::{ast, Terminal, Token}; +use cairo_lang_syntax::node::{ast, Token}; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use cairo_lang_utils::OptionHelper; use clap::Parser; @@ -49,8 +47,6 @@ use colored::Colorize; use itertools::{chain, Itertools}; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; -const CORELIB_DIR_NAME: &str = "corelib/src"; - /// Command line args parser. /// Exits with 0/1 if the input is formatted correctly/incorrectly. #[derive(Parser, Debug)] @@ -80,7 +76,7 @@ enum TestStatus { Ignore, } -fn main() -> anyhow::Result<()> { +pub fn main() -> anyhow::Result<()> { let args = Args::parse(); let runner = TestRunner::new(&args.path, "", false, false, true)?; if let Err(e) = runner.run() { @@ -120,10 +116,10 @@ impl TestRunner { let mut b = RootDatabase::builder(); b.detect_corelib(); b.with_cfg(CfgSet::from_iter([Cfg::name("test")])); - b.with_semantic_plugin(Arc::new(TestPlugin::default())); + b.with_macro_plugin(Arc::new(TestPlugin::default())); if starknet { - b.with_semantic_plugin(Arc::new(StarkNetPlugin::default())); + b.with_macro_plugin(Arc::new(StarkNetPlugin::default())); } b.build()? @@ -154,12 +150,12 @@ impl TestRunner { .iter() .flat_map(|contract| { chain!( - get_module_functions(db, contract, EXTERNAL_MODULE).unwrap(), - get_module_functions(db, contract, CONSTRUCTOR_MODULE).unwrap(), - get_module_functions(db, contract, L1_HANDLER_MODULE).unwrap() + get_module_abi_functions(db, contract, EXTERNAL_MODULE).unwrap(), + get_module_abi_functions(db, contract, CONSTRUCTOR_MODULE).unwrap(), + get_module_abi_functions(db, contract, L1_HANDLER_MODULE).unwrap() ) }) - .flat_map(|func_id| ConcreteFunctionWithBodyId::from_no_generics_free(db, func_id)) + .map(|func| ConcreteFunctionWithBodyId::from_semantic(db, func.value)) .collect() } else { vec![] @@ -229,6 +225,7 @@ impl TestRunner { function_set_costs, contracts_info, )?; + let mut result_string = String::new(); if failed.is_empty() { result_string.push_str( @@ -294,7 +291,7 @@ fn run_tests( named_tests: Vec<(String, TestConfig)>, sierra_program: cairo_lang_sierra::program::Program, function_set_costs: OrderedHashMap>, - contracts_info: HashMap, + contracts_info: OrderedHashMap, ) -> anyhow::Result { let runner = SierraCasmRunner::new( sierra_program, @@ -316,7 +313,7 @@ fn run_tests( return Ok((name, TestStatus::Ignore)); } let result = runner - .run_function( + .run_function_with_starknet_context( runner.find_function(name.as_str())?, &[], test.available_gas, @@ -568,12 +565,3 @@ impl MacroPlugin for TestPlugin { } } } -impl AsDynMacroPlugin for TestPlugin { - fn as_dyn_macro_plugin<'a>(self: Arc) -> Arc - where - Self: 'a, - { - self - } -} -impl SemanticPlugin for TestPlugin {} diff --git a/src/verify.rs b/src/verify.rs index c12f752fd..02dfdbdd9 100755 --- a/src/verify.rs +++ b/src/verify.rs @@ -68,13 +68,14 @@ fn compile_and_test_interactively(exercise: &Exercise) -> Result { fn compile_and_run_cairo(exercise: &Exercise, progress_bar: &ProgressBar) -> Result { let compilation_result = exercise.run_cairo(); - if let Some(error) = compilation_result.as_ref().err() { + if let Err(error) = compilation_result { progress_bar.finish_and_clear(); warn!( "Compiling of {} failed! Please try again. Here's the output:", exercise ); println!("{error}"); + eprintln!("{error}"); Err(()) } else { Ok(compilation_result.unwrap()) diff --git a/tests/fixture/cairo/compileFail.cairo b/tests/fixture/cairo/compileFail.cairo index 445443461..261fb70c0 100644 --- a/tests/fixture/cairo/compileFail.cairo +++ b/tests/fixture/cairo/compileFail.cairo @@ -1,3 +1,3 @@ fn main() -> felt252 { hi() -} \ No newline at end of file +} diff --git a/tests/fixture/cairo/compilePass.cairo b/tests/fixture/cairo/compilePass.cairo index ebf6b1b8e..af5c6127e 100644 --- a/tests/fixture/cairo/compilePass.cairo +++ b/tests/fixture/cairo/compilePass.cairo @@ -1,3 +1,3 @@ fn main() -> felt252 { 25 -} \ No newline at end of file +} diff --git a/tests/fixture/cairo/testFails.cairo b/tests/fixture/cairo/testFails.cairo index fffa2b86e..c6b9f91ae 100644 --- a/tests/fixture/cairo/testFails.cairo +++ b/tests/fixture/cairo/testFails.cairo @@ -1,4 +1,4 @@ #[test] fn test_0_is_1() { - assert(0==1,'0 should be equal to 1'); + assert(0 == 1, '0 should be equal to 1'); } diff --git a/tests/fixture/cairo/testPass.cairo b/tests/fixture/cairo/testPass.cairo index c00df5dbe..bc1596766 100644 --- a/tests/fixture/cairo/testPass.cairo +++ b/tests/fixture/cairo/testPass.cairo @@ -1,4 +1,4 @@ #[test] fn test_0_is_0() { - assert(0==0,'should be equal to 0'); + assert(0 == 0, 'should be equal to 0'); }