diff --git a/Cargo.lock b/Cargo.lock index 577f25a68..f9bb897e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,12 +261,24 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bincode" version = "1.3.3" @@ -891,6 +903,8 @@ dependencies = [ "educe", "id-arena", "itertools 0.12.1", + "k256", + "keccak", "lambdaworks-math", "lazy_static", "libc", @@ -900,8 +914,10 @@ dependencies = [ "mlir-sys", "num-bigint", "num-traits 0.2.18", + "p256", "pretty_assertions_sorted", "proptest", + "sec1", "serde", "serde_json", "starknet-types-core", @@ -1104,6 +1120,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const_format" version = "0.2.32" @@ -1225,6 +1247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", + "rand_core", "subtle", "zeroize", ] @@ -1299,6 +1322,17 @@ dependencies = [ "parking_lot_core 0.9.9", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1369,6 +1403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -1400,6 +1435,20 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "educe" version = "0.5.11" @@ -1418,6 +1467,26 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "ena" version = "0.14.2" @@ -1485,6 +1554,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1543,6 +1622,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1574,6 +1654,17 @@ dependencies = [ "minilp", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "half" version = "2.3.1" @@ -1786,6 +1877,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "keccak" version = "0.1.5" @@ -2224,6 +2329,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parity-scale-codec" version = "3.6.9" @@ -2316,6 +2433,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "petgraph" version = "0.6.4" @@ -2347,6 +2473,16 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -2443,6 +2579,15 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" version = "2.0.0" @@ -2804,6 +2949,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.22" @@ -2903,6 +3062,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -2940,6 +3109,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "sprs" version = "0.7.1" @@ -2953,9 +3132,9 @@ dependencies = [ [[package]] name = "starknet-crypto" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c03f5ac70f9b067f48db7d2d70bdf18ee0f731e8192b6cfa679136becfcdb0" +checksum = "2e2c30c01e8eb0fc913c4ee3cf676389fffc1d1182bfe5bb9670e4e72e968064" dependencies = [ "crypto-bigint", "hex", @@ -2973,9 +3152,9 @@ dependencies = [ [[package]] name = "starknet-crypto-codegen" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6527b845423542c8a16e060ea1bc43f67229848e7cd4c4d80be994a84220ce" +checksum = "bbc159a1934c7be9761c237333a57febe060ace2bc9e3b337a59a37af206d19f" dependencies = [ "starknet-curve", "starknet-ff", @@ -2984,18 +3163,18 @@ dependencies = [ [[package]] name = "starknet-curve" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c454fecadfb3fe56ee82c405439d663c8a037667cc9d8e4acb1fb17e15b1af" +checksum = "d1c383518bb312751e4be80f53e8644034aa99a0afb29d7ac41b89a997db875b" dependencies = [ "starknet-ff", ] [[package]] name = "starknet-ff" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "067419451efdea1ee968df8438369960c167e0e905c05b84afd074f50e1d6f3d" +checksum = "7abf1b44ec5b18d87c1ae5f54590ca9d0699ef4dd5b2ffa66fc97f24613ec585" dependencies = [ "ark-ff", "crypto-bigint", diff --git a/Cargo.toml b/Cargo.toml index f4a6a1e97..ba15522a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,9 @@ required-features = ["build-cli"] [features] default = ["build-cli", "with-runtime"] -build-cli = ["dep:clap", "dep:tracing-subscriber", "dep:anyhow", "dep:cairo-lang-test-plugin", "dep:cairo-lang-runner", "dep:colored", "dep:cairo-felt"] +build-cli = ["dep:clap", "dep:tracing-subscriber", "dep:anyhow", "dep:cairo-lang-test-plugin", +"dep:cairo-lang-runner", "dep:colored", "dep:cairo-felt", "dep:keccak", +"dep:k256", "dep:p256", "dep:sec1"] with-debug-utils = [] with-runtime = ["dep:cairo-native-runtime"] with-serde = ["dep:serde"] @@ -77,6 +79,10 @@ cairo-lang-runner = { version = "2.5.4", optional = true} colored = { version = "2.1.0", optional = true } # needed to interface with cairo-lang-* cairo-felt = { version = "0.9.1", optional = true } +keccak = { version = "0.1.3", optional = true } +k256 = { version = "0.13.3", optional = true } +p256 = { version = "0.13.2", optional = true } +sec1 = { version = "0.7.3", optional = true } [dev-dependencies] cairo-felt = "0.9.1" diff --git a/cairo-tests/src/keccak_test.cairo b/cairo-tests/src/keccak_test.cairo index 1c64bb0c8..cae04b7d1 100644 --- a/cairo-tests/src/keccak_test.cairo +++ b/cairo-tests/src/keccak_test.cairo @@ -1,9 +1,7 @@ use starknet::SyscallResultTrait; use core::test::test_utils::{assert_eq, assert_ne}; -// TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_syscall() { let input = array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; assert_eq( @@ -15,7 +13,6 @@ fn test_keccak_syscall() { // TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_hash() { let res = keccak::keccak_u256s_le_inputs(array![1].span()); assert_eq(@res.low, @0x587f7cc3722e9654ea3963d5fe8c0748, 'Wrong hash low 1'); @@ -37,7 +34,6 @@ fn test_keccak_hash() { // Same input as in `test_keccak_hash` but as a u64 array. // TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_u64() { let mut input = array![ 0x0000000000000001, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000 @@ -51,7 +47,6 @@ fn test_keccak_u64() { // TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_u64_full_block() { let mut input = array![ 0x0000000000000001, @@ -79,9 +74,7 @@ fn test_keccak_u64_full_block() { assert_eq(@res.high, @0x210740d45b1fe2ac908a497ef45509f5, 'Wrong hash high'); } -// TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_u64_full_block_minus_byte() { let mut input = array![ 0x0000000000000001, @@ -117,9 +110,7 @@ fn test_keccak_u64_full_block_minus_byte() { assert_eq(@res.high, @0xc98592786514c87f1a1a3d567b4dcd75, 'Wrong hash high 2'); } -// TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_u64_full_block_minus_word() { let mut input = array![ 0x0000000000000001, @@ -146,9 +137,7 @@ fn test_keccak_u64_full_block_minus_word() { assert_eq(@res.high, @0x6851f2dbbfb3bfadff94db3ad476164f, 'Wrong hash high'); } -// TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_u64_full_block_minus_word_minus_byte() { let mut input = array![ 0x0000000000000001, @@ -183,9 +172,7 @@ fn test_keccak_u64_full_block_minus_word_minus_byte() { assert_eq(@res.high, @0xdd7e11698dc8b37323c854a53abcd330, 'Wrong hash high 2'); } -// TODO: requires syscall handler on cairo-native-test #[test] -#[ignore] fn test_keccak_u64_full_block_minus_3_words_minus_4_bytes() { let mut input = array![ 0x0000000000000001, diff --git a/cairo-tests/src/lib.cairo b/cairo-tests/src/lib.cairo index 058b08ed7..459f32f82 100644 --- a/cairo-tests/src/lib.cairo +++ b/cairo-tests/src/lib.cairo @@ -17,3 +17,6 @@ mod num_test; mod panics_test; mod test_utils; mod result_test; +mod testing_test; +mod plugins_test; +mod print_test; diff --git a/cairo-tests/src/plugins_test.cairo b/cairo-tests/src/plugins_test.cairo new file mode 100644 index 000000000..95ccf8f8c --- /dev/null +++ b/cairo-tests/src/plugins_test.cairo @@ -0,0 +1,146 @@ +use core::test::test_utils::assert_eq; + +#[derive(Copy, Debug, Drop, Serde, PartialEq)] +enum EnumForSerde { + A, + B: u32, + C: u64, +} + +#[derive(Drop, Debug, Default, PartialEq)] +struct StructForDefault { + a: felt252, + b: u256, + c: bool +} + +#[derive(Drop, Debug, Default, PartialEq)] +enum EnumForDefault { + A: felt252, + B: u256, + #[default] + C: StructForDefault, +} + +#[test] +fn test_derive_serde_enum() { + let a = EnumForSerde::A; + let b = EnumForSerde::B(1); + let c = EnumForSerde::C(2); + 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_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @a, + 'expected a' + ); + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @a, + 'expected a' + ); + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @c, + 'expected c' + ); + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @b, + 'expected b' + ); + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), + @a, + 'expected a' + ); + assert(serialized.is_empty(), 'expected empty'); +} + +#[test] +fn test_derive_default_struct() { + assert_eq!(Default::default(), StructForDefault { a: 0, b: 0, c: false }); +} + +#[test] +fn test_derive_default_enum() { + assert_eq!(Default::default(), EnumForDefault::C(StructForDefault { a: 0, b: 0, c: false })); +} + +#[derive(Copy, Debug, Drop, Serde, PartialEq)] +enum LongEnum5 { + A, + B, + C, + D, + E +} +#[derive(Copy, Debug, Drop, Serde, PartialEq)] +enum longEnum10 { + A, + B, + C, + D, + E, + F, + G, + H, + I, + J +} +#[derive(Copy, Debug, Drop, Serde, PartialEq)] +enum longEnum15 { + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O +} + + +#[test] +fn test_long_enum5_deserialize() { + let x = LongEnum5::E; + let mut output = Default::default(); + x.serialize(ref output); + let mut serialized = output.span(); + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), @x, 'expected E' + ); +} +#[test] +fn test_long_enum10_deserialize() { + let x = longEnum10::J; + let mut output = Default::default(); + x.serialize(ref output); + let mut serialized = output.span(); + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), @x, 'expected J' + ); +} +#[test] +fn test_long_enum15_deserialize() { + let x = longEnum15::O; + let mut output = Default::default(); + x.serialize(ref output); + let mut serialized = output.span(); + assert_eq( + @Serde::::deserialize(ref serialized).expect('failed to read'), @x, 'expected O' + ); +} + diff --git a/cairo-tests/src/print_test.cairo b/cairo-tests/src/print_test.cairo new file mode 100644 index 000000000..161d10a8e --- /dev/null +++ b/cairo-tests/src/print_test.cairo @@ -0,0 +1,131 @@ +use core::byte_array::BYTE_ARRAY_MAGIC; +use core::debug::{PrintTrait, print_byte_array_as_string}; + +#[ignore] +#[test] +fn test_prints() { + // Valid short string. + 'hello'.print(); + + // felt252 + 1.print(); + + // Valid string with < 31 characters (no full words). + let x: ByteArray = "short, but string"; + print_byte_array_as_string(@x); + + // Valid string with > 31 characters (with a full word). + let x: ByteArray = "This is a long string with more than 31 characters."; + print_byte_array_as_string(@x); + + // Valid string as an array. + let x = array![ + BYTE_ARRAY_MAGIC, + 1, // A single full word. + 'This is a long string with more', + ' than 31 characters.', + 20 // pending word length. Bigger than the actual data in the pending word. + ]; + x.print(); + + // Only magic. + let x = array![BYTE_ARRAY_MAGIC]; + x.print(); + + // num_full_words > usize. + let x = array![BYTE_ARRAY_MAGIC, 0x100000000]; + x.print(); + + // Not enough data after num_full_words. + let x = array![BYTE_ARRAY_MAGIC, 0]; + x.print(); + + // Not enough full words. + let x = array![BYTE_ARRAY_MAGIC, 1, 0, 0]; + x.print(); + + // Too much data in full word. + let x = array![ + BYTE_ARRAY_MAGIC, + 1, // A single full word. + 0x161616161616161616161616161616161616161616161616161616161616161, // The invalid full word. + 0, + 0 // pending byte is empty. + ]; + x.print(); + + // num_pending_bytes > usize. + let x = array![BYTE_ARRAY_MAGIC, 0, 0, 0x100000000]; + x.print(); + + // "Not enough" data in pending_word (nulls in the beginning). + let x = array![ + BYTE_ARRAY_MAGIC, + 0, // No full words. + 'a', + 2 // pending word length. Bigger than the actual data in the pending word. + ]; + x.print(); + + // Too much data in pending_word. + let x = array![ + BYTE_ARRAY_MAGIC, + 0, // No full words. + 'aa', + 1 // pending word length. Smaller than the actual data in the pending word. + ]; + x.print(); + + // Valid string with Null. + let mut x: ByteArray = "Hello"; + x.append_byte(0); // '\0' + let suffix: ByteArray = "world"; + x.append(@suffix); + print_byte_array_as_string(@x); + + // Valid string with a non printable character. + let mut x: ByteArray = "Hello"; + x.append_byte(0x11); // Non printable character. + let suffix: ByteArray = "world"; + x.append(@suffix); + print_byte_array_as_string(@x); + + // Valid string with a newline. + let mut x: ByteArray = "Hello"; + x.append_byte(0xA); // '\n' + let suffix: ByteArray = "world"; + x.append(@suffix); + print_byte_array_as_string(@x); + + // Multiple values: (felt, string, short_string, felt) + let x = array![0x9999, BYTE_ARRAY_MAGIC, 0, 'hello', 5, 'world', 0x8888]; + x.print(); +} + +#[ignore] +#[test] +fn test_print_macro() { + // With a ByteArray. + let ba: ByteArray = "hello"; + print!("{}", ba); + + // With a felt252. + print!("{}", 97_felt252); + + // With an integer. + print!("{}", 97_usize); +} + +#[ignore] +#[test] +fn test_println_macro() { + // With a ByteArray. + let ba: ByteArray = "hello"; + println!("{}", ba); + + // With a felt252. + println!("{}", 97_felt252); + + // With an integer. + println!("{}", 97_usize); +} diff --git a/cairo-tests/src/testing_test.cairo b/cairo-tests/src/testing_test.cairo new file mode 100644 index 000000000..6e5e56444 --- /dev/null +++ b/cairo-tests/src/testing_test.cairo @@ -0,0 +1,82 @@ +use core::test::test_utils::assert_gt; + +#[test] +#[should_panic(expected: ('panic_with_felt252()',))] +fn test_panic_with_felt252() { + // No semicolon here: Missing implementation for core::traits::Drop:: + core::panic_with_felt252('panic_with_felt252()') +} + +#[test] +#[should_panic(expected: 'assert(false)')] +fn test_assert_false() { + assert(false, 'assert(false)'); +} + +#[test] +fn test_assert_true() { + assert(true, 'assert(true)'); +} + +#[test] +#[should_panic(expected: "assert(false)")] +fn test_assert_macro_false() { + assert!(false, "assert(false)"); +} + +#[test] +fn test_assert_macro_true() { + assert!(true, "assert(true)"); +} + +#[test] +fn test_assert_ne_with_description() { + assert_ne!(1, 2, "Description"); +} + +#[test] +fn test_assert_ne_no_description() { + assert_ne!(1, 2); +} + +#[test] +#[should_panic(expected: "assertion failed: `false`.")] +fn test_assert_macro_no_input() { + assert!(false); +} + +#[test] +#[should_panic(expected: "assertion `1 == 2` failed: Description +1: 1 +2: 2")] +fn test_assert_eq_with_description() { + assert_eq!(1, 2, "Description"); +} + +#[test] +#[should_panic(expected: "assertion `1 == 2` failed: 1 != 2 +1: 1 +2: 2")] +fn test_assert_eq_with_formatted_description() { + assert_eq!(1, 2, "{} != {}", 1, 2); +} + +#[test] +#[should_panic(expected: "assertion `1 == 2` failed. +1: 1 +2: 2")] +fn test_assert_eq_no_description() { + assert_eq!(1, 2); +} + +#[test] +#[available_gas(static)] +fn test_get_available_gas_no_gas_supply() { + assert_eq!(core::testing::get_available_gas(), 0) +} + +#[test] +#[available_gas(10000)] +fn test_get_available_gas_with_gas_supply() { + assert_gt(core::testing::get_available_gas(), 5000, 'high amount of gas used') +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 7f343a4c7..26c9cb94a 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -14,6 +14,6 @@ starknet-types-core = { version = "0.1.0", default-features = false, features = ] } cairo-lang-sierra-gas = "2.5.4" libc = "0.2" -starknet-crypto = "0.6" -starknet-curve = "0.4" +starknet-crypto = "0.6.2" +starknet-curve = "0.4.2" lazy_static = "1.4" diff --git a/src/bin/cairo-native-test.rs b/src/bin/cairo-native-test.rs index 951a44286..2d0df6832 100644 --- a/src/bin/cairo-native-test.rs +++ b/src/bin/cairo-native-test.rs @@ -22,15 +22,20 @@ use cairo_native::{ execution_result::ExecutionResult, executor::{AotNativeExecutor, JitNativeExecutor, NativeExecutor}, metadata::gas::{GasMetadata, MetadataComputationConfig}, + starknet::{Secp256k1Point, Secp256r1Point, StarknetSyscallHandler, SyscallResult, U256}, values::JitValue, OptLevel, }; use clap::{Parser, ValueEnum}; use colored::Colorize; use itertools::Itertools; +use k256::elliptic_curve::sec1::ToEncodedPoint; +use k256::elliptic_curve::{generic_array::GenericArray, sec1::FromEncodedPoint}; use num_traits::ToPrimitive; +use sec1::point::Coordinates; use starknet_types_core::felt::Felt; use std::{ + iter::once, path::{Path, PathBuf}, sync::Mutex, vec::IntoIter, @@ -430,15 +435,15 @@ fn run_tests( let func = find_function(&sierra_program, name.as_str())?; - let initial_gas = gas_metadata - .get_initial_available_gas( - &func.id, - test.available_gas.map(|x| x.try_into().unwrap()), - ) - .with_context(|| "not enough gas to run")?; + let initial_gas = test.available_gas.map(|x| x.try_into().unwrap()); let result = native_executor - .invoke_dynamic(&func.id, &[], Some(initial_gas)) + .invoke_dynamic_with_syscall_handler( + &func.id, + &[], + initial_gas, + TestSyscallHandler, + ) .with_context(|| format!("Failed to run the function `{}`.", name.as_str()))?; let run_result = result_to_runresult(&result)?; @@ -513,3 +518,599 @@ fn run_tests( }); wrapped_summary.into_inner().unwrap() } + +pub struct TestSyscallHandler; + +impl StarknetSyscallHandler for TestSyscallHandler { + fn get_block_hash( + &mut self, + _block_number: u64, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + unimplemented!() + } + + fn get_execution_info( + &mut self, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + unimplemented!() + } + + fn get_execution_info_v2( + &mut self, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + unimplemented!() + } + + fn deploy( + &mut self, + _class_hash: Felt, + _contract_address_salt: Felt, + _calldata: &[Felt], + _deploy_from_zero: bool, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult<(Felt, Vec)> { + unimplemented!() + } + + fn replace_class( + &mut self, + _class_hash: Felt, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult<()> { + unimplemented!() + } + + fn library_call( + &mut self, + _class_hash: Felt, + _function_selector: Felt, + _calldata: &[Felt], + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult> { + unimplemented!() + } + + fn call_contract( + &mut self, + _address: Felt, + _entry_point_selector: Felt, + _calldata: &[Felt], + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult> { + unimplemented!() + } + + fn storage_read( + &mut self, + _address_domain: u32, + _address: Felt, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + unimplemented!() + } + + fn storage_write( + &mut self, + _address_domain: u32, + _address: Felt, + _value: Felt, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult<()> { + unimplemented!() + } + + fn emit_event( + &mut self, + _keys: &[Felt], + _data: &[Felt], + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult<()> { + unimplemented!() + } + + fn send_message_to_l1( + &mut self, + _to_address: Felt, + _payload: &[Felt], + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult<()> { + unimplemented!() + } + + fn keccak( + &mut self, + input: &[u64], + gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + let length = input.len(); + + if length % 17 != 0 { + let error_msg = b"Invalid keccak input size"; + let felt_error = Felt::from_bytes_be_slice(error_msg); + return Err(vec![felt_error]); + } + + let n_chunks = length / 17; + let mut state = [0u64; 25]; + + for i in 0..n_chunks { + if *gas < KECCAK_ROUND_COST { + let error_msg = b"Syscall out of gas"; + let felt_error = Felt::from_bytes_be_slice(error_msg); + return Err(vec![felt_error]); + } + const KECCAK_ROUND_COST: u128 = 180000; + *gas -= KECCAK_ROUND_COST; + let chunk = &input[i * 17..(i + 1) * 17]; //(request.input_start + i * 17)?; + for (i, val) in chunk.iter().enumerate() { + state[i] ^= val; + } + keccak::f1600(&mut state) + } + + // state[0] and state[1] conform the hash_high (u128) + // state[2] and state[3] conform the hash_low (u128) + SyscallResult::Ok(U256 { + lo: state[2] as u128 | ((state[3] as u128) << 64), + hi: state[0] as u128 | ((state[1] as u128) << 64), + }) + } + + fn secp256k1_new( + &mut self, + x: cairo_native::starknet::U256, + y: cairo_native::starknet::U256, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult> { + // The following unwraps should be unreachable because the iterator we provide has the + // expected number of bytes. + let point = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + x.hi.to_be_bytes().into_iter().chain(x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + y.hi.to_be_bytes().into_iter().chain(y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ); + + if bool::from(point.is_some()) { + Ok(Some(Secp256k1Point { x, y })) + } else { + Ok(None) + } + } + + fn secp256k1_add( + &mut self, + p0: cairo_native::starknet::Secp256k1Point, + p1: cairo_native::starknet::Secp256k1Point, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + // The inner unwraps should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwraps depend on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p0 = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p0.x.hi + .to_be_bytes() + .into_iter() + .chain(p0.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p0.y.hi + .to_be_bytes() + .into_iter() + .chain(p0.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let p1 = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p1.x.hi + .to_be_bytes() + .into_iter() + .chain(p1.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p1.y.hi + .to_be_bytes() + .into_iter() + .chain(p1.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + + let p = p0 + p1; + + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256k1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256k1_mul( + &mut self, + p: cairo_native::starknet::Secp256k1Point, + m: cairo_native::starknet::U256, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + // The inner unwrap should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwrap depends on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p.x.hi.to_be_bytes().into_iter().chain(p.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p.y.hi.to_be_bytes().into_iter().chain(p.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let m: k256::Scalar = k256::elliptic_curve::ScalarPrimitive::from_slice(&{ + let mut buf = [0u8; 32]; + buf[0..16].copy_from_slice(&m.hi.to_be_bytes()); + buf[16..32].copy_from_slice(&m.lo.to_be_bytes()); + buf + }) + .map_err(|_| { + vec![Felt::from_bytes_be( + b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0invalid scalar", + )] + })? + .into(); + + let p = p * m; + + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256k1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256k1_get_point_from_x( + &mut self, + x: cairo_native::starknet::U256, + y_parity: bool, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult> { + // The inner unwrap should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwrap depends on the encoding format, which should be valid + // since it's hardcoded.. + let point = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_bytes( + k256::CompressedPoint::from_exact_iter( + once(0x02 | y_parity as u8) + .chain(x.hi.to_be_bytes()) + .chain(x.lo.to_be_bytes()), + ) + .unwrap(), + ) + .unwrap(), + ); + + if bool::from(point.is_some()) { + // This unwrap has already been checked in the `if` expression's condition. + let p = point.unwrap(); + + let p = p.to_encoded_point(false); + let y = match p.coordinates() { + Coordinates::Uncompressed { y, .. } => y, + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following unwrap should be safe because the array always has 32 bytes. The other + // two are definitely safe because the slicing guarantees its length to be the right + // one. + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Some(Secp256k1Point { + x, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + })) + } else { + Ok(None) + } + } + + fn secp256k1_get_xy( + &mut self, + p: cairo_native::starknet::Secp256k1Point, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult<( + cairo_native::starknet::U256, + cairo_native::starknet::U256, + )> { + Ok((p.x, p.y)) + } + + fn secp256r1_new( + &mut self, + x: cairo_native::starknet::U256, + y: cairo_native::starknet::U256, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult> { + // The following unwraps should be unreachable because the iterator we provide has the + // expected number of bytes. + let point = p256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + x.hi.to_be_bytes().into_iter().chain(x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + y.hi.to_be_bytes().into_iter().chain(y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ); + + if bool::from(point.is_some()) { + Ok(Some(Secp256r1Point { x, y })) + } else { + Ok(None) + } + } + + fn secp256r1_add( + &mut self, + p0: cairo_native::starknet::Secp256r1Point, + p1: cairo_native::starknet::Secp256r1Point, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + // The inner unwraps should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwraps depend on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p0 = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p0.x.hi + .to_be_bytes() + .into_iter() + .chain(p0.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p0.y.hi + .to_be_bytes() + .into_iter() + .chain(p0.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let p1 = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p1.x.hi + .to_be_bytes() + .into_iter() + .chain(p1.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p1.y.hi + .to_be_bytes() + .into_iter() + .chain(p1.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + + let p = p0 + p1; + + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256r1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256r1_mul( + &mut self, + p: cairo_native::starknet::Secp256r1Point, + m: cairo_native::starknet::U256, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult { + // The inner unwrap should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwrap depends on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p.x.hi.to_be_bytes().into_iter().chain(p.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p.y.hi.to_be_bytes().into_iter().chain(p.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let m: p256::Scalar = p256::elliptic_curve::ScalarPrimitive::from_slice(&{ + let mut buf = [0u8; 32]; + buf[0..16].copy_from_slice(&m.hi.to_be_bytes()); + buf[16..32].copy_from_slice(&m.lo.to_be_bytes()); + buf + }) + .map_err(|_| { + vec![Felt::from_bytes_be( + b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0invalid scalar", + )] + })? + .into(); + + let p = p * m; + + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256r1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256r1_get_point_from_x( + &mut self, + x: cairo_native::starknet::U256, + y_parity: bool, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult> { + let point = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_bytes( + p256::CompressedPoint::from_exact_iter( + once(0x02 | y_parity as u8) + .chain(x.hi.to_be_bytes()) + .chain(x.lo.to_be_bytes()), + ) + .unwrap(), + ) + .unwrap(), + ); + + if bool::from(point.is_some()) { + let p = point.unwrap(); + + let p = p.to_encoded_point(false); + let y = match p.coordinates() { + Coordinates::Uncompressed { y, .. } => y, + _ => unreachable!(), + }; + + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Some(Secp256r1Point { + x, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + })) + } else { + Ok(None) + } + } + + fn secp256r1_get_xy( + &mut self, + p: cairo_native::starknet::Secp256r1Point, + _remaining_gas: &mut u128, + ) -> cairo_native::starknet::SyscallResult<( + cairo_native::starknet::U256, + cairo_native::starknet::U256, + )> { + Ok((p.x, p.y)) + } +} diff --git a/src/libfuncs/enum.rs b/src/libfuncs/enum.rs index 43aa00e37..44a8bb245 100644 --- a/src/libfuncs/enum.rs +++ b/src/libfuncs/enum.rs @@ -219,7 +219,6 @@ pub fn build_match<'ctx, 'this>( let variant_ids = type_info.variants().unwrap(); match variant_ids.len() { 0 | 1 => { - dbg!(info.branch_signatures().len()); entry.append_operation(helper.br(0, &[entry.argument(0)?.into()], location)); } _ => {