├── .gitignore ├── README.md ├── cairo ├── .editorconfig ├── .github │ └── workflows │ │ └── rust.yml ├── .gitignore ├── .vscode │ └── extensions.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── Scarb.toml ├── corelib │ ├── Scarb.toml │ ├── cairo_project.toml │ └── src │ │ ├── array.cairo │ │ ├── box.cairo │ │ ├── byte_array.cairo │ │ ├── bytes_31.cairo │ │ ├── clone.cairo │ │ ├── cmp.cairo │ │ ├── debug.cairo │ │ ├── dict.cairo │ │ ├── ec.cairo │ │ ├── ecdsa.cairo │ │ ├── gas.cairo │ │ ├── hash.cairo │ │ ├── integer.cairo │ │ ├── internal.cairo │ │ ├── keccak.cairo │ │ ├── lib.cairo │ │ ├── math.cairo │ │ ├── nullable.cairo │ │ ├── option.cairo │ │ ├── panics.cairo │ │ ├── pedersen.cairo │ │ ├── poseidon.cairo │ │ ├── result.cairo │ │ ├── serde.cairo │ │ ├── starknet.cairo │ │ ├── starknet │ │ ├── account.cairo │ │ ├── class_hash.cairo │ │ ├── contract_address.cairo │ │ ├── eth_address.cairo │ │ ├── event.cairo │ │ ├── info.cairo │ │ ├── secp256_trait.cairo │ │ ├── secp256k1.cairo │ │ ├── secp256r1.cairo │ │ ├── storage_access.cairo │ │ ├── syscalls.cairo │ │ └── testing.cairo │ │ ├── test.cairo │ │ ├── test │ │ ├── array_test.cairo │ │ ├── bool_test.cairo │ │ ├── box_test.cairo │ │ ├── byte_array_test.cairo │ │ ├── bytes31_test.cairo │ │ ├── cmp_test.cairo │ │ ├── dict_test.cairo │ │ ├── ec_test.cairo │ │ ├── felt_test.cairo │ │ ├── hash_test.cairo │ │ ├── integer_test.cairo │ │ ├── keccak_test.cairo │ │ ├── math_test.cairo │ │ ├── plugins_test.cairo │ │ ├── secp256k1_test.cairo │ │ ├── secp256r1_test.cairo │ │ ├── test_utils.cairo │ │ └── testing_test.cairo │ │ ├── testing.cairo │ │ ├── traits.cairo │ │ └── zeroable.cairo ├── exercises │ ├── README.md │ ├── constants.cairo │ ├── conversions.cairo │ ├── main.cairo │ ├── operations.cairo │ ├── short_string.cairo │ └── tuple.cairo ├── info.toml ├── src │ ├── exercise.rs │ ├── main.rs │ ├── project.rs │ ├── run.rs │ ├── starklings_runner.rs │ ├── starklings_tester.rs │ ├── ui.rs │ └── verify.rs └── tests │ ├── fixture │ └── cairo │ │ ├── compileFail.cairo │ │ ├── compilePass.cairo │ │ ├── info.toml │ │ ├── testFails.cairo │ │ └── testPass.cairo │ └── integration_tests.rs ├── circom ├── README.md ├── exrx-is-zero │ ├── README.md │ └── is-zero.task.circom └── exrx-square │ ├── README.md │ └── square.task.circom ├── risc0 └── examples │ ├── password-checker │ ├── Cargo.toml │ ├── README.md │ ├── core │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── methods │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── guest │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── main.rs │ │ └── src │ │ │ └── lib.rs │ └── src │ │ └── main.rs │ ├── sudoku │ ├── .gitignore │ ├── Cargo.toml │ ├── LICENSE │ ├── board │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── host │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── methods │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── guest │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── bin │ │ │ │ └── layout.rs │ │ └── src │ │ │ └── lib.rs │ └── rust-toolchain │ └── wordle │ ├── Cargo.toml │ ├── README.md │ ├── core │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ ├── methods │ ├── Cargo.toml │ ├── build.rs │ ├── guest │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── src │ │ └── lib.rs │ └── src │ ├── main.rs │ └── wordlist.rs └── rust ├── .all-contributorsrc ├── .clog.toml ├── .editorconfig ├── .gitignore ├── .gitpod.yml ├── .replit ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── homeworks ├── homework4 │ ├── functions │ │ ├── README.md │ │ ├── functions1.rs │ │ ├── functions2.rs │ │ ├── functions3.rs │ │ ├── functions4.rs │ │ ├── functions5.rs │ │ └── mod.rs │ ├── if │ │ ├── README.md │ │ ├── if1.rs │ │ ├── if2.rs │ │ └── mod.rs │ ├── primitive_types │ │ ├── README.md │ │ ├── mod.rs │ │ ├── primitive_types1.rs │ │ ├── primitive_types2.rs │ │ ├── primitive_types3.rs │ │ ├── primitive_types4.rs │ │ ├── primitive_types5.rs │ │ └── primitive_types6.rs │ ├── strings │ │ ├── README.md │ │ ├── mod.rs │ │ ├── strings1.rs │ │ └── strings2.rs │ └── variables │ │ ├── README.md │ │ ├── mod.rs │ │ ├── variables1.rs │ │ ├── variables2.rs │ │ ├── variables3.rs │ │ ├── variables4.rs │ │ ├── variables5.rs │ │ └── variables6.rs └── mod.rs ├── info.toml ├── install.ps1 ├── install.sh ├── src ├── exercise.rs ├── lib.rs ├── main.rs ├── run.rs ├── ui.rs └── verify.rs └── tests ├── fixture ├── failure │ ├── compFailure.rs │ ├── compNoExercise.rs │ ├── info.toml │ ├── testFailure.rs │ └── testNotPassed.rs ├── state │ ├── finished_exercise.rs │ ├── info.toml │ ├── pending_exercise.rs │ └── pending_test_exercise.rs └── success │ ├── compSuccess.rs │ ├── info.toml │ └── testSuccess.rs └── integration_tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZeroKnowledgeBootcamp 2 | 3 | Exercises for the Zero Knowledge Bootcamp 4 | 5 | -------------------------------------------------------------------------------- /cairo/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.rs] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 4 8 | -------------------------------------------------------------------------------- /cairo/.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: starklings Tests 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Build 18 | run: cargo build --verbose 19 | - name: Run tests 20 | run: cargo test --verbose 21 | -------------------------------------------------------------------------------- /cairo/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | target/ 3 | **/*.rs.bk 4 | .DS_Store 5 | *.pdb 6 | exercises/clippy/Cargo.toml 7 | exercises/clippy/Cargo.lock 8 | rust-project.json 9 | .idea 10 | .vscode/* 11 | !.vscode/extensions.json 12 | *.iml 13 | *.o 14 | solutions/ -------------------------------------------------------------------------------- /cairo/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "rust-lang.rust-analyzer" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /cairo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "starklings" 3 | version = "0.1.0" 4 | authors = [ 5 | "Shramee ", 6 | "Mathieu ", 7 | # Authors of original rustlings 8 | "Liv ", 9 | "Carol (Nichols || Goulding) ", 10 | ] 11 | edition = "2021" 12 | 13 | [dependencies] 14 | argh = "0.1" 15 | indicatif = "0.16" 16 | console = "0.15" 17 | notify = "4.0" 18 | toml = "0.5" 19 | regex = "1.5" 20 | serde = { version = "1.0", features = ["derive"] } 21 | serde_json = "1.0.81" 22 | home = "0.5.3" 23 | glob = "0.3.0" 24 | cairo-felt = "0.8.2" 25 | 26 | # Cairo runner dependencies 27 | cairo-lang-runner = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 28 | cairo-lang-test-runner = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 29 | cairo-lang-compiler = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 30 | cairo-lang-casm = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 31 | cairo-lang-diagnostics = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 32 | cairo-lang-debug = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 33 | cairo-lang-defs = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 34 | cairo-lang-sierra = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 35 | cairo-lang-sierra-ap-change = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 36 | cairo-lang-sierra-gas = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 37 | cairo-lang-sierra-generator = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 38 | cairo-lang-semantic = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 39 | cairo-lang-sierra-to-casm = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 40 | cairo-lang-utils = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 41 | cairo-lang-filesystem = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 42 | cairo-lang-starknet = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 43 | cairo-lang-syntax = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 44 | cairo-lang-plugins = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 45 | cairo-lang-lowering = {git = "https://github.com/starkware-libs/cairo", version = "2.1.1"} 46 | 47 | 48 | anyhow = "1.0.66" 49 | ark-ff = "0.4.0-alpha.7" 50 | ark-std = "0.3.0" 51 | clap = { version = "4.0", features = ["derive"] } 52 | itertools = "0.10.3" 53 | num-bigint = "0.4" 54 | num-traits = "0.2" 55 | salsa = "0.16.1" 56 | thiserror = "1.0.32" 57 | rayon = "0.9.0" 58 | colored = "2" 59 | unescaper = "0.1.1" 60 | 61 | [dev-dependencies] 62 | assert_cmd = "0.11.0" 63 | predicates = "1.0.1" 64 | glob = "0.3.0" 65 | 66 | [[bin]] 67 | name = "starklings" 68 | path = "src/main.rs" 69 | 70 | [[bin]] 71 | name = "starklings-runner" 72 | path = "src/starklings_runner.rs" 73 | 74 | [[bin]] 75 | name = "starklings-tester" 76 | path = "src/starklings_tester.rs" 77 | -------------------------------------------------------------------------------- /cairo/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Carol (Nichols || Goulding) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /cairo/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "starklings_cairo1" 3 | version = "0.1.0" 4 | 5 | # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest 6 | 7 | [dependencies] 8 | starknet = ">=2.1.0" 9 | 10 | [[target.starknet-contract]] -------------------------------------------------------------------------------- /cairo/corelib/Scarb.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core" 3 | version = "2.1.1" 4 | 5 | # NOTE: This is non-public, unstable Scarb's field, which instructs resolver that this package does not 6 | # depend on `core`, which is only true for this particular package. Nobody else should use it. 7 | no-core = true 8 | -------------------------------------------------------------------------------- /cairo/corelib/cairo_project.toml: -------------------------------------------------------------------------------- 1 | [crate_roots] 2 | core = "src" 3 | -------------------------------------------------------------------------------- /cairo/corelib/src/box.cairo: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Drop)] 2 | extern type Box; 3 | 4 | // These functions are only exposed in the corelib through the trait below since calling them 5 | // directly with tuples panics due to auto unpacking of the tuple. 6 | // TODO(Gil): Expose in the core lib when the described behaviour is fixed. 7 | extern fn into_box(value: T) -> Box nopanic; 8 | extern fn unbox(box: Box) -> T nopanic; 9 | 10 | #[generate_trait] 11 | impl BoxImpl of BoxTrait { 12 | #[inline(always)] 13 | fn new(value: T) -> Box nopanic { 14 | into_box(value) 15 | } 16 | #[inline(always)] 17 | fn unbox(self: Box) -> T nopanic { 18 | unbox(self) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cairo/corelib/src/clone.cairo: -------------------------------------------------------------------------------- 1 | trait Clone { 2 | fn clone(self: @T) -> T; 3 | } 4 | 5 | impl TCopyClone> of Clone { 6 | fn clone(self: @T) -> T { 7 | *self 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /cairo/corelib/src/cmp.cairo: -------------------------------------------------------------------------------- 1 | fn min, impl DropT: Drop, impl CopyT: Copy>( 2 | a: T, b: T 3 | ) -> T { 4 | if a > b { 5 | return b; 6 | } 7 | a 8 | } 9 | 10 | fn max, impl DropT: Drop, impl CopyT: Copy>( 11 | a: T, b: T 12 | ) -> T { 13 | if a > b { 14 | return a; 15 | } 16 | b 17 | } 18 | -------------------------------------------------------------------------------- /cairo/corelib/src/debug.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | use traits::Into; 3 | use option::Option; 4 | 5 | // Usage: 6 | // 7 | // use debug::PrintTrait; 8 | // 9 | // 1.print(); 10 | // 11 | // (1 == 2).print(); 12 | // 13 | // get_caller_address().print(); 14 | // 15 | // let mut arr = array![]; 16 | // arr.append('1234567890123456789012345678901'); 17 | // arr.append('Sca'); 18 | // arr.append('SomeVeryLongMessage'); 19 | // arr.print(); 20 | 21 | extern fn print(message: Array) nopanic; 22 | 23 | fn print_felt252(message: felt252) { 24 | print(array![message]); 25 | } 26 | 27 | trait PrintTrait { 28 | fn print(self: T); 29 | } 30 | 31 | impl Felt252PrintImpl of PrintTrait { 32 | fn print(self: felt252) { 33 | print_felt252(self); 34 | } 35 | } 36 | 37 | impl BoolPrintImpl of PrintTrait { 38 | fn print(self: bool) { 39 | if self { 40 | 'true'.print(); 41 | } else { 42 | 'false'.print(); 43 | } 44 | } 45 | } 46 | 47 | impl ContractAddressPrintImpl of PrintTrait { 48 | fn print(self: starknet::ContractAddress) { 49 | Into::<_, felt252>::into(self).print(); 50 | } 51 | } 52 | 53 | impl U8PrintImpl of PrintTrait { 54 | fn print(self: u8) { 55 | Into::<_, felt252>::into(self).print(); 56 | } 57 | } 58 | 59 | impl U16PrintImpl of PrintTrait { 60 | fn print(self: u16) { 61 | Into::<_, felt252>::into(self).print(); 62 | } 63 | } 64 | 65 | impl U32PrintImpl of PrintTrait { 66 | fn print(self: u32) { 67 | Into::<_, felt252>::into(self).print(); 68 | } 69 | } 70 | 71 | impl U64PrintImpl of PrintTrait { 72 | fn print(self: u64) { 73 | Into::<_, felt252>::into(self).print(); 74 | } 75 | } 76 | 77 | impl U128PrintImpl of PrintTrait { 78 | fn print(self: u128) { 79 | Into::<_, felt252>::into(self).print(); 80 | } 81 | } 82 | 83 | impl U256PrintImpl of PrintTrait { 84 | fn print(self: u256) { 85 | Into::::into(self.low).print(); 86 | Into::::into(self.high).print(); 87 | } 88 | } 89 | 90 | impl ArrayGenericPrintImpl of PrintTrait> { 91 | fn print(mut self: Array) { 92 | print(self); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /cairo/corelib/src/dict.cairo: -------------------------------------------------------------------------------- 1 | use traits::{Index, Default}; 2 | 3 | extern type Felt252Dict; 4 | extern type SquashedFelt252Dict; 5 | extern type Felt252DictEntry; 6 | impl SquashedFelt252DictDrop> of Drop>; 7 | 8 | extern fn felt252_dict_new() -> Felt252Dict implicits(SegmentArena) nopanic; 9 | 10 | extern fn felt252_dict_entry_get( 11 | dict: Felt252Dict, key: felt252 12 | ) -> (Felt252DictEntry, T) nopanic; 13 | 14 | extern fn felt252_dict_entry_finalize( 15 | dict_entry: Felt252DictEntry, new_value: T 16 | ) -> Felt252Dict nopanic; 17 | 18 | /// Squashes the dictionary and returns SquashedFelt252Dict. 19 | /// 20 | /// NOTE: Never use this libfunc directly. Use Felt252DictTrait::squash() instead. Using this 21 | /// libfunc directly will result in multiple unnecessary copies of the libfunc in the compiled CASM 22 | /// code. 23 | extern fn felt252_dict_squash( 24 | dict: Felt252Dict 25 | ) -> SquashedFelt252Dict implicits(RangeCheck, GasBuiltin, SegmentArena) nopanic; 26 | 27 | trait Felt252DictTrait { 28 | /// Inserts the given value for the given key. 29 | /// 30 | /// Requires the `Destruct` trait, as the previous value is dropped. 31 | fn insert>(ref self: Felt252Dict, key: felt252, value: T); 32 | /// Returns a copy of the value at the given key. 33 | /// 34 | /// Requires the `Copy` trait. 35 | fn get>(ref self: Felt252Dict, key: felt252) -> T; 36 | fn squash(self: Felt252Dict) -> SquashedFelt252Dict nopanic; 37 | fn entry(self: Felt252Dict, key: felt252) -> (Felt252DictEntry, T) nopanic; 38 | } 39 | impl Felt252DictImpl> of Felt252DictTrait { 40 | #[inline] 41 | fn insert>(ref self: Felt252Dict, key: felt252, value: T) { 42 | let (entry, _prev_value) = felt252_dict_entry_get(self, key); 43 | self = felt252_dict_entry_finalize(entry, value); 44 | } 45 | 46 | #[inline] 47 | fn get>(ref self: Felt252Dict, key: felt252) -> T { 48 | let (entry, prev_value) = felt252_dict_entry_get(self, key); 49 | let return_value = prev_value; 50 | self = felt252_dict_entry_finalize(entry, prev_value); 51 | return_value 52 | } 53 | 54 | #[inline(never)] 55 | fn squash(self: Felt252Dict) -> SquashedFelt252Dict nopanic { 56 | felt252_dict_squash(self) 57 | } 58 | 59 | #[inline(always)] 60 | fn entry(self: Felt252Dict, key: felt252) -> (Felt252DictEntry, T) nopanic { 61 | felt252_dict_entry_get(self, key) 62 | } 63 | } 64 | 65 | trait Felt252DictEntryTrait { 66 | fn finalize(self: Felt252DictEntry, new_value: T) -> Felt252Dict; 67 | } 68 | 69 | impl Felt252DictEntryImpl> of Felt252DictEntryTrait { 70 | #[inline(always)] 71 | fn finalize(self: Felt252DictEntry, new_value: T) -> Felt252Dict { 72 | felt252_dict_entry_finalize(self, new_value) 73 | } 74 | } 75 | 76 | impl Felt252DictDefault of Default> { 77 | #[inline(always)] 78 | fn default() -> Felt252Dict { 79 | felt252_dict_new() 80 | } 81 | } 82 | 83 | impl Felt252DictDestruct< 84 | T, impl TDrop: Drop, impl TDefault: Felt252DictValue 85 | > of Destruct> { 86 | #[inline(always)] 87 | fn destruct(self: Felt252Dict) nopanic { 88 | self.squash(); 89 | } 90 | } 91 | 92 | impl Felt252DictEntryDestruct< 93 | T, impl TDrop: Drop, impl TDefault: Felt252DictValue 94 | > of Destruct> { 95 | #[inline(always)] 96 | fn destruct(self: Felt252DictEntry::) nopanic { 97 | felt252_dict_entry_finalize(self, TDefault::zero_default()); 98 | } 99 | } 100 | 101 | impl Felt252DictIndex< 102 | T, 103 | impl TDictImpl: Felt252DictTrait, 104 | impl TCopy: Copy, 105 | impl EntryDestruct: Destruct> 106 | > of Index, felt252, T> { 107 | #[inline(always)] 108 | fn index(ref self: Felt252Dict, index: felt252) -> T { 109 | self.get(index) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /cairo/corelib/src/ecdsa.cairo: -------------------------------------------------------------------------------- 1 | use ec::EcPointTrait; 2 | use option::OptionTrait; 3 | use traits::{Into, TryInto}; 4 | use zeroable::IsZeroResult; 5 | 6 | // Checks if (`signature_r`, `signature_s`) is a valid ECDSA signature for the given `public_key` 7 | // on the given `message`. 8 | // 9 | // Note: the verification algorithm implemented by this function slightly deviates from the 10 | // standard ECDSA. 11 | // While this does not allow to create valid signatures if one does not possess the private key, 12 | // it means that the signature algorithm used should be modified accordingly. 13 | // Namely, it should check that `r, s < stark_curve::ORDER`. 14 | // 15 | // Arguments: 16 | // * `message_hash` - the signed message. 17 | // * `public_key` - the public key corresponding to the key with which the message was signed. 18 | // * `signature_r` - the `r` component of the ECDSA signature. 19 | // * `signature_s` - the `s` component of the ECDSA signature. 20 | // 21 | // Returns: 22 | // `true` if the signature is valid and `false` otherwise. 23 | // TODO(lior): Make this function nopanic once possible. 24 | fn check_ecdsa_signature( 25 | message_hash: felt252, public_key: felt252, signature_r: felt252, signature_s: felt252 26 | ) -> bool { 27 | // TODO(orizi): Change to || once it does not prevent `a == 0` comparison optimization. 28 | // Check that s != 0 (mod stark_curve::ORDER). 29 | if signature_s == 0 { 30 | return false; 31 | } 32 | if signature_s == ec::stark_curve::ORDER { 33 | return false; 34 | } 35 | if signature_r == ec::stark_curve::ORDER { 36 | return false; 37 | } 38 | 39 | // Check that the public key is the x coordinate of a point on the curve and get such a point. 40 | let public_key_point = match ec::EcPointTrait::new_from_x(public_key) { 41 | Option::Some(point) => point, 42 | Option::None => { 43 | return false; 44 | }, 45 | }; 46 | 47 | // Check that `r` is the x coordinate of a point on the curve and get such a point. 48 | // Note that this ensures that `r != 0`. 49 | let signature_r_point = match EcPointTrait::new_from_x(signature_r) { 50 | Option::Some(point) => point, 51 | Option::None => { 52 | return false; 53 | }, 54 | }; 55 | 56 | // Retrieve the generator point. 57 | let gen_point = match EcPointTrait::new(ec::stark_curve::GEN_X, ec::stark_curve::GEN_Y) { 58 | Option::Some(point) => point, 59 | Option::None => { 60 | return false; 61 | }, 62 | }; 63 | 64 | // To verify ECDSA, obtain: 65 | // zG = z * G, where z is the message and G is a generator of the EC. 66 | // rQ = r * Q, where Q.x = public_key. 67 | // sR = s * R, where R.x = r. 68 | // and check that: 69 | // zG +/- rQ = +/- sR, or more efficiently that: 70 | // (zG +/- rQ).x = sR.x. 71 | let sR: EcPoint = signature_r_point.mul(signature_s); 72 | let sR_x = match sR.try_into() { 73 | Option::Some(pt) => { 74 | let (x, _) = ec::ec_point_unwrap(pt); 75 | x 76 | }, 77 | Option::None => { 78 | return false; 79 | }, 80 | }; 81 | 82 | let zG: EcPoint = gen_point.mul(message_hash); 83 | let rQ: EcPoint = public_key_point.mul(signature_r); 84 | match (zG + rQ).try_into() { 85 | Option::Some(pt) => { 86 | let (x, _) = ec::ec_point_unwrap(pt); 87 | if (x == sR_x) { 88 | return true; 89 | } 90 | }, 91 | Option::None => {}, 92 | }; 93 | 94 | match (zG - rQ).try_into() { 95 | Option::Some(pt) => { 96 | let (x, _) = ec::ec_point_unwrap(pt); 97 | if (x == sR_x) { 98 | return true; 99 | } 100 | }, 101 | Option::None => {}, 102 | }; 103 | 104 | return false; 105 | } 106 | 107 | /// Receives a signature and the signed message hash. 108 | /// Returns the public key associated with the signer. 109 | fn recover_public_key( 110 | message_hash: felt252, signature_r: felt252, signature_s: felt252, y_parity: bool 111 | ) -> Option { 112 | let mut signature_r_point = EcPointTrait::new_from_x(signature_r)?; 113 | let (_, y) = signature_r_point.try_into()?.coordinates(); 114 | let y: u256 = y.into(); 115 | // If the actual the parity of the actual y is different than requested, flip the parity. 116 | if (y.low & 1 == 1) != y_parity { 117 | signature_r_point = -signature_r_point; 118 | } 119 | 120 | // Retrieve the generator point. 121 | let gen_point = EcPointTrait::new(ec::stark_curve::GEN_X, ec::stark_curve::GEN_Y)?; 122 | 123 | // a Valid signature should satisfy: 124 | // zG + rQ = sR. 125 | // Where: 126 | // zG = z * G, z is the message and G is a generator of the EC. 127 | // rQ = r * Q, Q.x = public_key. 128 | // sR = s * R, where R.x = r and R.y is determined by y_parity. 129 | // 130 | // Hence: 131 | // rQ = sR - zG. 132 | // Q = (s/r)R - (z/r)G 133 | // and we can recover the public key using: 134 | // Q.x = ((s/r)R - (z/r)G).x. 135 | let r_nz: u256 = signature_r.into(); 136 | let r_nz = r_nz.try_into()?; 137 | let ord_nz: u256 = ec::stark_curve::ORDER.into(); 138 | let ord_nz = ord_nz.try_into()?; 139 | let r_inv = math::inv_mod(r_nz, ord_nz)?; 140 | let s_div_r: felt252 = math::u256_mul_mod_n(signature_s.into(), r_inv, ord_nz).try_into()?; 141 | let z_div_r: felt252 = math::u256_mul_mod_n(message_hash.into(), r_inv, ord_nz).try_into()?; 142 | let s_div_rR: EcPoint = signature_r_point.mul(s_div_r); 143 | let z_div_rG: EcPoint = gen_point.mul(z_div_r); 144 | let (x, _) = (s_div_rR - z_div_rG).try_into()?.coordinates(); 145 | Option::Some(x) 146 | } 147 | -------------------------------------------------------------------------------- /cairo/corelib/src/gas.cairo: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Drop)] 2 | extern type BuiltinCosts; 3 | extern type GasBuiltin; 4 | 5 | extern fn withdraw_gas() -> Option<()> implicits(RangeCheck, GasBuiltin) nopanic; 6 | extern fn withdraw_gas_all( 7 | costs: BuiltinCosts 8 | ) -> Option<()> implicits(RangeCheck, GasBuiltin) nopanic; 9 | extern fn get_builtin_costs() -> BuiltinCosts nopanic; 10 | -------------------------------------------------------------------------------- /cairo/corelib/src/hash.cairo: -------------------------------------------------------------------------------- 1 | use traits::Into; 2 | use starknet::ContractAddress; 3 | 4 | /// A trait for hash state accumulators. 5 | trait HashStateTrait { 6 | fn update(self: S, value: felt252) -> S; 7 | fn finalize(self: S) -> felt252; 8 | } 9 | 10 | /// A trait for values that can be hashed. 11 | trait Hash> { 12 | /// Updates the hash state with the given value. 13 | fn update_state(state: S, value: T) -> S; 14 | } 15 | 16 | /// Trait for hashing values. 17 | /// Used for backwards compatibility. 18 | /// NOTE: Implement `Hash` instead of this trait if possible. 19 | trait LegacyHash { 20 | fn hash(state: felt252, value: T) -> felt252; 21 | } 22 | 23 | /// Implementation of `LegacyHash` for types that have `Hash` for backwards compatibility. 24 | impl LegacyHashForHash< 25 | T, impl THash: Hash 26 | > of LegacyHash { 27 | #[inline(always)] 28 | fn hash(state: felt252, value: T) -> felt252 { 29 | THash::update_state(pedersen::HashState { state }, value).state 30 | } 31 | } 32 | 33 | /// Extension trait for hash state accumulators. 34 | trait HashStateExTrait { 35 | /// Updates the hash state with the given value. 36 | fn update_with(self: S, value: T) -> S; 37 | } 38 | 39 | impl HashStateEx< 40 | S, impl SHashState: HashStateTrait, T, impl THash: Hash 41 | > of HashStateExTrait { 42 | #[inline(always)] 43 | fn update_with(self: S, value: T) -> S { 44 | THash::update_state(self, value) 45 | } 46 | } 47 | 48 | impl HashFelt252> of Hash { 49 | #[inline(always)] 50 | fn update_state(state: S, value: felt252) -> S { 51 | state.update(value) 52 | } 53 | } 54 | 55 | impl HashBool< 56 | S, impl SHashState: HashStateTrait, impl SDrop: Drop 57 | > of Hash { 58 | #[inline(always)] 59 | fn update_state(state: S, value: bool) -> S { 60 | state.update(value.into()) 61 | } 62 | } 63 | 64 | impl HashU8, impl SDrop: Drop> of Hash { 65 | #[inline(always)] 66 | fn update_state(state: S, value: u8) -> S { 67 | state.update(value.into()) 68 | } 69 | } 70 | 71 | impl HashU16< 72 | S, impl SHashState: HashStateTrait, impl SDrop: Drop 73 | > of Hash { 74 | #[inline(always)] 75 | fn update_state(state: S, value: u16) -> S { 76 | state.update(value.into()) 77 | } 78 | } 79 | 80 | impl HashU32< 81 | S, impl SHashState: HashStateTrait, impl SDrop: Drop 82 | > of Hash { 83 | #[inline(always)] 84 | fn update_state(state: S, value: u32) -> S { 85 | state.update(value.into()) 86 | } 87 | } 88 | 89 | impl HashU64< 90 | S, impl SHashState: HashStateTrait, impl SDrop: Drop 91 | > of Hash { 92 | #[inline(always)] 93 | fn update_state(state: S, value: u64) -> S { 94 | state.update(value.into()) 95 | } 96 | } 97 | 98 | impl HashU128< 99 | S, impl SHashState: HashStateTrait, impl SDrop: Drop 100 | > of Hash { 101 | #[inline(always)] 102 | fn update_state(state: S, value: u128) -> S { 103 | state.update(value.into()) 104 | } 105 | } 106 | 107 | impl HashU256< 108 | S, impl SHashState: HashStateTrait, impl SDrop: Drop 109 | > of Hash { 110 | #[inline(always)] 111 | fn update_state(state: S, value: u256) -> S { 112 | state.update_with(value.low).update_with(value.high) 113 | } 114 | } 115 | 116 | impl HashContractAddress< 117 | S, impl SHashState: HashStateTrait, impl SDrop: Drop 118 | > of Hash { 119 | #[inline(always)] 120 | fn update_state(state: S, value: ContractAddress) -> S { 121 | state.update(value.into()) 122 | } 123 | } 124 | 125 | impl TupleSize0Hash> of Hash<(), S, SHashState> { 126 | #[inline(always)] 127 | fn update_state(state: S, value: ()) -> S { 128 | state 129 | } 130 | } 131 | 132 | impl TupleSize1Hash< 133 | E0, S, impl E0Hash: Hash, impl SHashState: HashStateTrait 134 | > of Hash<(E0,), S, SHashState> { 135 | #[inline(always)] 136 | fn update_state(state: S, value: (E0,)) -> S { 137 | let (e0,) = value; 138 | state.update_with(e0) 139 | } 140 | } 141 | 142 | impl TupleSize2Hash< 143 | E0, 144 | E1, 145 | S, 146 | impl SHashState: HashStateTrait, 147 | impl E0Hash: Hash, 148 | impl E1Hash: Hash, 149 | impl E0Drop: Drop, 150 | impl E1Drop: Drop, 151 | > of Hash<(E0, E1), S, SHashState> { 152 | #[inline(always)] 153 | fn update_state(state: S, value: (E0, E1,)) -> S { 154 | let (e0, e1) = value; 155 | state.update_with(e0).update_with(e1) 156 | } 157 | } 158 | 159 | impl TupleSize3Hash< 160 | E0, 161 | E1, 162 | E2, 163 | S, 164 | impl SHashState: HashStateTrait, 165 | impl E0Hash: Hash, 166 | impl E1Hash: Hash, 167 | impl E2Hash: Hash, 168 | impl E0Drop: Drop, 169 | impl E1Drop: Drop, 170 | impl E2Drop: Drop, 171 | > of Hash<(E0, E1, E2), S, SHashState> { 172 | #[inline(always)] 173 | fn update_state(state: S, value: (E0, E1, E2)) -> S { 174 | let (e0, e1, e2) = value; 175 | state.update_with(e0).update_with(e1).update_with(e2) 176 | } 177 | } 178 | 179 | impl TupleSize4Hash< 180 | E0, 181 | E1, 182 | E2, 183 | E3, 184 | S, 185 | impl SHashState: HashStateTrait, 186 | impl E0Hash: Hash, 187 | impl E1Hash: Hash, 188 | impl E2Hash: Hash, 189 | impl E3Hash: Hash, 190 | impl E0Drop: Drop, 191 | impl E1Drop: Drop, 192 | impl E2Drop: Drop, 193 | impl E3Drop: Drop, 194 | > of Hash<(E0, E1, E2, E3), S, SHashState> { 195 | #[inline(always)] 196 | fn update_state(state: S, value: (E0, E1, E2, E3)) -> S { 197 | let (e0, e1, e2, e3) = value; 198 | state.update_with(e0).update_with(e1).update_with(e2).update_with(e3) 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /cairo/corelib/src/internal.cairo: -------------------------------------------------------------------------------- 1 | extern fn revoke_ap_tracking() implicits() nopanic; 2 | 3 | /// Function to enforce that `Implicit` is used by a function calling it. 4 | /// Note: This extern function is not mapped to a Sierra function, and all usages of it are removed 5 | /// during compilation. 6 | extern fn require_implicit() implicits(Implicit) nopanic; 7 | -------------------------------------------------------------------------------- /cairo/corelib/src/keccak.cairo: -------------------------------------------------------------------------------- 1 | use array::{Span, ArrayTrait, SpanTrait}; 2 | use integer::TryInto; 3 | use option::OptionTrait; 4 | use starknet::SyscallResultTrait; 5 | 6 | const KECCAK_FULL_RATE_IN_BYTES: usize = 136; 7 | const KECCAK_FULL_RATE_IN_U64S: usize = 17; 8 | const BYTES_IN_U64_WORD: usize = 8; 9 | 10 | 11 | fn u128_to_u64(input: u128) -> u64 { 12 | input.try_into().unwrap() 13 | } 14 | 15 | fn u128_split(input: u128) -> (u64, u64) { 16 | let (high, low) = integer::u128_safe_divmod( 17 | input, 0x10000000000000000_u128.try_into().unwrap() 18 | ); 19 | 20 | (u128_to_u64(high), u128_to_u64(low)) 21 | } 22 | 23 | fn keccak_add_u256_le(ref keccak_input: Array::, v: u256) { 24 | let (high, low) = u128_split(v.low); 25 | keccak_input.append(low); 26 | keccak_input.append(high); 27 | let (high, low) = u128_split(v.high); 28 | keccak_input.append(low); 29 | keccak_input.append(high); 30 | } 31 | 32 | 33 | // Computes the keccak256 of multiple u256 values. 34 | // The input values are interpreted as little-endian. 35 | // The 32-byte result is represented as a little-endian u256. 36 | fn keccak_u256s_le_inputs(mut input: Span) -> u256 { 37 | let mut keccak_input: Array:: = Default::default(); 38 | 39 | loop { 40 | match input.pop_front() { 41 | Option::Some(v) => { 42 | keccak_add_u256_le(ref keccak_input, *v); 43 | }, 44 | Option::None => { 45 | break (); 46 | }, 47 | }; 48 | }; 49 | 50 | add_padding(ref keccak_input, 0, 0); 51 | starknet::syscalls::keccak_syscall(keccak_input.span()).unwrap_syscall() 52 | } 53 | 54 | fn keccak_add_u256_be(ref keccak_input: Array::, v: u256) { 55 | let (high, low) = u128_split(integer::u128_byte_reverse(v.high)); 56 | keccak_input.append(low); 57 | keccak_input.append(high); 58 | let (high, low) = u128_split(integer::u128_byte_reverse(v.low)); 59 | keccak_input.append(low); 60 | keccak_input.append(high); 61 | } 62 | 63 | // Computes the keccak256 of multiple u256 values. 64 | // The input values are interpreted as big-endian. 65 | // The 32-byte result is represented as a little-endian u256. 66 | fn keccak_u256s_be_inputs(mut input: Span) -> u256 { 67 | let mut keccak_input: Array:: = Default::default(); 68 | 69 | loop { 70 | match input.pop_front() { 71 | Option::Some(v) => { 72 | keccak_add_u256_be(ref keccak_input, *v); 73 | }, 74 | Option::None => { 75 | break (); 76 | }, 77 | }; 78 | }; 79 | 80 | add_padding(ref keccak_input, 0, 0); 81 | starknet::syscalls::keccak_syscall(keccak_input.span()).unwrap_syscall() 82 | } 83 | 84 | // Computes the keccak of `input` + `last_input_num_bytes` LSB bytes of `last_input_word`. 85 | // To use this function, split the input into words of 64 bits (little endian). 86 | // For example, to compute keccak('Hello world!'), use: 87 | // inputs = [8031924123371070792, 560229490] 88 | // where: 89 | // 8031924123371070792 == int.from_bytes(b'Hello wo', 'little') 90 | // 560229490 == int.from_bytes(b'rld!', 'little') 91 | // 92 | // Returns the hash as a little endian u256. 93 | fn cairo_keccak(ref input: Array, last_input_word: u64, last_input_num_bytes: usize) -> u256 { 94 | add_padding(ref input, last_input_word, last_input_num_bytes); 95 | starknet::syscalls::keccak_syscall(input.span()).unwrap_syscall() 96 | } 97 | 98 | // The padding in keccak256 is "1 0* 1". 99 | // `last_input_num_bytes` (0-7) is the number of bytes in the last u64 input - `last_input_word`. 100 | fn add_padding(ref input: Array, last_input_word: u64, last_input_num_bytes: usize) { 101 | let words_divisor = KECCAK_FULL_RATE_IN_U64S.try_into().unwrap(); 102 | // `last_block_num_full_words` is in range [0, KECCAK_FULL_RATE_IN_U64S - 1] 103 | let (_, last_block_num_full_words) = integer::u32_safe_divmod(input.len(), words_divisor); 104 | // `last_block_num_bytes` is in range [0, KECCAK_FULL_RATE_IN_BYTES - 1] 105 | let last_block_num_bytes = last_block_num_full_words * BYTES_IN_U64_WORD + last_input_num_bytes; 106 | 107 | // The first word to append would be of the form 108 | // 0x1<`last_input_num_bytes` LSB bytes of `last_input_word`>. 109 | // For example, for `last_input_num_bytes == 4`: 110 | // 0x1000000 + (last_input_word & 0xffffff) 111 | let first_word_to_append = if last_input_num_bytes == 0 { 112 | // This case is handled separately to avoid unnecessary computations. 113 | 1 114 | } else { 115 | let first_padding_byte_part = if last_input_num_bytes == 1 { 116 | 0x100 117 | } else if last_input_num_bytes == 2 { 118 | 0x10000 119 | } else if last_input_num_bytes == 3 { 120 | 0x1000000 121 | } else if last_input_num_bytes == 4 { 122 | 0x100000000 123 | } else if last_input_num_bytes == 5 { 124 | 0x10000000000 125 | } else if last_input_num_bytes == 6 { 126 | 0x1000000000000 127 | } else if last_input_num_bytes == 7 { 128 | 0x100000000000000 129 | } else { 130 | panic_with_felt252('Keccak last input word >7b') 131 | }; 132 | let (_, r) = integer::u64_safe_divmod( 133 | last_input_word, first_padding_byte_part.try_into().unwrap() 134 | ); 135 | first_padding_byte_part + r 136 | }; 137 | 138 | if last_block_num_full_words == KECCAK_FULL_RATE_IN_U64S - 1 { 139 | input.append(0x8000000000000000 + first_word_to_append); 140 | return; 141 | } 142 | 143 | // last_block_num_full_words < KECCAK_FULL_RATE_IN_U64S - 1 144 | input.append(first_word_to_append); 145 | finalize_padding(ref input, KECCAK_FULL_RATE_IN_U64S - 1 - last_block_num_full_words); 146 | } 147 | 148 | // Finalize the padding by appending "0* 1". 149 | fn finalize_padding(ref input: Array, num_padding_words: u32) { 150 | if (num_padding_words == 1) { 151 | input.append(0x8000000000000000); 152 | return; 153 | } 154 | 155 | input.append(0); 156 | finalize_padding(ref input, num_padding_words - 1); 157 | } 158 | 159 | -------------------------------------------------------------------------------- /cairo/corelib/src/math.cairo: -------------------------------------------------------------------------------- 1 | use zeroable::{IsZeroResult, NonZeroIntoImpl, Zeroable}; 2 | use traits::{Into, TryInto}; 3 | use option::OptionTrait; 4 | use integer::{u256_wide_mul, u512_safe_div_rem_by_u256}; 5 | 6 | // TODO(yuval): use signed integers once supported. 7 | // TODO(yuval): use a single impl of a trait with associated impls, once associated impls are 8 | // supported. 9 | /// Extended GCD: finds (g, s, t, sub_direction) such that 10 | /// `g = gcd(a, b) = s * a - t * b` if `sub_direction` is true, or 11 | /// `g = gcd(a, b) = t * b - s * a` if `sub_direction` is false. 12 | /// `(s, -t)` or `(-s, t)` are the Bezout coefficients (according to `sub_direction`). 13 | /// 14 | /// Uses the Extended Euclidean algorithm. 15 | fn egcd< 16 | T, 17 | impl TCopyImpl: Copy, 18 | impl TDropImpl: Drop, 19 | impl TAddImpl: Add, 20 | impl TMulImpl: Mul, 21 | impl TDivRemImpl: DivRem, 22 | impl TZeroableImpl: Zeroable, 23 | impl TOneableImpl: Oneable, 24 | impl TTryIntoNonZeroImpl: TryInto>, 25 | >( 26 | a: NonZero, b: NonZero 27 | ) -> (T, T, T, bool) { 28 | let (q, r) = TDivRemImpl::div_rem(a.into(), b); 29 | 30 | if r.is_zero() { 31 | return (b.into(), TZeroableImpl::zero(), TOneableImpl::one(), false); 32 | } 33 | 34 | // `sign` (1 for true, -1 for false) is the sign of `g` in the current iteration. 35 | // 0 is considered negative for this purpose. 36 | let (g, s, t, sign) = egcd(b, r.try_into().unwrap()); 37 | // We know that `a = q*b + r` and that `s*b - t*r = sign*g`. 38 | // So `t*a - (s + q*t)*b = t*r - s*b = sign*g`. 39 | // Thus we pick `new_s = t`, `new_t = s + q*t`, `new_sign = !sign`. 40 | (g, t, s + q * t, !sign) 41 | } 42 | 43 | // TODO(yuval): use signed integers once supported. 44 | /// Returns the inverse of `a` modulo `n`, or None if `gcd(a, n) > 1`. 45 | fn inv_mod< 46 | T, 47 | impl TCopyImpl: Copy, 48 | impl TDropImpl: Drop, 49 | impl TAddImpl: Add, 50 | impl TSubImpl: Sub, 51 | impl TMulImpl: Mul, 52 | impl TDivRemImpl: DivRem, 53 | impl TZeroableImpl: Zeroable, 54 | impl TOneableImpl: Oneable, 55 | impl TTryIntoNonZeroImpl: TryInto>, 56 | >( 57 | a: NonZero, n: NonZero 58 | ) -> Option { 59 | if TOneableImpl::is_one(n.into()) { 60 | return Option::Some(TZeroableImpl::zero()); 61 | } 62 | let (g, s, _, sub_direction) = egcd(a, n); 63 | if g.is_one() { 64 | // `1 = g = gcd(a, n) = +-(s*a - t*n) => s*a = +-1 (mod n)`. 65 | // The absolute values of Bezout coefficients are guaranteed to be `< n`. 66 | // With n > 1 and gcd = 1, `s` can't be 0. 67 | if sub_direction { 68 | // `s` is the Bezout coefficient, `0 < s < n`. 69 | Option::Some(s) 70 | } else { 71 | // `-s` is the Bezout coefficient. 72 | // `-n < -s < 0 => 0 < n - s < n`, and `n - s = -s (mod n)`. 73 | Option::Some(n.into() - s) 74 | } 75 | } else { 76 | Option::None 77 | } 78 | } 79 | 80 | /// Returns `a / b (mod n)`, or None if `b` is not invertible modulo `n`. 81 | fn u256_div_mod_n(a: u256, b: NonZero, n: NonZero) -> Option { 82 | Option::Some(u256_mul_mod_n(a, inv_mod(b, n)?, n)) 83 | } 84 | 85 | /// Returns `a * b (mod n)`. 86 | fn u256_mul_mod_n(a: u256, b: u256, n: NonZero) -> u256 { 87 | let (_, r) = u512_safe_div_rem_by_u256(u256_wide_mul(a, b), n); 88 | r 89 | } 90 | 91 | // === Oneable === 92 | 93 | trait Oneable { 94 | /// Returns the multiplicative identity element of Self, 1. 95 | fn one() -> T; 96 | /// Returns whether self is equal to 1, the multiplicative identity element. 97 | fn is_one(self: T) -> bool; 98 | /// Returns whether self is not equal to 1, the multiplicative identity element. 99 | fn is_non_one(self: T) -> bool; 100 | } 101 | 102 | impl U8Oneable of Oneable { 103 | fn one() -> u8 { 104 | 1 105 | } 106 | #[inline(always)] 107 | fn is_one(self: u8) -> bool { 108 | self == U8Oneable::one() 109 | } 110 | #[inline(always)] 111 | fn is_non_one(self: u8) -> bool { 112 | self != U8Oneable::one() 113 | } 114 | } 115 | 116 | impl U16Oneable of Oneable { 117 | fn one() -> u16 { 118 | 1 119 | } 120 | #[inline(always)] 121 | fn is_one(self: u16) -> bool { 122 | self == U16Oneable::one() 123 | } 124 | #[inline(always)] 125 | fn is_non_one(self: u16) -> bool { 126 | self != U16Oneable::one() 127 | } 128 | } 129 | 130 | impl U32Oneable of Oneable { 131 | fn one() -> u32 { 132 | 1 133 | } 134 | #[inline(always)] 135 | fn is_one(self: u32) -> bool { 136 | self == U32Oneable::one() 137 | } 138 | #[inline(always)] 139 | fn is_non_one(self: u32) -> bool { 140 | self != U32Oneable::one() 141 | } 142 | } 143 | 144 | impl U64Oneable of Oneable { 145 | fn one() -> u64 { 146 | 1 147 | } 148 | #[inline(always)] 149 | fn is_one(self: u64) -> bool { 150 | self == U64Oneable::one() 151 | } 152 | #[inline(always)] 153 | fn is_non_one(self: u64) -> bool { 154 | self != U64Oneable::one() 155 | } 156 | } 157 | 158 | impl U128Oneable of Oneable { 159 | fn one() -> u128 { 160 | 1 161 | } 162 | #[inline(always)] 163 | fn is_one(self: u128) -> bool { 164 | self == U128Oneable::one() 165 | } 166 | #[inline(always)] 167 | fn is_non_one(self: u128) -> bool { 168 | self != U128Oneable::one() 169 | } 170 | } 171 | 172 | impl U256Oneable of Oneable { 173 | fn one() -> u256 { 174 | 1 175 | } 176 | #[inline(always)] 177 | fn is_one(self: u256) -> bool { 178 | self == U256Oneable::one() 179 | } 180 | #[inline(always)] 181 | fn is_non_one(self: u256) -> bool { 182 | self != U256Oneable::one() 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /cairo/corelib/src/nullable.cairo: -------------------------------------------------------------------------------- 1 | use box::BoxTrait; 2 | use traits::Default; 3 | use traits::Felt252DictValue; 4 | 5 | #[derive(Copy, Drop)] 6 | extern type Nullable; 7 | 8 | enum FromNullableResult { 9 | Null, 10 | NotNull: Box, 11 | } 12 | 13 | extern fn null() -> Nullable nopanic; 14 | extern fn nullable_from_box(value: Box) -> Nullable nopanic; 15 | extern fn match_nullable(value: Nullable) -> FromNullableResult nopanic; 16 | 17 | trait NullableTrait { 18 | fn deref(self: Nullable) -> T; 19 | fn new(value: T) -> Nullable; 20 | } 21 | 22 | impl NullableImpl of NullableTrait { 23 | fn deref(self: Nullable) -> T { 24 | match match_nullable(self) { 25 | FromNullableResult::Null => panic_with_felt252('Attempted to deref null value'), 26 | FromNullableResult::NotNull(value) => value.unbox(), 27 | } 28 | } 29 | fn new(value: T) -> Nullable { 30 | nullable_from_box(BoxTrait::new(value)) 31 | } 32 | } 33 | 34 | impl NullableDefault of Default> { 35 | #[inline(always)] 36 | fn default() -> Nullable nopanic { 37 | null() 38 | } 39 | } 40 | 41 | impl NullableFelt252DictValue of Felt252DictValue> { 42 | #[inline(always)] 43 | fn zero_default() -> Nullable nopanic { 44 | null() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cairo/corelib/src/option.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | use serde::Serde; 3 | use array::SpanTrait; 4 | 5 | #[derive(Copy, Drop, Serde, PartialEq)] 6 | enum Option { 7 | Some: T, 8 | None, 9 | } 10 | 11 | trait OptionTrait { 12 | /// If `val` is `Option::Some(x)`, returns `x`. Otherwise, panics with `err`. 13 | fn expect(self: Option, err: felt252) -> T; 14 | /// If `val` is `Option::Some(x)`, returns `x`. Otherwise, panics. 15 | fn unwrap(self: Option) -> T; 16 | /// Returns `true` if the `Option` is `Option::Some`. 17 | fn is_some(self: @Option) -> bool; 18 | /// Returns `true` if the `Option` is `Option::None`. 19 | fn is_none(self: @Option) -> bool; 20 | } 21 | impl OptionTraitImpl of OptionTrait { 22 | #[inline(always)] 23 | fn expect(self: Option, err: felt252) -> T { 24 | match self { 25 | Option::Some(x) => x, 26 | Option::None => panic_with_felt252(err), 27 | } 28 | } 29 | #[inline(always)] 30 | fn unwrap(self: Option) -> T { 31 | self.expect('Option::unwrap failed.') 32 | } 33 | #[inline(always)] 34 | fn is_some(self: @Option) -> bool { 35 | match self { 36 | Option::Some(_) => true, 37 | Option::None => false, 38 | } 39 | } 40 | #[inline(always)] 41 | fn is_none(self: @Option) -> bool { 42 | match self { 43 | Option::Some(_) => false, 44 | Option::None => true, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cairo/corelib/src/panics.cairo: -------------------------------------------------------------------------------- 1 | use array::Array; 2 | 3 | struct Panic {} 4 | 5 | enum PanicResult { 6 | Ok: T, 7 | Err: (Panic, Array), 8 | } 9 | 10 | extern fn panic(data: Array) -> never; 11 | -------------------------------------------------------------------------------- /cairo/corelib/src/pedersen.cairo: -------------------------------------------------------------------------------- 1 | extern type Pedersen; 2 | 3 | extern fn pedersen(a: felt252, b: felt252) -> felt252 implicits(Pedersen) nopanic; 4 | 5 | 6 | /// State for Pedersen hash. 7 | #[derive(Copy, Drop)] 8 | struct HashState { 9 | state: felt252, 10 | } 11 | 12 | #[generate_trait] 13 | impl PedersenImpl of PedersenTrait { 14 | /// Creates a state from a base value. 15 | #[inline(always)] 16 | fn new(base: felt252) -> HashState { 17 | HashState { state: base } 18 | } 19 | } 20 | 21 | impl HashStateImpl of hash::HashStateTrait { 22 | #[inline(always)] 23 | fn update(self: HashState, value: felt252) -> HashState { 24 | HashState { state: pedersen(self.state, value) } 25 | } 26 | 27 | #[inline(always)] 28 | fn finalize(self: HashState) -> felt252 { 29 | self.state 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /cairo/corelib/src/poseidon.cairo: -------------------------------------------------------------------------------- 1 | use array::Span; 2 | use array::SpanTrait; 3 | use option::OptionTrait; 4 | use hash::HashStateTrait; 5 | 6 | extern type Poseidon; 7 | 8 | extern fn hades_permutation( 9 | s0: felt252, s1: felt252, s2: felt252 10 | ) -> (felt252, felt252, felt252) implicits(Poseidon) nopanic; 11 | 12 | /// State for Poseidon hash. 13 | #[derive(Copy, Drop)] 14 | struct HashState { 15 | s0: felt252, 16 | s1: felt252, 17 | s2: felt252, 18 | odd: bool, 19 | } 20 | 21 | #[generate_trait] 22 | impl PoseidonImpl of PoseidonTrait { 23 | /// Creates an initial state. 24 | #[inline(always)] 25 | fn new() -> HashState { 26 | HashState { s0: 0, s1: 0, s2: 0, odd: false } 27 | } 28 | } 29 | 30 | impl HashStateDefault of Default { 31 | fn default() -> HashState { 32 | PoseidonTrait::new() 33 | } 34 | } 35 | 36 | impl HashStateImpl of HashStateTrait { 37 | #[inline(always)] 38 | fn update(self: HashState, value: felt252) -> HashState { 39 | if self.odd { 40 | let (s0, s1, s2) = hades_permutation(self.s0, self.s1 + value, self.s2); 41 | HashState { s0, s1, s2, odd: false } 42 | } else { 43 | HashState { s0: self.s0 + value, s1: self.s1, s2: self.s2, odd: true } 44 | } 45 | } 46 | 47 | #[inline(always)] 48 | fn finalize(self: HashState) -> felt252 { 49 | if self.odd { 50 | let (r, _, _) = hades_permutation(self.s0, self.s1 + 1, self.s2); 51 | r 52 | } else { 53 | let (r, _, _) = hades_permutation(self.s0 + 1, self.s1, self.s2); 54 | r 55 | } 56 | } 57 | } 58 | 59 | /// Computes the Poseidon hash on the given input. 60 | /// 61 | /// Applies the sponge construction to digest many elements. 62 | /// To distinguish between use cases, the capacity element is initialized to 0. 63 | /// To distinguish between different input sizes always pads with 1, and possibly with another 0 to 64 | /// complete to an even-sized input. 65 | fn poseidon_hash_span(mut span: Span) -> felt252 { 66 | _poseidon_hash_span_inner(get_builtin_costs(), (0, 0, 0), ref span) 67 | } 68 | 69 | /// Helper function for poseidon_hash_span. 70 | fn _poseidon_hash_span_inner( 71 | builtin_costs: gas::BuiltinCosts, state: (felt252, felt252, felt252), ref span: Span 72 | ) -> felt252 { 73 | let (s0, s1, s2) = state; 74 | let x = *match span.pop_front() { 75 | Option::Some(x) => x, 76 | Option::None => { 77 | return HashState { s0, s1, s2, odd: false }.finalize(); 78 | }, 79 | }; 80 | let y = *match span.pop_front() { 81 | Option::Some(y) => y, 82 | Option::None => { 83 | return HashState { s0: s0 + x, s1, s2, odd: true }.finalize(); 84 | }, 85 | }; 86 | let next_state = hades_permutation(s0 + x, s1 + y, s2); 87 | gas::withdraw_gas_all(builtin_costs).expect('Out of gas'); 88 | _poseidon_hash_span_inner(builtin_costs, next_state, ref span) 89 | } 90 | -------------------------------------------------------------------------------- /cairo/corelib/src/result.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | use serde::Serde; 3 | use array::SpanTrait; 4 | 5 | #[derive(Copy, Drop, Serde, PartialEq)] 6 | enum Result { 7 | Ok: T, 8 | Err: E, 9 | } 10 | 11 | #[generate_trait] 12 | impl ResultTraitImpl of ResultTrait { 13 | /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics with `err`. 14 | fn expect>(self: Result, err: felt252) -> T { 15 | match self { 16 | Result::Ok(x) => x, 17 | Result::Err(_) => panic_with_felt252(err), 18 | } 19 | } 20 | /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics. 21 | fn unwrap>(self: Result) -> T { 22 | self.expect('Result::unwrap failed.') 23 | } 24 | /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics with `err`. 25 | fn expect_err>(self: Result, err: felt252) -> E { 26 | match self { 27 | Result::Ok(_) => panic_with_felt252(err), 28 | Result::Err(x) => x, 29 | } 30 | } 31 | /// If `val` is `Result::Err(x)`, returns `x`. Otherwise, panics. 32 | fn unwrap_err>(self: Result) -> E { 33 | self.expect_err('Result::unwrap_err failed.') 34 | } 35 | /// Returns `true` if the `Result` is `Result::Ok`. 36 | #[inline] 37 | fn is_ok(self: @Result) -> bool { 38 | match self { 39 | Result::Ok(_) => true, 40 | Result::Err(_) => false, 41 | } 42 | } 43 | /// Returns `true` if the `Result` is `Result::Err`. 44 | #[inline] 45 | fn is_err(self: @Result) -> bool { 46 | match self { 47 | Result::Ok(_) => false, 48 | Result::Err(_) => true, 49 | } 50 | } 51 | /// Returns `true` if the `Result` is `Result::Ok`, and consumes the value. 52 | #[inline] 53 | fn into_is_err, impl EDrop: Drop>(self: Result) -> bool { 54 | match self { 55 | Result::Ok(_) => false, 56 | Result::Err(_) => true, 57 | } 58 | } 59 | /// Returns `true` if the `Result` is `Result::Err`, and consumes the value. 60 | #[inline] 61 | fn into_is_ok, impl EDrop: Drop>(self: Result) -> bool { 62 | match self { 63 | Result::Ok(_) => true, 64 | Result::Err(_) => false, 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /cairo/corelib/src/serde.cairo: -------------------------------------------------------------------------------- 1 | use array::ArrayTrait; 2 | use array::SpanTrait; 3 | use traits::Into; 4 | use traits::TryInto; 5 | 6 | trait Serde { 7 | fn serialize(self: @T, ref output: Array); 8 | fn deserialize(ref serialized: Span) -> Option; 9 | } 10 | 11 | impl TupleSize0Serde of Serde<()> { 12 | fn serialize(self: @(), ref output: Array) {} 13 | fn deserialize(ref serialized: Span) -> Option<()> { 14 | Option::Some(()) 15 | } 16 | } 17 | 18 | impl TupleSize1Serde> of Serde<(E0,)> { 19 | fn serialize(self: @(E0,), ref output: Array) { 20 | let (e0,) = self; 21 | e0.serialize(ref output) 22 | } 23 | fn deserialize(ref serialized: Span) -> Option<(E0,)> { 24 | Option::Some((E0Serde::deserialize(ref serialized)?,)) 25 | } 26 | } 27 | 28 | impl TupleSize2Serde< 29 | E0, 30 | E1, 31 | impl E0Serde: Serde, 32 | impl E0Drop: Drop, 33 | impl E1Serde: Serde, 34 | impl E0Drop: Drop 35 | > of Serde<(E0, E1)> { 36 | fn serialize(self: @(E0, E1), ref output: Array) { 37 | let (e0, e1) = self; 38 | e0.serialize(ref output); 39 | e1.serialize(ref output) 40 | } 41 | fn deserialize(ref serialized: Span) -> Option<(E0, E1)> { 42 | Option::Some((E0Serde::deserialize(ref serialized)?, E1Serde::deserialize(ref serialized)?)) 43 | } 44 | } 45 | 46 | impl TupleSize3Serde< 47 | E0, 48 | E1, 49 | E2, 50 | impl E0Serde: Serde, 51 | impl E0Drop: Drop, 52 | impl E1Serde: Serde, 53 | impl E1Drop: Drop, 54 | impl E2Serde: Serde, 55 | impl E2Drop: Drop 56 | > of Serde<(E0, E1, E2)> { 57 | fn serialize(self: @(E0, E1, E2), ref output: Array) { 58 | let (e0, e1, e2) = self; 59 | e0.serialize(ref output); 60 | e1.serialize(ref output); 61 | e2.serialize(ref output) 62 | } 63 | fn deserialize(ref serialized: Span) -> Option<(E0, E1, E2)> { 64 | Option::Some( 65 | ( 66 | E0Serde::deserialize(ref serialized)?, 67 | E1Serde::deserialize(ref serialized)?, 68 | E2Serde::deserialize(ref serialized)? 69 | ) 70 | ) 71 | } 72 | } 73 | 74 | impl TupleSize4Serde< 75 | E0, 76 | E1, 77 | E2, 78 | E3, 79 | impl E0Serde: Serde, 80 | impl E0Drop: Drop, 81 | impl E1Serde: Serde, 82 | impl E1Drop: Drop, 83 | impl E2Serde: Serde, 84 | impl E2Drop: Drop, 85 | impl E3Serde: Serde, 86 | impl E3Drop: Drop 87 | > of Serde<(E0, E1, E2, E3)> { 88 | fn serialize(self: @(E0, E1, E2, E3), ref output: Array) { 89 | let (e0, e1, e2, e3) = self; 90 | e0.serialize(ref output); 91 | e1.serialize(ref output); 92 | e2.serialize(ref output); 93 | e3.serialize(ref output) 94 | } 95 | fn deserialize(ref serialized: Span) -> Option<(E0, E1, E2, E3)> { 96 | Option::Some( 97 | ( 98 | E0Serde::deserialize(ref serialized)?, 99 | E1Serde::deserialize(ref serialized)?, 100 | E2Serde::deserialize(ref serialized)?, 101 | E3Serde::deserialize(ref serialized)? 102 | ) 103 | ) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet.cairo: -------------------------------------------------------------------------------- 1 | use box::Box; 2 | use option::OptionTrait; 3 | use array::Span; 4 | use traits::Into; 5 | use traits::TryInto; 6 | use zeroable::Zeroable; 7 | 8 | // Re-imports 9 | // Store 10 | mod storage_access; 11 | use storage_access::{ 12 | Store, StorePacking, StorageAddress, StorageBaseAddress, storage_base_address_const, 13 | storage_base_address_from_felt252, storage_address_from_base, 14 | storage_address_from_base_and_offset, storage_address_to_felt252, 15 | storage_address_try_from_felt252 16 | }; 17 | 18 | // Module containing all the extern declaration of the syscalls. 19 | mod syscalls; 20 | use syscalls::{ 21 | call_contract_syscall, deploy_syscall, emit_event_syscall, get_block_hash_syscall, 22 | get_execution_info_syscall, library_call_syscall, send_message_to_l1_syscall, 23 | storage_read_syscall, storage_write_syscall, replace_class_syscall, keccak_syscall 24 | }; 25 | 26 | // secp256 27 | mod secp256_trait; 28 | mod secp256k1; 29 | mod secp256r1; 30 | 31 | // ContractAddress 32 | mod contract_address; 33 | use contract_address::{ 34 | ContractAddress, ContractAddressIntoFelt252, Felt252TryIntoContractAddress, 35 | contract_address_const, contract_address_to_felt252, contract_address_try_from_felt252 36 | }; 37 | 38 | // EthAddress 39 | mod eth_address; 40 | use eth_address::{ 41 | EthAddress, EthAddressIntoFelt252, EthAddressSerde, EthAddressZeroable, Felt252TryIntoEthAddress 42 | }; 43 | 44 | // ClassHash 45 | mod class_hash; 46 | use class_hash::{ 47 | ClassHash, ClassHashIntoFelt252, Felt252TryIntoClassHash, class_hash_const, 48 | class_hash_to_felt252, class_hash_try_from_felt252 49 | }; 50 | 51 | mod info; 52 | use info::{ 53 | ExecutionInfo, BlockInfo, TxInfo, get_execution_info, get_caller_address, get_contract_address, 54 | get_block_info, get_tx_info, get_block_timestamp 55 | }; 56 | 57 | mod event; 58 | use event::Event; 59 | 60 | mod account; 61 | use account::AccountContract; 62 | 63 | extern type System; 64 | 65 | // An Helper function to force the inclusion of `System` in the list of implicits. 66 | fn use_system_implicit() implicits(System) {} 67 | 68 | /// The result type for a syscall. 69 | type SyscallResult = Result>; 70 | 71 | trait SyscallResultTrait { 72 | /// If `val` is `Result::Ok(x)`, returns `x`. Otherwise, panics with the revert reason. 73 | fn unwrap_syscall(self: SyscallResult) -> T; 74 | } 75 | impl SyscallResultTraitImpl of SyscallResultTrait { 76 | fn unwrap_syscall(self: SyscallResult) -> T { 77 | match self { 78 | Result::Ok(x) => x, 79 | Result::Err(revert_reason) => panic(revert_reason), 80 | } 81 | } 82 | } 83 | 84 | /// The expected return value of the `__validate*__` functions of an accounted contract. 85 | const VALIDATED: felt252 = 'VALID'; 86 | 87 | // Module for starknet testing only. 88 | mod testing; 89 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/account.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | 3 | #[derive(Drop, Serde)] 4 | struct Call { 5 | to: ContractAddress, 6 | selector: felt252, 7 | calldata: Array 8 | } 9 | 10 | #[starknet::interface] 11 | trait AccountContract { 12 | fn __validate_declare__(self: @TContractState, class_hash: felt252) -> felt252; 13 | fn __validate__(ref self: TContractState, calls: Array) -> felt252; 14 | fn __execute__(ref self: TContractState, calls: Array) -> Array>; 15 | } 16 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/class_hash.cairo: -------------------------------------------------------------------------------- 1 | use zeroable::Zeroable; 2 | use serde::Serde; 3 | 4 | #[derive(Copy, Drop)] 5 | extern type ClassHash; 6 | 7 | 8 | extern fn class_hash_const() -> ClassHash nopanic; 9 | extern fn class_hash_to_felt252(address: ClassHash) -> felt252 nopanic; 10 | 11 | extern fn class_hash_try_from_felt252( 12 | address: felt252 13 | ) -> Option implicits(RangeCheck) nopanic; 14 | 15 | impl Felt252TryIntoClassHash of TryInto { 16 | fn try_into(self: felt252) -> Option { 17 | class_hash_try_from_felt252(self) 18 | } 19 | } 20 | impl ClassHashIntoFelt252 of Into { 21 | fn into(self: ClassHash) -> felt252 { 22 | class_hash_to_felt252(self) 23 | } 24 | } 25 | 26 | impl ClassHashZeroable of Zeroable { 27 | fn zero() -> ClassHash { 28 | class_hash_const::<0>() 29 | } 30 | #[inline(always)] 31 | fn is_zero(self: ClassHash) -> bool { 32 | class_hash_to_felt252(self).is_zero() 33 | } 34 | #[inline(always)] 35 | fn is_non_zero(self: ClassHash) -> bool { 36 | !self.is_zero() 37 | } 38 | } 39 | 40 | impl ClassHashSerde of serde::Serde { 41 | fn serialize(self: @ClassHash, ref output: Array) { 42 | class_hash_to_felt252(*self).serialize(ref output); 43 | } 44 | fn deserialize(ref serialized: Span) -> Option { 45 | Option::Some( 46 | class_hash_try_from_felt252(serde::Serde::::deserialize(ref serialized)?)? 47 | ) 48 | } 49 | } 50 | 51 | impl ClassHashPartialEq of PartialEq { 52 | #[inline(always)] 53 | fn eq(lhs: @ClassHash, rhs: @ClassHash) -> bool { 54 | class_hash_to_felt252(*lhs) == class_hash_to_felt252(*rhs) 55 | } 56 | #[inline(always)] 57 | fn ne(lhs: @ClassHash, rhs: @ClassHash) -> bool { 58 | !(lhs == rhs) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/contract_address.cairo: -------------------------------------------------------------------------------- 1 | use zeroable::Zeroable; 2 | use serde::Serde; 3 | 4 | #[derive(Copy, Drop)] 5 | extern type ContractAddress; 6 | 7 | 8 | extern fn contract_address_const() -> ContractAddress nopanic; 9 | extern fn contract_address_to_felt252(address: ContractAddress) -> felt252 nopanic; 10 | 11 | extern fn contract_address_try_from_felt252( 12 | address: felt252 13 | ) -> Option implicits(RangeCheck) nopanic; 14 | 15 | impl Felt252TryIntoContractAddress of TryInto { 16 | fn try_into(self: felt252) -> Option { 17 | contract_address_try_from_felt252(self) 18 | } 19 | } 20 | impl ContractAddressIntoFelt252 of Into { 21 | fn into(self: ContractAddress) -> felt252 { 22 | contract_address_to_felt252(self) 23 | } 24 | } 25 | 26 | impl ContractAddressZeroable of Zeroable { 27 | fn zero() -> ContractAddress { 28 | contract_address_const::<0>() 29 | } 30 | #[inline(always)] 31 | fn is_zero(self: ContractAddress) -> bool { 32 | contract_address_to_felt252(self).is_zero() 33 | } 34 | #[inline(always)] 35 | fn is_non_zero(self: ContractAddress) -> bool { 36 | !self.is_zero() 37 | } 38 | } 39 | 40 | impl ContractAddressSerde of serde::Serde { 41 | fn serialize(self: @ContractAddress, ref output: Array) { 42 | contract_address_to_felt252(*self).serialize(ref output); 43 | } 44 | fn deserialize(ref serialized: Span) -> Option { 45 | Option::Some( 46 | contract_address_try_from_felt252( 47 | serde::Serde::::deserialize(ref serialized)? 48 | )? 49 | ) 50 | } 51 | } 52 | 53 | impl ContractAddressPartialEq of PartialEq { 54 | #[inline(always)] 55 | fn eq(lhs: @ContractAddress, rhs: @ContractAddress) -> bool { 56 | contract_address_to_felt252(*lhs) == contract_address_to_felt252(*rhs) 57 | } 58 | #[inline(always)] 59 | fn ne(lhs: @ContractAddress, rhs: @ContractAddress) -> bool { 60 | !(lhs == rhs) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/eth_address.cairo: -------------------------------------------------------------------------------- 1 | use debug::PrintTrait; 2 | use integer::{u128_safe_divmod, U128TryIntoNonZero, U256TryIntoFelt252}; 3 | use option::{Option, OptionTrait}; 4 | use serde::Serde; 5 | use traits::{Into, TryInto}; 6 | use zeroable::Zeroable; 7 | 8 | // An Ethereum address (160 bits). 9 | #[derive(Copy, Drop, starknet::Store, PartialEq)] 10 | struct EthAddress { 11 | address: felt252, 12 | } 13 | impl Felt252TryIntoEthAddress of TryInto { 14 | fn try_into(self: felt252) -> Option { 15 | let ETH_ADDRESS_BOUND = 0x10000000000000000000000000000000000000000_u256; // 2 ** 160 16 | 17 | if self.into() < ETH_ADDRESS_BOUND { 18 | Option::Some(EthAddress { address: self }) 19 | } else { 20 | Option::None 21 | } 22 | } 23 | } 24 | impl EthAddressIntoFelt252 of Into { 25 | fn into(self: EthAddress) -> felt252 { 26 | self.address 27 | } 28 | } 29 | impl U256IntoEthAddress of Into { 30 | fn into(self: u256) -> EthAddress { 31 | // The Ethereum address is the 20 least significant bytes (=160=128+32 bits) of the value. 32 | let high_32_bits = self.high % 0x100000000_u128; 33 | EthAddress { 34 | address: high_32_bits.into() * 0x100000000000000000000000000000000_felt252 35 | + self.low.into() 36 | } 37 | } 38 | } 39 | impl EthAddressSerde of Serde { 40 | fn serialize(self: @EthAddress, ref output: Array) { 41 | self.address.serialize(ref output); 42 | } 43 | fn deserialize(ref serialized: Span) -> Option { 44 | Serde::::deserialize(ref serialized)?.try_into() 45 | } 46 | } 47 | impl EthAddressZeroable of Zeroable { 48 | fn zero() -> EthAddress { 49 | 0.try_into().unwrap() 50 | } 51 | #[inline(always)] 52 | fn is_zero(self: EthAddress) -> bool { 53 | self.address.is_zero() 54 | } 55 | #[inline(always)] 56 | fn is_non_zero(self: EthAddress) -> bool { 57 | !self.is_zero() 58 | } 59 | } 60 | 61 | impl EthAddressPrintImpl of PrintTrait { 62 | fn print(self: EthAddress) { 63 | self.address.print(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/event.cairo: -------------------------------------------------------------------------------- 1 | trait Event { 2 | fn append_keys_and_data(self: @T, ref keys: Array, ref data: Array); 3 | fn deserialize(ref keys: Span, ref data: Span) -> Option; 4 | } 5 | 6 | trait EventEmitter { 7 | fn emit>(ref self: T, event: S); 8 | } 9 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/info.cairo: -------------------------------------------------------------------------------- 1 | use starknet::{ 2 | SyscallResultTrait, SyscallResult, syscalls::get_execution_info_syscall, 3 | contract_address::ContractAddress 4 | }; 5 | use box::BoxTrait; 6 | 7 | #[derive(Copy, Drop)] 8 | struct ExecutionInfo { 9 | block_info: Box, 10 | tx_info: Box, 11 | caller_address: ContractAddress, 12 | contract_address: ContractAddress, 13 | entry_point_selector: felt252, 14 | } 15 | 16 | #[derive(Copy, Drop, Serde)] 17 | struct BlockInfo { 18 | block_number: u64, 19 | block_timestamp: u64, 20 | sequencer_address: ContractAddress, 21 | } 22 | 23 | #[derive(Copy, Drop, Serde)] 24 | struct TxInfo { 25 | // The version of the transaction. It is fixed (currently, 1) in the OS, and should be 26 | // signed by the account contract. 27 | // This field allows invalidating old transactions, whenever the meaning of the other 28 | // transaction fields is changed (in the OS). 29 | version: felt252, 30 | // The account contract from which this transaction originates. 31 | account_contract_address: ContractAddress, 32 | // The max_fee field of the transaction. 33 | max_fee: u128, 34 | // The signature of the transaction. 35 | signature: Span, 36 | // The hash of the transaction. 37 | transaction_hash: felt252, 38 | // The identifier of the chain. 39 | // This field can be used to prevent replay of testnet transactions on mainnet. 40 | chain_id: felt252, 41 | // The transaction's nonce. 42 | nonce: felt252, 43 | } 44 | 45 | fn get_execution_info() -> Box { 46 | get_execution_info_syscall().unwrap_syscall() 47 | } 48 | 49 | fn get_caller_address() -> ContractAddress { 50 | get_execution_info().unbox().caller_address 51 | } 52 | 53 | fn get_contract_address() -> ContractAddress { 54 | get_execution_info().unbox().contract_address 55 | } 56 | 57 | fn get_block_info() -> Box { 58 | get_execution_info().unbox().block_info 59 | } 60 | 61 | fn get_tx_info() -> Box { 62 | get_execution_info().unbox().tx_info 63 | } 64 | 65 | fn get_block_timestamp() -> u64 { 66 | get_block_info().unbox().block_timestamp 67 | } 68 | 69 | fn get_block_number() -> u64 { 70 | get_block_info().unbox().block_number 71 | } 72 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/secp256k1.cairo: -------------------------------------------------------------------------------- 1 | //! This module contains functions and constructs related to elliptic curve operations on the 2 | //! secp256k1 curve. 3 | 4 | use option::OptionTrait; 5 | use starknet::{ 6 | EthAddress, secp256_trait::{Secp256Trait, Secp256PointTrait}, SyscallResult, SyscallResultTrait 7 | }; 8 | 9 | #[derive(Copy, Drop)] 10 | extern type Secp256k1Point; 11 | 12 | impl Secp256k1Impl of Secp256Trait { 13 | // TODO(yuval): change to constant once u256 constants are supported. 14 | fn get_curve_size() -> u256 { 15 | 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 16 | } 17 | /// Creates the generator point of the secp256k1 curve. 18 | fn get_generator_point() -> Secp256k1Point { 19 | secp256k1_new_syscall( 20 | 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 21 | 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 22 | ) 23 | .unwrap_syscall() 24 | .unwrap() 25 | } 26 | 27 | fn secp256_ec_new_syscall(x: u256, y: u256) -> SyscallResult> { 28 | secp256k1_new_syscall(x, y) 29 | } 30 | fn secp256_ec_get_point_from_x_syscall( 31 | x: u256, y_parity: bool 32 | ) -> SyscallResult> { 33 | secp256k1_get_point_from_x_syscall(x, y_parity) 34 | } 35 | } 36 | 37 | impl Secp256k1PointImpl of Secp256PointTrait { 38 | fn get_coordinates(self: Secp256k1Point) -> SyscallResult<(u256, u256)> { 39 | secp256k1_get_xy_syscall(self) 40 | } 41 | fn add(self: Secp256k1Point, other: Secp256k1Point) -> SyscallResult { 42 | secp256k1_add_syscall(self, other) 43 | } 44 | fn mul(self: Secp256k1Point, scalar: u256) -> SyscallResult { 45 | secp256k1_mul_syscall(self, scalar) 46 | } 47 | } 48 | 49 | /// Creates a secp256k1 EC point from the given x and y coordinates. 50 | /// Returns None if the given coordinates do not correspond to a point on the curve. 51 | extern fn secp256k1_new_syscall( 52 | x: u256, y: u256 53 | ) -> SyscallResult> implicits(GasBuiltin, System) nopanic; 54 | 55 | /// Computes the addition of secp256k1 EC points `p0 + p1`. 56 | extern fn secp256k1_add_syscall( 57 | p0: Secp256k1Point, p1: Secp256k1Point 58 | ) -> SyscallResult implicits(GasBuiltin, System) nopanic; 59 | /// Computes the product of a secp256k1 EC point `p` by the given scalar `scalar`. 60 | extern fn secp256k1_mul_syscall( 61 | p: Secp256k1Point, scalar: u256 62 | ) -> SyscallResult implicits(GasBuiltin, System) nopanic; 63 | 64 | /// Computes the point on the secp256k1 curve that matches the given `x` coordinate, if such exists. 65 | /// Out of the two possible y's, chooses according to `y_parity`. 66 | /// `y_parity` == true means that the y coordinate is odd. 67 | extern fn secp256k1_get_point_from_x_syscall( 68 | x: u256, y_parity: bool 69 | ) -> SyscallResult> implicits(GasBuiltin, System) nopanic; 70 | 71 | /// Returns the coordinates of a point on the secp256k1 curve. 72 | extern fn secp256k1_get_xy_syscall( 73 | p: Secp256k1Point 74 | ) -> SyscallResult<(u256, u256)> implicits(GasBuiltin, System) nopanic; 75 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/secp256r1.cairo: -------------------------------------------------------------------------------- 1 | //! This module contains functions and constructs related to elliptic curve operations on the 2 | //! secp256r1 curve. 3 | 4 | use option::OptionTrait; 5 | use starknet::{ 6 | EthAddress, secp256_trait::{Secp256Trait, Secp256PointTrait}, SyscallResult, SyscallResultTrait 7 | }; 8 | 9 | #[derive(Copy, Drop)] 10 | extern type Secp256r1Point; 11 | 12 | impl Secp256r1Impl of Secp256Trait { 13 | // TODO(yuval): change to constant once u256 constants are supported. 14 | fn get_curve_size() -> u256 { 15 | 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 16 | } 17 | /// Creates the generator point of the secp256r1 curve. 18 | fn get_generator_point() -> Secp256r1Point { 19 | secp256r1_new_syscall( 20 | 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 21 | 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5, 22 | ) 23 | .unwrap_syscall() 24 | .unwrap() 25 | } 26 | 27 | fn secp256_ec_new_syscall(x: u256, y: u256) -> SyscallResult> { 28 | secp256r1_new_syscall(x, y) 29 | } 30 | fn secp256_ec_get_point_from_x_syscall( 31 | x: u256, y_parity: bool 32 | ) -> SyscallResult> { 33 | secp256r1_get_point_from_x_syscall(x, y_parity) 34 | } 35 | } 36 | 37 | impl Secp256r1PointImpl of Secp256PointTrait { 38 | fn get_coordinates(self: Secp256r1Point) -> SyscallResult<(u256, u256)> { 39 | secp256r1_get_xy_syscall(self) 40 | } 41 | fn add(self: Secp256r1Point, other: Secp256r1Point) -> SyscallResult { 42 | secp256r1_add_syscall(self, other) 43 | } 44 | fn mul(self: Secp256r1Point, scalar: u256) -> SyscallResult { 45 | secp256r1_mul_syscall(self, scalar) 46 | } 47 | } 48 | 49 | /// Creates a secp256r1 EC point from the given x and y coordinates. 50 | /// Returns None if the given coordinates do not correspond to a point on the curve. 51 | extern fn secp256r1_new_syscall( 52 | x: u256, y: u256 53 | ) -> SyscallResult> implicits(GasBuiltin, System) nopanic; 54 | 55 | /// Computes the addition of secp256r1 EC points `p0 + p1`. 56 | extern fn secp256r1_add_syscall( 57 | p0: Secp256r1Point, p1: Secp256r1Point 58 | ) -> SyscallResult implicits(GasBuiltin, System) nopanic; 59 | /// Computes the product of a secp256r1 EC point `p` by the given scalar `scalar`. 60 | extern fn secp256r1_mul_syscall( 61 | p: Secp256r1Point, scalar: u256 62 | ) -> SyscallResult implicits(GasBuiltin, System) nopanic; 63 | 64 | /// Computes the point on the secp256r1 curve that matches the given `x` coordinate, if such exists. 65 | /// Out of the two possible y's, chooses according to `y_parity`. 66 | /// `y_parity` == true means that the y coordinate is odd. 67 | extern fn secp256r1_get_point_from_x_syscall( 68 | x: u256, y_parity: bool 69 | ) -> SyscallResult> implicits(GasBuiltin, System) nopanic; 70 | 71 | /// Returns the coordinates of a point on the secp256r1 curve. 72 | extern fn secp256r1_get_xy_syscall( 73 | p: Secp256r1Point 74 | ) -> SyscallResult<(u256, u256)> implicits(GasBuiltin, System) nopanic; 75 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/syscalls.cairo: -------------------------------------------------------------------------------- 1 | use starknet::{ 2 | SyscallResult, storage_access::StorageAddress, class_hash::ClassHash, 3 | contract_address::ContractAddress 4 | }; 5 | 6 | // Calls a given contract. 7 | // `address` - The address of the called contract. 8 | // `entry_point_selector` - A selector for a function within that contract. 9 | // `calldata` - Call arguments. 10 | extern fn call_contract_syscall( 11 | address: ContractAddress, entry_point_selector: felt252, calldata: Span 12 | ) -> SyscallResult> implicits(GasBuiltin, System) nopanic; 13 | 14 | // Deploys a new instance of a previously declared class. 15 | // `class_hash` - The class hash of the contract to be deployed. 16 | // `contract_address_salt` - The salt, an arbitrary value provided by the sender, used in the 17 | // computation of the contract's address. 18 | // `calldata` - Call arguments for the constructor. 19 | // `deploy_from_zero` - Deploy the contract from the zero address. 20 | extern fn deploy_syscall( 21 | class_hash: ClassHash, 22 | contract_address_salt: felt252, 23 | calldata: Span, 24 | deploy_from_zero: bool, 25 | ) -> SyscallResult<(ContractAddress, Span)> implicits(GasBuiltin, System) nopanic; 26 | 27 | // Emits an event. 28 | // `keys` - The keys of the event. 29 | // `data` - The data of the event. 30 | extern fn emit_event_syscall( 31 | keys: Span, data: Span 32 | ) -> SyscallResult<()> implicits(GasBuiltin, System) nopanic; 33 | 34 | // Gets the block hash of the block with the given number. 35 | extern fn get_block_hash_syscall( 36 | block_number: u64 37 | ) -> SyscallResult implicits(GasBuiltin, System) nopanic; 38 | 39 | // Gets information about the current execution. 40 | extern fn get_execution_info_syscall() -> SyscallResult> implicits( 41 | GasBuiltin, System 42 | ) nopanic; 43 | 44 | // Calls the requested function in any previously declared class. 45 | // `class_hash` - The hash of the class you want to use. 46 | // `function_selector` - A selector for a function within that class. 47 | // `calldata` - Call arguments. 48 | extern fn library_call_syscall( 49 | class_hash: ClassHash, function_selector: felt252, calldata: Span 50 | ) -> SyscallResult> implicits(GasBuiltin, System) nopanic; 51 | 52 | // TODO(Ilya): Decide if we limit the type of `to_address`. 53 | // Sends a message to L1. 54 | // `to_address` - The recipient's L1 address. 55 | // `payload` - The content of the message. 56 | extern fn send_message_to_l1_syscall( 57 | to_address: felt252, payload: Span 58 | ) -> SyscallResult<()> implicits(GasBuiltin, System) nopanic; 59 | 60 | // Gets the value of a key in the storage of the calling contract. 61 | // `address_domain` - The domain of the address. Only address_domain 0 is currently supported, 62 | // in the future it will enable access to address spaces with different data availability 63 | // guarantees. 64 | // `address` - The address of the storage key to read. 65 | extern fn storage_read_syscall( 66 | address_domain: u32, address: StorageAddress, 67 | ) -> SyscallResult implicits(GasBuiltin, System) nopanic; 68 | 69 | // Sets the value of a key in the storage of the calling contract. 70 | // `address_domain` - The domain of the address. Only address_domain 0 is currently supported, 71 | // in the future it will enable access to address spaces with different data availability 72 | // guarantees. 73 | // `address` - The address of the storage key to write. 74 | // `value` - The value to write to the key. 75 | extern fn storage_write_syscall( 76 | address_domain: u32, address: StorageAddress, value: felt252 77 | ) -> SyscallResult<()> implicits(GasBuiltin, System) nopanic; 78 | 79 | 80 | // Replaces the class hash of the current contract. 81 | // `class_hash` - The class hash that should replace the current one. 82 | extern fn replace_class_syscall( 83 | class_hash: ClassHash 84 | ) -> SyscallResult<()> implicits(GasBuiltin, System) nopanic; 85 | 86 | 87 | // Computes the keccak of the input. 88 | // The system call does not add any padding and the input needs to be a multiple of 1088 bits 89 | // (== 17 u64 word). 90 | extern fn keccak_syscall( 91 | input: Span 92 | ) -> SyscallResult implicits(GasBuiltin, System) nopanic; 93 | -------------------------------------------------------------------------------- /cairo/corelib/src/starknet/testing.cairo: -------------------------------------------------------------------------------- 1 | use starknet::ContractAddress; 2 | use array::ArrayTrait; 3 | use array::SpanTrait; 4 | use traits::Into; 5 | 6 | // A general cheatcode function used to simplify implementation of Starknet testing functions. 7 | // External users of the cairo crates can also implement their own cheatcodes 8 | // by injecting custom `CairoHintProcessor`. 9 | extern fn cheatcode( 10 | input: Span 11 | ) -> Span implicits() nopanic; 12 | 13 | // Set the block number to the provided value. 14 | fn set_block_number(block_number: u64) { 15 | cheatcode::<'set_block_number'>(array![block_number.into()].span()); 16 | } 17 | 18 | // Set the caller address to the provided value. 19 | fn set_caller_address(address: ContractAddress) { 20 | cheatcode::<'set_caller_address'>(array![address.into()].span()); 21 | } 22 | 23 | // Set the contract address to the provided value. 24 | fn set_contract_address(address: ContractAddress) { 25 | cheatcode::<'set_contract_address'>(array![address.into()].span()); 26 | } 27 | 28 | // Set the sequencer address to the provided value. 29 | fn set_sequencer_address(address: ContractAddress) { 30 | cheatcode::<'set_sequencer_address'>(array![address.into()].span()); 31 | } 32 | 33 | // Set the block timestamp to the provided value. 34 | fn set_block_timestamp(block_timestamp: u64) { 35 | cheatcode::<'set_block_timestamp'>(array![block_timestamp.into()].span()); 36 | } 37 | 38 | // Set the version to the provided value. 39 | fn set_version(version: felt252) { 40 | cheatcode::<'set_version'>(array![version].span()); 41 | } 42 | 43 | // Set the account contract address. 44 | fn set_account_contract_address(address: ContractAddress) { 45 | cheatcode::<'set_account_contract_address'>(array![address.into()].span()); 46 | } 47 | 48 | // Set the max fee. 49 | fn set_max_fee(fee: u128) { 50 | cheatcode::<'set_max_fee'>(array![fee.into()].span()); 51 | } 52 | 53 | // Set the transaction hash. 54 | fn set_transaction_hash(hash: felt252) { 55 | cheatcode::<'set_transaction_hash'>(array![hash].span()); 56 | } 57 | 58 | // Set the chain id. 59 | fn set_chain_id(chain_id: felt252) { 60 | cheatcode::<'set_chain_id'>(array![chain_id].span()); 61 | } 62 | 63 | // Set the nonce. 64 | fn set_nonce(nonce: felt252) { 65 | cheatcode::<'set_nonce'>(array![nonce].span()); 66 | } 67 | 68 | // Set the signature. 69 | fn set_signature(signature: Span) { 70 | cheatcode::<'set_signature'>(signature); 71 | } 72 | 73 | // Pop the earliest unpopped logged event for the contract. 74 | fn pop_log_raw(address: ContractAddress) -> Option<(Span, Span)> { 75 | let mut log = cheatcode::<'pop_log'>(array![address.into()].span()); 76 | Option::Some((serde::Serde::deserialize(ref log)?, serde::Serde::deserialize(ref log)?,)) 77 | } 78 | 79 | // Pop the earliest unpopped logged event for the contract as the requested type. 80 | fn pop_log>(address: ContractAddress) -> Option { 81 | let (mut keys, mut data) = pop_log_raw(address)?; 82 | starknet::Event::deserialize(ref keys, ref data) 83 | } 84 | 85 | // TODO(Ilya): Decide if we limit the type of `to_address`. 86 | // Pop the earliest unpopped l2 to l1 message for the contract. 87 | fn pop_l2_to_l1_message(address: ContractAddress) -> Option<(felt252, Span)> { 88 | let mut l2_to_l1_message = cheatcode::<'pop_l2_to_l1_message'>(array![address.into()].span()); 89 | Option::Some( 90 | ( 91 | serde::Serde::deserialize(ref l2_to_l1_message)?, 92 | serde::Serde::deserialize(ref l2_to_l1_message)?, 93 | ) 94 | ) 95 | } 96 | -------------------------------------------------------------------------------- /cairo/corelib/src/test.cairo: -------------------------------------------------------------------------------- 1 | mod array_test; 2 | mod bool_test; 3 | mod box_test; 4 | mod bytes31_test; 5 | mod cmp_test; 6 | mod dict_test; 7 | mod ec_test; 8 | mod felt_test; 9 | mod hash_test; 10 | mod integer_test; 11 | mod keccak_test; 12 | mod math_test; 13 | mod plugins_test; 14 | mod secp256k1_test; 15 | mod secp256r1_test; 16 | mod test_utils; 17 | mod testing_test; 18 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/array_test.cairo: -------------------------------------------------------------------------------- 1 | use test::test_utils::{assert_eq, assert_ne}; 2 | 3 | #[test] 4 | fn test_array() { 5 | let arr = array![10, 11, 12]; 6 | assert_eq(arr[0], @10, 'array[0] != 10'); 7 | assert_eq(arr[1], @11, 'array[1] != 11'); 8 | assert_eq(arr[2], @12, 'array[2] != 12'); 9 | } 10 | 11 | #[test] 12 | #[should_panic] 13 | fn test_array_out_of_bound_1() { 14 | let arr = array![10, 11, 12]; 15 | arr[3]; 16 | } 17 | 18 | #[test] 19 | #[should_panic] 20 | fn test_array_out_of_bound_2() { 21 | let arr = array![10, 11, 12]; 22 | arr[11]; 23 | } 24 | 25 | #[test] 26 | #[available_gas(100000)] 27 | fn test_array_clone() { 28 | let felt252_snap_array: @Array = @array![10, 11, 12]; 29 | let felt252_snap_array_clone = felt252_snap_array.clone(); 30 | assert_eq(@felt252_snap_array_clone.len(), @3, 'array len != 3'); 31 | assert_eq(felt252_snap_array_clone[0], @10, 'array[0] != 10'); 32 | assert_eq(felt252_snap_array_clone[1], @11, 'array[1] != 11'); 33 | assert_eq(felt252_snap_array_clone[2], @12, 'array[2] != 12'); 34 | } 35 | 36 | #[test] 37 | fn test_span() { 38 | let mut span = array![10, 11, 12].span(); 39 | assert_eq(@span.len(), @3, 'Unexpected span length.'); 40 | assert_eq(span.get(0).unwrap().unbox(), @10, 'Unexpected element'); 41 | assert_eq(span.pop_front().unwrap(), @10, 'Unexpected element'); 42 | assert_eq(@span.len(), @2, 'Unexpected span length.'); 43 | assert_eq(span[1], @12, 'Unexpected element'); 44 | assert_eq(span.pop_back().unwrap(), @12, 'Unexpected element'); 45 | assert_eq(@span.len(), @1, 'Unexpected span length.'); 46 | } 47 | 48 | #[test] 49 | fn test_slice() { 50 | let span = array![10, 11, 12].span(); 51 | assert_eq(@span.slice(0, 3).len(), @3, 'Unexpected span length.'); 52 | assert_eq(span.slice(0, 3)[0], @10, 'Unexpected Element.'); 53 | assert_eq(@span.slice(0, 2).len(), @2, 'Unexpected span length.'); 54 | assert_eq(span.slice(0, 2)[0], @10, 'Unexpected Element.'); 55 | assert_eq(@span.slice(0, 1).len(), @1, 'Unexpected span length.'); 56 | assert_eq(span.slice(0, 1)[0], @10, 'Unexpected Element.'); 57 | assert_eq(@span.slice(0, 0).len(), @0, 'Unexpected span length.'); 58 | assert_eq(@span.slice(1, 2).len(), @2, 'Unexpected span length.'); 59 | assert_eq(span.slice(1, 2)[0], @11, 'Unexpected Element.'); 60 | assert_eq(@span.slice(1, 1).len(), @1, 'Unexpected span length.'); 61 | assert_eq(span.slice(1, 1)[0], @11, 'Unexpected Element.'); 62 | assert_eq(@span.slice(1, 0).len(), @0, 'Unexpected span length.'); 63 | } 64 | 65 | #[test] 66 | #[should_panic] 67 | fn test_slice_out_of_bound_1() { 68 | array![10, 11, 12].span().slice(3, 1); 69 | } 70 | 71 | #[test] 72 | #[should_panic] 73 | fn test_slice_out_of_bound_2() { 74 | array![10, 11, 12].span().slice(0, 4); 75 | } 76 | 77 | #[test] 78 | #[available_gas(10000000)] 79 | fn test_equality() { 80 | let arr1 = array![]; 81 | let arr2 = array![10, 11, 12]; 82 | let arr3 = array![10, 11, 13]; 83 | let arr4 = array![10, 11]; 84 | let arr5 = array![10, 11, 12, 13]; 85 | 86 | assert(arr1 == arr1, 'arr1 != arr1'); 87 | assert(arr2 == arr2, 'arr2 != arr2'); 88 | assert(arr3 == arr3, 'arr3 != arr3'); 89 | assert(arr4 == arr4, 'arr4 != arr4'); 90 | assert(arr5 == arr5, 'arr5 != arr5'); 91 | 92 | assert(arr1 != arr2, 'arr1 == arr2'); 93 | assert(arr1 != arr3, 'arr1 == arr3'); 94 | assert(arr1 != arr4, 'arr1 == arr4'); 95 | assert(arr1 != arr5, 'arr1 == arr5'); 96 | assert(arr2 != arr3, 'arr2 == arr3'); 97 | assert(arr2 != arr4, 'arr2 == arr4'); 98 | assert(arr2 != arr5, 'arr2 == arr5'); 99 | assert(arr3 != arr4, 'arr3 == arr4'); 100 | assert(arr3 != arr5, 'arr3 == arr5'); 101 | assert(arr4 != arr5, 'arr4 == arr5'); 102 | } 103 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/bool_test.cairo: -------------------------------------------------------------------------------- 1 | use test::test_utils::{assert_eq, assert_ne}; 2 | 3 | #[test] 4 | fn test_bool_operators() { 5 | assert_eq(@true, @true, 't != t'); 6 | assert_eq(@false, @false, 'f != f'); 7 | assert_eq(@!true, @false, '!t != f'); 8 | assert_eq(@!false, @true, '!f != t'); 9 | assert_ne(@true, @false, 't == f'); 10 | assert_ne(@false, @true, 'f == t'); 11 | assert(!(false & false), '!(f & f)'); 12 | assert(!(true & false), '!(t & f)'); 13 | assert(!(false & true), '!(f & t)'); 14 | assert(true & true, 't & t'); 15 | assert(!(false | false), '!(f | f)'); 16 | assert(true | false, 't | f'); 17 | assert(false | true, 'f | t'); 18 | assert(true | true, 't | t'); 19 | assert(!(false ^ false), '!(f ^ f)'); 20 | assert(true ^ false, 't ^ f'); 21 | assert(false ^ true, 'f ^ t'); 22 | assert(!(true ^ true), '!(t ^ t)'); 23 | } 24 | 25 | #[test] 26 | fn test_bool_conversion() { 27 | assert_eq(@false.into(), @0, 'f.into() != 0'); 28 | assert_eq(@true.into(), @1, 'f.into() != 1'); 29 | } 30 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/box_test.cairo: -------------------------------------------------------------------------------- 1 | use test::test_utils::{assert_eq, assert_ne}; 2 | 3 | #[test] 4 | fn test_box_unbox_felt252s() { 5 | let x = 10; 6 | let boxed_x = BoxTrait::new(x); 7 | let y = 11; 8 | let boxed_y = BoxTrait::new(y); 9 | assert_eq(@boxed_x.unbox(), @10, 'x != 10'); 10 | assert_eq(@boxed_y.unbox(), @11, 'y != 11'); 11 | } 12 | 13 | // Test objects of size>1. 14 | #[test] 15 | fn test_box_unbox_u256() { 16 | let x = u256 { low: 1, high: 0 }; 17 | let boxed_x = BoxTrait::new(x); 18 | let y = u256 { low: 1, high: 1 }; 19 | let boxed_y = BoxTrait::new(y); 20 | assert_eq(@boxed_x.unbox(), @x, 'unbox u256 x'); 21 | assert_eq(@boxed_y.unbox(), @y, 'unbox u256 y'); 22 | } 23 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/dict_test.cairo: -------------------------------------------------------------------------------- 1 | use dict::Felt252DictEntryTrait; 2 | use test::test_utils::{assert_eq, assert_ne}; 3 | 4 | #[test] 5 | fn test_dict_new() -> Felt252Dict { 6 | Default::default() 7 | } 8 | 9 | #[test] 10 | fn test_dict_squash_empty() { 11 | let mut dict: Felt252Dict = Default::default(); 12 | let squashed_dict = dict.squash(); 13 | } 14 | 15 | #[test] 16 | fn test_dict_default_val() { 17 | let mut dict: Felt252Dict = Default::default(); 18 | let default_val = dict.get(0); 19 | assert_eq(@default_val, @0, 'default_val == 0'); 20 | } 21 | 22 | #[test] 23 | fn test_dict_write_read() { 24 | let mut dict = Default::default(); 25 | dict.insert(10, 110); 26 | dict.insert(11, 111); 27 | let val10 = dict[10]; 28 | let val11 = dict[11]; 29 | let val12 = dict[12]; 30 | assert_eq(@val10, @110, 'dict[10] == 110'); 31 | assert_eq(@val11, @111, 'dict[11] == 111'); 32 | assert_eq(@val12, @0, 'default_val == 0'); 33 | } 34 | 35 | #[test] 36 | fn test_dict_entry() { 37 | let mut dict = Default::default(); 38 | dict.insert(10, 110); 39 | let (entry, value) = dict.entry(10); 40 | assert_eq(@value, @110, 'dict[10] == 110'); 41 | let mut dict = entry.finalize(11); 42 | assert_eq(@dict[10], @11, 'dict[10] == 11'); 43 | } 44 | 45 | #[test] 46 | fn test_dict_entry_uninitialized() { 47 | let mut dict = Default::default(); 48 | let (entry, value) = dict.entry(10); 49 | assert_eq(@value, @0_felt252, 'dict[10] == 0'); 50 | let mut dict = entry.finalize(110); 51 | assert_eq(@dict[10], @110, 'dict[10] == 110'); 52 | } 53 | 54 | #[test] 55 | fn test_dict_update_twice() { 56 | let mut dict = Default::default(); 57 | dict.insert(10, 110); 58 | let (entry, value) = dict.entry(10); 59 | assert_eq(@value, @110, 'dict[10] == 110'); 60 | dict = entry.finalize(11); 61 | assert_eq(@dict[10], @11, 'dict[10] == 11'); 62 | let (entry, value) = dict.entry(10); 63 | assert_eq(@value, @11, 'dict[10] == 11'); 64 | dict = entry.finalize(12); 65 | assert_eq(@dict[10], @12, 'dict[10] == 12'); 66 | } 67 | 68 | 69 | /// Tests the destruction of a non-finalized `Felt252DictEntry`. 70 | /// 71 | /// Calls the destructor of the entry, which in turn calls the destructor of the `Felt252Dict`. 72 | #[test] 73 | fn test_dict_entry_destruct() { 74 | let mut dict = Default::default(); 75 | dict.insert(10, 110); 76 | let (entry, value) = dict.entry(10); 77 | } 78 | 79 | const KEY1: felt252 = 10; 80 | const KEY2: felt252 = 21; 81 | // KEY3 is ~37% * PRIME. 82 | const KEY3: felt252 = 1343531647004637707094910297222796970954128321746173119103571679493202324940; 83 | // KEY4 and KEY5 are ~92% * PRIME. 84 | const KEY4: felt252 = 3334603141101959564751596861783084684819726025596122159217101666076094555684; 85 | const KEY5: felt252 = 3334603141101959564751596861783084684819726025596122159217101666076094555685; 86 | 87 | /// Tests the big-keys behavior of `felt252_dict_squash()`. 88 | /// 89 | /// Uses a few keys to simulate the 3 possible cases in `validate_felt252_le`. 90 | #[test] 91 | fn test_dict_big_keys() { 92 | let mut dict = Default::default(); 93 | 94 | dict.insert(KEY1, 1); 95 | dict.insert(KEY2, 2); 96 | dict.insert(KEY3, 3); 97 | dict.insert(KEY4, 4); 98 | dict.insert(KEY5, 5); 99 | 100 | assert_eq(@dict[KEY1], @1, 'KEY1'); 101 | assert_eq(@dict[KEY2], @2, 'KEY2'); 102 | assert_eq(@dict[KEY3], @3, 'KEY3'); 103 | assert_eq(@dict[KEY4], @4, 'KEY4'); 104 | assert_eq(@dict[KEY5], @5, 'KEY5'); 105 | } 106 | 107 | #[test] 108 | fn test_dict_of_nullable() { 109 | let mut dict = Default::default(); 110 | dict.insert(10, nullable_from_box(BoxTrait::new(1))); 111 | dict.insert(11, nullable_from_box(BoxTrait::new(2))); 112 | let val10 = dict[10].deref(); 113 | let val11 = dict[11].deref(); 114 | let val12 = dict[12]; 115 | assert_eq(@val10, @1, 'dict[10] == 1'); 116 | assert_eq(@val11, @2, 'dict[11] == 2'); 117 | assert( 118 | match nullable::match_nullable(val12) { 119 | nullable::FromNullableResult::Null => true, 120 | nullable::FromNullableResult::NotNull(_) => false, 121 | }, 122 | 'default_val == null' 123 | ); 124 | } 125 | // TODO(lior): Re-enable the test once Dict of bools are supported. 126 | // #[test] 127 | // fn test_bool_dict() { 128 | // let mut bool_dict: Felt252Dict = Felt252DictTrait::new(); 129 | // let squashed_dict = bool_dict.squash(); 130 | // let mut bool_dict: Felt252Dict = Felt252DictTrait::new(); 131 | // assert(!bool_dict.get(0), 'default_val != false'); 132 | // bool_dict.insert(1, true); 133 | // assert(bool_dict.get(1), 'bool_dict[1] != true'); 134 | // } 135 | 136 | 137 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/ec_test.cairo: -------------------------------------------------------------------------------- 1 | use ec::{EcPointTrait, EcStateTrait}; 2 | use option::OptionTrait; 3 | use test::test_utils::{assert_eq, assert_ne}; 4 | use traits::{Into, TryInto}; 5 | 6 | #[test] 7 | #[should_panic] 8 | fn test_ec_from_zero() { 9 | EcPointTrait::new_from_x(0).expect('Not on curve.'); 10 | } 11 | 12 | #[test] 13 | fn test_ec_operations() { 14 | // Beta + 2 is a square, and for x = 1 and alpha = 1, x^3 + alpha * x + beta = beta + 2. 15 | let beta_p2_root = 2487829544412206244690656897973144572467842667075005257202960243805141046681; 16 | let p = EcPointTrait::new_from_x(1).unwrap(); 17 | let p_nz = p.try_into().unwrap(); 18 | let (x, y) = p_nz.coordinates(); 19 | assert_eq(@x, @1, 'x != 1'); 20 | assert(y == beta_p2_root || y == -beta_p2_root, 'y is wrong'); 21 | 22 | let mut state = EcStateTrait::init(); 23 | state.add(p_nz); 24 | let q = state.finalize_nz().expect('zero point'); 25 | let (qx, qy) = q.coordinates(); 26 | assert_eq(@qx, @x, 'bad finalize x'); 27 | assert_eq(@qy, @y, 'bad finalize y'); 28 | 29 | // Try doing the same thing with the EC op builtin. 30 | let mut state = EcStateTrait::init(); 31 | state.add_mul(1, p_nz); 32 | let q3 = state.finalize_nz().expect('zero point'); 33 | let (qx, qy) = q3.coordinates(); 34 | assert_eq(@qx, @x, 'bad EC op x'); 35 | assert_eq(@qy, @y, 'bad EC op y'); 36 | 37 | // Try computing `p + p` using the ec_mul function. 38 | let double_p = p.mul(2); 39 | let (double_x, double_y) = double_p.try_into().unwrap().coordinates(); 40 | let expected_double_y = 41 | 3572434102142093425782752266058856056057826477682467661647843687948039943621; 42 | assert_eq( 43 | @double_x, 44 | @75984168971785666410219869038140038216102669781812169677875295511117260233, 45 | 'bad double x' 46 | ); 47 | assert(double_y == expected_double_y || double_y == -expected_double_y, 'bad double y'); 48 | 49 | // Compute `2p - p`. 50 | let (sub_x, sub_y) = (double_p - p).try_into().unwrap().coordinates(); 51 | assert_eq(@sub_x, @x, 'bad x for 2p - p'); 52 | assert_eq(@sub_y, @y, 'bad y for 2p - p'); 53 | 54 | // Compute `p - p`. 55 | assert((p - p).try_into().is_none(), 'p - p did not return 0.'); 56 | 57 | // Compute `(-p) - p`. 58 | let (sub2_x, sub2_y) = (-p - p).try_into().unwrap().coordinates(); 59 | assert_eq(@sub2_x, @double_x, 'bad x for (-p) - p'); 60 | assert_eq(@sub2_y, @-double_y, 'bad y for (-p) - p'); 61 | } 62 | 63 | #[test] 64 | #[should_panic] 65 | fn test_bad_ec_point_creation() { 66 | EcPointTrait::new(0, 0).unwrap(); 67 | } 68 | 69 | #[test] 70 | fn test_ec_point_finalization_zero() { 71 | let state = EcStateTrait::init(); 72 | let point_at_infinity = state.finalize_nz(); 73 | assert(point_at_infinity.is_none(), 'Wrong point'); 74 | } 75 | 76 | #[test] 77 | fn test_ecdsa() { 78 | let message_hash = 0x503f4bea29baee10b22a7f10bdc82dda071c977c1f25b8f3973d34e6b03b2c; 79 | let public_key = 0x7b7454acbe7845da996377f85eb0892044d75ae95d04d3325a391951f35d2ec; 80 | let signature_r = 0xbe96d72eb4f94078192c2e84d5230cde2a70f4b45c8797e2c907acff5060bb; 81 | let signature_s = 0x677ae6bba6daf00d2631fab14c8acf24be6579f9d9e98f67aa7f2770e57a1f5; 82 | assert( 83 | ecdsa::check_ecdsa_signature(:message_hash, :public_key, :signature_r, :signature_s), 84 | 'ecdsa returned false' 85 | ); 86 | assert( 87 | !ecdsa::check_ecdsa_signature( 88 | message_hash: message_hash + 1, :public_key, :signature_r, :signature_s 89 | ), 90 | 'ecdsa - wrong message' 91 | ); 92 | assert( 93 | !ecdsa::check_ecdsa_signature( 94 | :message_hash, public_key: public_key + 1, :signature_r, :signature_s 95 | ), 96 | 'ecdsa - wrong public_key' 97 | ); 98 | assert( 99 | !ecdsa::check_ecdsa_signature( 100 | :message_hash, :public_key, signature_r: signature_r + 1, :signature_s 101 | ), 102 | 'ecdsa - wrong r' 103 | ); 104 | assert( 105 | !ecdsa::check_ecdsa_signature( 106 | :message_hash, :public_key, :signature_r, signature_s: signature_s + 1 107 | ), 108 | 'ecdsa - wrong s' 109 | ); 110 | } 111 | 112 | #[test] 113 | #[available_gas(100000000)] 114 | fn test_ecdsa_recover_public_key() { 115 | let message_hash = 0x503f4bea29baee10b22a7f10bdc82dda071c977c1f25b8f3973d34e6b03b2c; 116 | let signature_r = 0xbe96d72eb4f94078192c2e84d5230cde2a70f4b45c8797e2c907acff5060bb; 117 | let signature_s = 0x677ae6bba6daf00d2631fab14c8acf24be6579f9d9e98f67aa7f2770e57a1f5; 118 | assert_eq( 119 | @ecdsa::recover_public_key(:message_hash, :signature_r, :signature_s, y_parity: false) 120 | .unwrap(), 121 | @0x7b7454acbe7845da996377f85eb0892044d75ae95d04d3325a391951f35d2ec, 122 | 'recover_ecdsa_public_key failed' 123 | ); 124 | assert( 125 | ecdsa::check_ecdsa_signature( 126 | :message_hash, 127 | public_key: ecdsa::recover_public_key( 128 | :message_hash, :signature_r, :signature_s, y_parity: true 129 | ) 130 | .unwrap(), 131 | :signature_r, 132 | :signature_s 133 | ), 134 | 'ecdsa returned false' 135 | ); 136 | } 137 | 138 | #[test] 139 | fn test_ec_mul() { 140 | let p = EcPointTrait::new( 141 | x: 336742005567258698661916498343089167447076063081786685068305785816009957563, 142 | y: 1706004133033694959518200210163451614294041810778629639790706933324248611779, 143 | ) 144 | .unwrap(); 145 | let m = 2713877091499598330239944961141122840311015265600950719674787125185463975936; 146 | let (x, y) = p.mul(m).try_into().unwrap().coordinates(); 147 | 148 | assert_eq( 149 | @x, 150 | @2881632108168892236043523177391659237686965655035240771134509747985978822780, 151 | 'ec_mul failed (x).' 152 | ); 153 | assert_eq( 154 | @y, 155 | @591135563672138037839394207500885413019058613584891498394077262936524140839, 156 | 'ec_mul failed (y).' 157 | ); 158 | } 159 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/felt_test.cairo: -------------------------------------------------------------------------------- 1 | use test::test_utils::{assert_eq, assert_ne}; 2 | 3 | #[test] 4 | fn test_felt252_operators() { 5 | assert_eq(@(1 + 3), @4, '1 + 3 == 4'); 6 | assert_eq(@(3 + 6), @9, '3 + 6 == 9'); 7 | assert_eq(@(3 - 1), @2, '3 - 1 == 2'); 8 | assert_eq(@(1231 - 231), @1000, '1231-231=1000'); 9 | assert_eq(@(1 * 3), @3, '1 * 3 == 3'); 10 | assert_eq(@(3 * 6), @18, '3 * 6 == 18'); 11 | assert_eq(@(-3), @(1 - 4), '-3 == 1 - 4'); 12 | } 13 | 14 | #[test] 15 | fn test_felt252_clone() { 16 | let felt252_snap = @2; 17 | let felt252_clone = felt252_snap.clone(); 18 | assert_eq(@felt252_clone, @2, 'felt252_clone == 2'); 19 | } 20 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/hash_test.cairo: -------------------------------------------------------------------------------- 1 | use test::test_utils::{assert_eq, assert_ne}; 2 | 3 | #[test] 4 | fn test_pedersen_hash() { 5 | assert_eq( 6 | @pedersen::pedersen(1, 2), 7 | @2592987851775965742543459319508348457290966253241455514226127639100457844774, 8 | 'Wrong hash value' 9 | ); 10 | } 11 | 12 | #[test] 13 | fn test_poseidon_hades_permutation() { 14 | let (s0, s1, s2) = poseidon::hades_permutation(1, 2, 3); 15 | assert_eq( 16 | @s0, 17 | @442682200349489646213731521593476982257703159825582578145778919623645026501, 18 | 'wrong s0' 19 | ); 20 | assert_eq( 21 | @s1, 22 | @2233832504250924383748553933071188903279928981104663696710686541536735838182, 23 | 'wrong s1' 24 | ); 25 | assert_eq( 26 | @s2, 27 | @2512222140811166287287541003826449032093371832913959128171347018667852712082, 28 | 'wrong s2' 29 | ); 30 | } 31 | 32 | #[test] 33 | #[available_gas(300000)] 34 | fn test_poseidon_hash_span() { 35 | // Test odd number of inputs. 36 | assert_eq( 37 | @poseidon::poseidon_hash_span(array![1, 2, 3].span()), 38 | @0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082, 39 | 'wrong result' 40 | ); 41 | 42 | // Test even number of inputs. 43 | assert_eq( 44 | @poseidon::poseidon_hash_span(array![1, 2, 3, 4].span()), 45 | @0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d, 46 | 'wrong result' 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/math_test.cairo: -------------------------------------------------------------------------------- 1 | #[test] 2 | #[available_gas(10000000)] 3 | fn test_egcd() { 4 | let (g, s, t, sub_direction) = math::egcd(68_u8.try_into().unwrap(), 16_u8.try_into().unwrap()); 5 | assert(g == 4, 'g != 4'); 6 | assert(s == 1, 's != 1'); 7 | assert(t == 4, 't != 4'); 8 | assert(sub_direction, 'sub_direction is wrong'); 9 | assert(1 * 68 - 4 * 16 == 4, 'Sanity check failed'); 10 | 11 | let (g, s, t, sub_direction) = math::egcd( 12 | 240_u256.try_into().unwrap(), 46_u256.try_into().unwrap() 13 | ); 14 | assert(g == 2, 'g != 2'); 15 | assert(s == 9, 's != 9'); 16 | assert(t == 47, 't != 47'); 17 | assert(!sub_direction, 'sub_direction is wrong'); 18 | assert(47 * 46 - 9 * 240 == 2, 'Sanity check failed'); 19 | 20 | let (g, s, t, sub_direction) = math::egcd( 21 | 50_u128.try_into().unwrap(), 17_u128.try_into().unwrap() 22 | ); 23 | assert(g == 1, 'g != 1'); 24 | assert(s == 1, 's != 1'); 25 | assert(t == 3, 't != 3'); 26 | assert(!sub_direction, 'sub_direction is wrong'); 27 | assert(3 * 17 - 1 * 50 == 1, 'Sanity check failed'); 28 | 29 | let (g, s, t, sub_direction) = math::egcd( 30 | 5_u128.try_into().unwrap(), 15_u128.try_into().unwrap() 31 | ); 32 | assert(g == 5, 'g != 5'); 33 | assert(s == 1, 's != 1'); 34 | assert(t == 0, 't != 0'); 35 | assert(sub_direction, 'sub_direction is wrong'); 36 | assert(1 * 5 - 0 * 15 == 5, 'Sanity check failed'); 37 | 38 | let (g, s, t, sub_direction) = math::egcd( 39 | 1_u128.try_into().unwrap(), 1_u128.try_into().unwrap() 40 | ); 41 | assert(g == 1, 'g != 1'); 42 | assert(s == 0, 's != 0'); 43 | assert(t == 1, 't != 1'); 44 | assert(!sub_direction, 'sub_direction is wrong'); 45 | assert(1 * 1 - 0 * 1 == 1, 'Sanity check failed'); 46 | } 47 | 48 | #[test] 49 | #[available_gas(10000000)] 50 | fn test_inv_mod() { 51 | let inv = math::inv_mod(5_u256.try_into().unwrap(), 24_u256.try_into().unwrap()).unwrap(); 52 | assert(inv == 5, 'inv != 5'); 53 | 54 | let inv = math::inv_mod(29_u128.try_into().unwrap(), 24_u128.try_into().unwrap()).unwrap(); 55 | assert(inv == 5, 'inv != 5'); 56 | 57 | let inv = math::inv_mod(1_u16.try_into().unwrap(), 24_u16.try_into().unwrap()).unwrap(); 58 | assert(inv == 1, 'inv != 1'); 59 | 60 | let inv = math::inv_mod(1_u32.try_into().unwrap(), 5_u32.try_into().unwrap()).unwrap(); 61 | assert(inv == 1, 'inv != 1'); 62 | 63 | let inv = math::inv_mod(8_usize.try_into().unwrap(), 24_usize.try_into().unwrap()); 64 | assert(inv.is_none(), 'inv should be None'); 65 | 66 | let inv = math::inv_mod(1_usize.try_into().unwrap(), 1_usize.try_into().unwrap()).unwrap(); 67 | assert(inv == 0, 'inv != 0'); 68 | 69 | let inv = math::inv_mod(7_usize.try_into().unwrap(), 1_usize.try_into().unwrap()).unwrap(); 70 | assert(inv == 0, 'inv != 0'); 71 | } 72 | 73 | #[test] 74 | #[available_gas(10000000)] 75 | fn test_u256_div_mod_n() { 76 | let q = math::u256_div_mod_n(6_u256, 2_u256.try_into().unwrap(), 7_u256.try_into().unwrap()) 77 | .unwrap(); 78 | assert(q == 3, '6 / 2 != 3 (7)'); 79 | 80 | let q = math::u256_div_mod_n(5_u256, 1_u256.try_into().unwrap(), 7_u256.try_into().unwrap()) 81 | .unwrap(); 82 | assert(q == 5, '5 / 1 != 5 (7)'); 83 | 84 | let q = math::u256_div_mod_n(1_u256, 1_u256.try_into().unwrap(), 7_u256.try_into().unwrap()) 85 | .unwrap(); 86 | assert(q == 1, '1 / 1 != 1 (7)'); 87 | 88 | let q = math::u256_div_mod_n(7_u256, 2_u256.try_into().unwrap(), 13_u256.try_into().unwrap()) 89 | .unwrap(); 90 | assert(q == 10, '7 / 2 != 10 (13)'); 91 | 92 | let q = math::u256_div_mod_n(0_u256, 3_u256.try_into().unwrap(), 13_u256.try_into().unwrap()) 93 | .unwrap(); 94 | assert(q == 0, '0 / 3 != 0 (13)'); 95 | } 96 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/plugins_test.cairo: -------------------------------------------------------------------------------- 1 | use test::test_utils::{assert_eq, assert_ne}; 2 | 3 | #[derive(Copy, Drop, Serde, PartialEq)] 4 | enum EnumForSerde { 5 | A, 6 | B: u32, 7 | C: u64, 8 | } 9 | 10 | #[test] 11 | fn test_derive_serde_enum() { 12 | let a = EnumForSerde::A(()); 13 | let b = EnumForSerde::B(1); 14 | let c = EnumForSerde::C(2); 15 | let mut output = Default::default(); 16 | a.serialize(ref output); 17 | a.serialize(ref output); 18 | c.serialize(ref output); 19 | b.serialize(ref output); 20 | a.serialize(ref output); 21 | let mut serialized = output.span(); 22 | assert_eq( 23 | @Serde::::deserialize(ref serialized).expect('failed to read'), 24 | @a, 25 | 'expected a' 26 | ); 27 | assert_eq( 28 | @Serde::::deserialize(ref serialized).expect('failed to read'), 29 | @a, 30 | 'expected a' 31 | ); 32 | assert_eq( 33 | @Serde::::deserialize(ref serialized).expect('failed to read'), 34 | @c, 35 | 'expected c' 36 | ); 37 | assert_eq( 38 | @Serde::::deserialize(ref serialized).expect('failed to read'), 39 | @b, 40 | 'expected b' 41 | ); 42 | assert_eq( 43 | @Serde::::deserialize(ref serialized).expect('failed to read'), 44 | @a, 45 | 'expected a' 46 | ); 47 | assert(serialized.is_empty(), 'expected empty'); 48 | } 49 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/secp256k1_test.cairo: -------------------------------------------------------------------------------- 1 | use starknet::{ 2 | eth_address::U256IntoEthAddress, EthAddress, secp256k1::Secp256k1Impl, SyscallResultTrait 3 | }; 4 | use starknet::secp256_trait::{ 5 | Signature, recover_public_key, verify_eth_signature, Secp256PointTrait, signature_from_vrs 6 | }; 7 | use starknet::secp256k1::{Secp256k1Point, Secp256k1PointImpl}; 8 | 9 | #[test] 10 | #[available_gas(100000000)] 11 | fn test_secp256k1_recover_public_key() { 12 | let y_parity = true; 13 | let (msg_hash, signature, expected_public_key_x, expected_public_key_y, _) = 14 | get_message_and_signature( 15 | :y_parity 16 | ); 17 | let public_key = recover_public_key::(msg_hash, signature).unwrap(); 18 | let (x, y) = public_key.get_coordinates().unwrap_syscall(); 19 | assert(expected_public_key_x == x, 'recover failed 1'); 20 | assert(expected_public_key_y == y, 'recover failed 2'); 21 | 22 | let y_parity = false; 23 | let (msg_hash, signature, expected_public_key_x, expected_public_key_y, _) = 24 | get_message_and_signature( 25 | :y_parity 26 | ); 27 | let public_key = recover_public_key::(msg_hash, signature).unwrap(); 28 | let (x, y) = public_key.get_coordinates().unwrap_syscall(); 29 | assert(expected_public_key_x == x, 'recover failed 3'); 30 | assert(expected_public_key_y == y, 'recover failed 4'); 31 | } 32 | 33 | #[test] 34 | fn test_signature_from_vrs() { 35 | let v = 27; 36 | let r = 1; 37 | let s = 2; 38 | let signature = signature_from_vrs(v, r, s); 39 | 40 | assert(signature == Signature { r, s, y_parity: false }, 'Wrong result'); 41 | } 42 | 43 | /// Returns a golden valid message hash and its signature, for testing. 44 | fn get_message_and_signature(y_parity: bool) -> (u256, Signature, u256, u256, EthAddress) { 45 | let msg_hash = 0xe888fbb4cf9ae6254f19ba12e6d9af54788f195a6f509ca3e934f78d7a71dd85; 46 | let r = 0x4c8e4fbc1fbb1dece52185e532812c4f7a5f81cf3ee10044320a0d03b62d3e9a; 47 | let s = 0x4ac5e5c0c0e8a4871583cc131f35fb49c2b7f60e6a8b84965830658f08f7410c; 48 | 49 | let (public_key_x, public_key_y) = if y_parity { 50 | ( 51 | 0xa9a02d48081294b9bb0d8740d70d3607feb20876964d432846d9b9100b91eefd, 52 | 0x18b410b5523a1431024a6ab766c89fa5d062744c75e49efb9925bf8025a7c09e 53 | ) 54 | } else { 55 | ( 56 | 0x57a910a2a58ef7d57f452e1f6ea7ee0080789091de946b0ca6e5c6af2c8ff5c8, 57 | 0x249d233d0d21f35db55ce852edbd340d31e92ea4d591886149ca5d89911331ac 58 | ) 59 | }; 60 | let eth_address = 0x767410c1bb448978bd42b984d7de5970bcaf5c43_u256.into(); 61 | 62 | (msg_hash, Signature { r, s, y_parity }, public_key_x, public_key_y, eth_address) 63 | } 64 | 65 | #[test] 66 | #[available_gas(100000000)] 67 | fn test_verify_eth_signature() { 68 | let y_parity = true; 69 | let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = 70 | get_message_and_signature( 71 | :y_parity 72 | ); 73 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 74 | } 75 | 76 | #[test] 77 | #[should_panic(expected: ('Invalid signature',))] 78 | #[available_gas(100000000)] 79 | fn test_verify_eth_signature_wrong_eth_address() { 80 | let y_parity = true; 81 | let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = 82 | get_message_and_signature( 83 | :y_parity 84 | ); 85 | let eth_address = (eth_address.into() + 1).try_into().unwrap(); 86 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 87 | } 88 | 89 | #[test] 90 | #[should_panic(expected: ('Signature out of range',))] 91 | #[available_gas(100000000)] 92 | fn test_verify_eth_signature_overflowing_signature_r() { 93 | let y_parity = true; 94 | let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = 95 | get_message_and_signature( 96 | :y_parity 97 | ); 98 | signature.r = Secp256k1Impl::get_curve_size() + 1; 99 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 100 | } 101 | 102 | #[test] 103 | #[should_panic(expected: ('Signature out of range',))] 104 | #[available_gas(100000000)] 105 | fn test_verify_eth_signature_overflowing_signature_s() { 106 | let y_parity = true; 107 | let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = 108 | get_message_and_signature( 109 | :y_parity 110 | ); 111 | signature.s = Secp256k1Impl::get_curve_size() + 1; 112 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 113 | } 114 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/secp256r1_test.cairo: -------------------------------------------------------------------------------- 1 | use starknet::{ 2 | eth_address::U256IntoEthAddress, EthAddress, secp256r1::Secp256r1Impl, SyscallResultTrait 3 | }; 4 | use starknet::secp256_trait::{ 5 | recover_public_key, verify_eth_signature, Secp256PointTrait, Signature 6 | }; 7 | use starknet::secp256r1::{Secp256r1Point, Secp256r1PointImpl}; 8 | use test::test_utils::assert_eq; 9 | 10 | #[test] 11 | #[available_gas(100000000)] 12 | fn test_secp256r1_recover_public_key() { 13 | let (msg_hash, signature, expected_public_key_x, expected_public_key_y, _) = 14 | get_message_and_signature(); 15 | let public_key = recover_public_key::(msg_hash, signature).unwrap(); 16 | let (x, y) = public_key.get_coordinates().unwrap_syscall(); 17 | assert(expected_public_key_x == x, 'recover failed 1'); 18 | assert(expected_public_key_y == y, 'recover failed 2'); 19 | } 20 | 21 | 22 | /// Returns a golden valid message hash and its signature, for testing. 23 | fn get_message_and_signature() -> (u256, Signature, u256, u256, EthAddress) { 24 | // msg = "" 25 | // public key: (0x04aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad5, 26 | // 0x0087d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d) 27 | let msg_hash = 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855; 28 | let r = 0xb292a619339f6e567a305c951c0dcbcc42d16e47f219f9e98e76e09d8770b34a; 29 | let s = 0x177e60492c5a8242f76f07bfe3661bde59ec2a17ce5bd2dab2abebdf89a62e2; 30 | 31 | let (public_key_x, public_key_y) = ( 32 | 0x04aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad5, 33 | 0x0087d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d 34 | ); 35 | let eth_address = 0x492882426e1cda979008bfaf874ff796eb3bb1c0_u256.into(); 36 | 37 | (msg_hash, Signature { r, s, y_parity: true }, public_key_x, public_key_y, eth_address) 38 | } 39 | 40 | #[test] 41 | #[available_gas(100000000)] 42 | fn test_verify_eth_signature() { 43 | let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = 44 | get_message_and_signature(); 45 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 46 | } 47 | 48 | #[test] 49 | #[should_panic(expected: ('Invalid signature',))] 50 | #[available_gas(100000000)] 51 | fn test_verify_eth_signature_wrong_eth_address() { 52 | let (msg_hash, signature, expected_public_key_x, expected_public_key_y, eth_address) = 53 | get_message_and_signature(); 54 | let eth_address = (eth_address.into() + 1).try_into().unwrap(); 55 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 56 | } 57 | 58 | #[test] 59 | #[should_panic(expected: ('Signature out of range',))] 60 | #[available_gas(100000000)] 61 | fn test_verify_eth_signature_overflowing_signature_r() { 62 | let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = 63 | get_message_and_signature(); 64 | signature.r = Secp256r1Impl::get_curve_size() + 1; 65 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 66 | } 67 | 68 | #[test] 69 | #[should_panic(expected: ('Signature out of range',))] 70 | #[available_gas(100000000)] 71 | fn test_verify_eth_signature_overflowing_signature_s() { 72 | let (msg_hash, mut signature, expected_public_key_x, expected_public_key_y, eth_address) = 73 | get_message_and_signature(); 74 | signature.s = Secp256r1Impl::get_curve_size() + 1; 75 | verify_eth_signature::(:msg_hash, :signature, :eth_address); 76 | } 77 | 78 | 79 | #[test] 80 | #[available_gas(100_000_000)] 81 | fn test_recover_public_key_y_even() { 82 | let x: u256 = 0x502a43ce77c6f5c736a82f847fa95f8c2d483fe223b12b91047d83258a958b0f; 83 | let y: u256 = 0xdb0a2e6710c71ba80afeb3abdf69d306ce729c7704f4ddf2eaaf0b76209fe1b0; 84 | let r: u256 = 0x7380df4a623c5c2259a5e5f5b225d7265a9e24b3a13c101d1afddcf29e3cf8b2; 85 | let s: u256 = 0x0d131afacdd17a4ea1b544bb3ade677ff8accbe7830e15b9c225e6031155946a; 86 | let y_parity = false; 87 | let message_hash: u256 = 0x28c7fff9aef4847a82cd64280434712a5b49205831b60eea6e70614077e672eb; 88 | let recovered = recover_public_key::(message_hash, Signature { r, s, y_parity }) 89 | .unwrap(); 90 | let (recovered_x, recovered_y) = recovered.get_coordinates().unwrap_syscall(); 91 | 92 | assert_eq(@recovered_x, @x, 'Signature is not valid'); 93 | } 94 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/test_utils.cairo: -------------------------------------------------------------------------------- 1 | #[inline] 2 | fn assert_eq>(a: @T, b: @T, err_code: felt252) { 3 | assert(a == b, err_code); 4 | } 5 | 6 | #[inline] 7 | fn assert_ne>(a: @T, b: @T, err_code: felt252) { 8 | assert(a != b, err_code); 9 | } 10 | 11 | #[inline] 12 | fn assert_le>(a: T, b: T, err_code: felt252) { 13 | assert(a <= b, err_code); 14 | } 15 | 16 | #[inline] 17 | fn assert_lt>(a: T, b: T, err_code: felt252) { 18 | assert(a < b, err_code); 19 | } 20 | 21 | #[inline] 22 | fn assert_ge>(a: T, b: T, err_code: felt252) { 23 | assert(a >= b, err_code); 24 | } 25 | 26 | #[inline] 27 | fn assert_gt>(a: T, b: T, err_code: felt252) { 28 | assert(a > b, err_code); 29 | } 30 | -------------------------------------------------------------------------------- /cairo/corelib/src/test/testing_test.cairo: -------------------------------------------------------------------------------- 1 | use test::test_utils::{assert_eq, assert_ne, assert_gt}; 2 | 3 | #[test] 4 | #[should_panic(expected: ('panic_with_felt252()',))] 5 | fn test_panic_with_felt252() { 6 | // No semicolon here: Missing implementation for core::traits::Drop:: 7 | panic_with_felt252('panic_with_felt252()') 8 | } 9 | 10 | #[test] 11 | #[should_panic(expected: ('assert(false)',))] 12 | fn test_assert_false() { 13 | assert(false, 'assert(false)'); 14 | } 15 | 16 | #[test] 17 | fn test_assert_true() { 18 | assert(true, 'assert(true)'); 19 | } 20 | 21 | #[test] 22 | fn test_get_available_gas_no_gas_supply() { 23 | assert_eq(@testing::get_available_gas(), @0, 'expected no_gas_supply') 24 | } 25 | 26 | #[test] 27 | #[available_gas(10000)] 28 | fn test_get_available_gas_with_gas_supply() { 29 | assert_gt(testing::get_available_gas(), 5000, 'high amount of gas used') 30 | } 31 | -------------------------------------------------------------------------------- /cairo/corelib/src/testing.cairo: -------------------------------------------------------------------------------- 1 | extern fn get_available_gas() -> u128 implicits(GasBuiltin) nopanic; 2 | -------------------------------------------------------------------------------- /cairo/corelib/src/zeroable.cairo: -------------------------------------------------------------------------------- 1 | // === Zeroable === 2 | 3 | trait Zeroable { 4 | /// Returns the additive identity element of Self, 0. 5 | fn zero() -> T; 6 | /// Returns whether self is equal to 0, the additive identity element. 7 | fn is_zero(self: T) -> bool; 8 | /// Returns whether self is not equal to 0, the additive identity element. 9 | fn is_non_zero(self: T) -> bool; 10 | } 11 | 12 | impl Felt252Zeroable of Zeroable { 13 | fn zero() -> felt252 { 14 | 0 15 | } 16 | #[inline(always)] 17 | fn is_zero(self: felt252) -> bool { 18 | self == 0 19 | } 20 | #[inline(always)] 21 | fn is_non_zero(self: felt252) -> bool { 22 | !self.is_zero() 23 | } 24 | } 25 | 26 | // === NonZero === 27 | 28 | #[derive(Copy, Drop)] 29 | extern type NonZero; 30 | 31 | enum IsZeroResult { 32 | Zero, 33 | NonZero: NonZero, 34 | } 35 | extern fn unwrap_non_zero(a: NonZero) -> T nopanic; 36 | 37 | impl NonZeroIntoImpl of Into, T> { 38 | fn into(self: NonZero) -> T nopanic { 39 | unwrap_non_zero(self) 40 | } 41 | } 42 | 43 | impl IsZeroResultIntoBool> of Into, bool> { 44 | fn into(self: IsZeroResult) -> bool { 45 | match self { 46 | IsZeroResult::Zero => true, 47 | IsZeroResult::NonZero(_) => false, 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cairo/exercises/README.md: -------------------------------------------------------------------------------- 1 | # Felt operations 2 | 3 | A field element - felt, is a native type in Cairo. Learn more about it in the [Cairo book](https://cairo-book.github.io/ch02-02-data-types.html#felt-type) to learn more about it 4 | 5 | Cairo1 has native integer types which support more operators then felts, like %, / 6 | Take a look [here](https://cairo-book.github.io/ch02-02-data-types.html#integer-types) for more details 7 | 8 | # Variables 9 | 10 | In Cairo, variables are immutable by default. 11 | When a variable is immutable, once a value is bound to a name, you can’t change that value. 12 | You can make them mutable by adding mut in front of the variable name. 13 | 14 | It is however important to clarify the fact that even though the variable can be made mutable, Cairo works with an immutable memory model, meaning that changing the value of a variable will not change the value in memory but rather assign a new memory location to that variable. 15 | 16 | ## Further information 17 | 18 | - [Memory model (from Cairo 0)](https://www.cairo-lang.org/docs/how_cairo_works/cairo_intro.html#memory-model) 19 | - [Variables](https://cairo-book.github.io/ch02-01-variables-and-mutability.html) 20 | - [Integer types](https://cairo-book.github.io/ch02-02-data-types.html#integer-types) 21 | 22 | # Primitive Types 23 | 24 | Cairo has a couple of basic types that are directly implemented into the 25 | compiler. In this section, we'll go through the most important ones. 26 | 27 | [Data Types](https://cairo-book.github.io/ch02-02-data-types.html) 28 | -------------------------------------------------------------------------------- /cairo/exercises/constants.cairo: -------------------------------------------------------------------------------- 1 | // constants.cairo 2 | // Execute `hint` watch subcommand for a hint. 3 | 4 | // I AM NOT DONE 5 | use debug::PrintTrait; 6 | 7 | const NUMBER = 3; 8 | const SMALL_NUMBER = 3_u8; 9 | fn main() { 10 | NUMBER.print(); 11 | SMALL_NUMBER.print(); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /cairo/exercises/conversions.cairo: -------------------------------------------------------------------------------- 1 | // conversion.cairo 2 | // Modify the integer types to make the tests pass. 3 | // Learn how to convert between integer types, and felts. 4 | // Execute `hint` watch subcommand for a hint. 5 | 6 | // I AM NOT DONE 7 | 8 | use traits::Into; 9 | use traits::TryInto; 10 | use option::OptionTrait; 11 | 12 | fn sum_u8s(x: u8, y: u8) -> u8 { 13 | x + y 14 | } 15 | 16 | //TODO modify the types of this function to prevent an overflow when summing big values 17 | fn sum_big_numbers(x: u8, y: u8) -> u8 { 18 | x + y 19 | } 20 | 21 | fn convert_to_felt(x: u8) -> felt252 { //TODO return x as a felt252. 22 | } 23 | 24 | fn convert_felt_to_u8(x: felt252) -> u8 { //TODO return x as a u8. 25 | } 26 | 27 | #[test] 28 | fn test_sum_u8s() { 29 | assert(sum_u8s(1, 2_u8) == 3_u8, 'Something went wrong'); 30 | } 31 | 32 | #[test] 33 | fn test_sum_big_numbers() { 34 | //TODO modify this test to use the correct integer types. 35 | // Don't modify the values, just the types. 36 | // See how using the _u8 suffix on the numbers lets us specify the type? 37 | // Try to do the same thing with other integer types. 38 | assert(sum_big_numbers(255_u8, 255_u8) == 510_u8, 'Something went wrong'); 39 | } 40 | 41 | #[test] 42 | fn test_convert_to_felt() { 43 | assert(convert_to_felt(1_u8) == 1, 'Type conversion went wrong'); 44 | } 45 | 46 | #[test] 47 | fn test_convert_to_u8() { 48 | assert(convert_felt_to_u8(1) == 1_u8, 'Type conversion went wrong'); 49 | } 50 | -------------------------------------------------------------------------------- /cairo/exercises/main.cairo: -------------------------------------------------------------------------------- 1 | // I AM NOT DONE 2 | // This exercise won't compile... Can you make it compile? 3 | -------------------------------------------------------------------------------- /cairo/exercises/operations.cairo: -------------------------------------------------------------------------------- 1 | // Remember last time you calculated division in Cairo0? 2 | // Now Cairo1 has native integer types e.g. u8, u32, ...u256, usize which support more operators than felts 3 | // And always watch out for overflows e.g in the last test 4 | // Let try to use them 5 | 6 | // I AM NOT DONE 7 | 8 | fn modulus(x: u8, y: u8) -> u8 { 9 | // calculate the modulus of x and y 10 | // FILL ME 11 | res 12 | } 13 | 14 | fn floor_division(x: usize, y: usize) -> u32 { 15 | // calculate the floor_division of x and y 16 | // FILL ME 17 | res 18 | } 19 | 20 | fn multiplication(x: u64, y: u64) -> u64 { 21 | // calculate the multiplication of x and y 22 | // FILL ME 23 | res 24 | } 25 | 26 | 27 | // Do not change the tests 28 | #[test] 29 | fn test_modulus() { 30 | let res = modulus(16, 2); 31 | assert(res == 0, 'Error message'); 32 | 33 | let res = modulus(17, 3); 34 | assert(res == 2, 'Error message'); 35 | } 36 | 37 | #[test] 38 | fn test_floor_division() { 39 | let res = floor_division(160, 2); 40 | assert(res == 80, 'Error message'); 41 | 42 | let res = floor_division(21, 4); 43 | assert(res == 5, 'Error message'); 44 | } 45 | 46 | #[test] 47 | fn test_mul() { 48 | let res = multiplication(16, 2); 49 | assert(res == 32, 'Error message'); 50 | 51 | let res = multiplication(21, 4); 52 | assert(res == 84, 'Error message'); 53 | } 54 | 55 | #[test] 56 | #[should_panic] 57 | fn test_u64_mul_overflow_1() { 58 | let res = multiplication(0x100000000, 0x100000000); 59 | } 60 | -------------------------------------------------------------------------------- /cairo/exercises/short_string.cairo: -------------------------------------------------------------------------------- 1 | // short_string.cairo 2 | // Fill in the rest of the line that has code missing! 3 | // No hints, there's no tricks, just get used to typing these :) 4 | 5 | // I AM NOT DONE 6 | 7 | use debug::PrintTrait; 8 | 9 | fn main() { 10 | // A short string is a string whose length is at most 31 characters, and therefore can fit into a single field element. 11 | // Short strings are actually felts, they are not a real string. 12 | // Note the _single_ quotes that are used with short strings. 13 | 14 | let mut my_first_initial = 'C'; 15 | if is_alphabetic( 16 | ref my_first_initial 17 | ) { 18 | ('Alphabetical!').print(); 19 | } else if is_numeric( 20 | ref my_first_initial 21 | ) { 22 | ('Numerical!').print(); 23 | } else { 24 | ('Neither alphabetic nor numeric!').print(); 25 | } 26 | 27 | let // Finish this line like the example! What's your favorite short string? 28 | // Try a letter, try a number, try a special character, try a short string! 29 | if is_alphabetic( 30 | ref your_character 31 | ) { 32 | ('Alphabetical!').print(); 33 | } else if is_numeric( 34 | ref your_character 35 | ) { 36 | ('Numerical!').print(); 37 | } else { 38 | ('Neither alphabetic nor numeric!').print(); 39 | } 40 | } 41 | 42 | fn is_alphabetic(ref char: felt252) -> bool { 43 | if char >= 'a' { 44 | if char <= 'z' { 45 | return true; 46 | } 47 | } 48 | if char >= 'A' { 49 | if char <= 'Z' { 50 | return true; 51 | } 52 | } 53 | false 54 | } 55 | 56 | fn is_numeric(ref char: felt252) -> bool { 57 | if char >= '0' { 58 | if char <= '9' { 59 | return true; 60 | } 61 | } 62 | false 63 | } 64 | 65 | // Note: the following code is not part of the challenge, it's just here to make the code above work. 66 | // Direct felt252 comparisons have been removed from the core library, so we need to implement them ourselves. 67 | // There will probably be a string / short string type in the future 68 | impl PartialOrdFelt of PartialOrd { 69 | #[inline(always)] 70 | fn le(lhs: felt252, rhs: felt252) -> bool { 71 | !(rhs < lhs) 72 | } 73 | #[inline(always)] 74 | fn ge(lhs: felt252, rhs: felt252) -> bool { 75 | !(lhs < rhs) 76 | } 77 | #[inline(always)] 78 | fn lt(lhs: felt252, rhs: felt252) -> bool { 79 | integer::u256_from_felt252(lhs) < integer::u256_from_felt252(rhs) 80 | } 81 | #[inline(always)] 82 | fn gt(lhs: felt252, rhs: felt252) -> bool { 83 | rhs < lhs 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cairo/exercises/tuple.cairo: -------------------------------------------------------------------------------- 1 | // tuple.cairo 2 | // Destructure the `cat` tuple to call print on each member. 3 | // Execute `hint` watch subcommand for a hint. 4 | 5 | // I AM NOT DONE 6 | 7 | use debug::PrintTrait; 8 | 9 | fn main() { 10 | let cat = ('Furry McFurson', 3); 11 | let // your pattern here = cat; 12 | name.print(); 13 | age.print(); 14 | } 15 | -------------------------------------------------------------------------------- /cairo/info.toml: -------------------------------------------------------------------------------- 1 | # Intro 2 | [[exercises]] 3 | name = "main" 4 | path = "exercises/main.cairo" 5 | mode = "compile" 6 | hint = """ 7 | No hints this time ;) 8 | """ 9 | 10 | 11 | # Variable 12 | 13 | [[exercises]] 14 | name = "constants" 15 | path = "exercises/constants.cairo" 16 | mode = "compile" 17 | hint = """ 18 | We know about variables and mutability, but there is another important type of 19 | variable available: constants. 20 | Constants are always immutable and they are declared with keyword 'const' rather 21 | than keyword 'let'. 22 | Constants types must also always be annotated. 23 | You can read about the constants here: https://cairo-book.github.io/ch02-01-variables-and-mutability.html?highlight=const#constants 24 | """ 25 | 26 | 27 | [[exercises]] 28 | name = "short_string" 29 | path = "exercises/short_string.cairo" 30 | mode = "compile" 31 | hint = "No hints this time ;)" 32 | 33 | [[exercises]] 34 | name = "tuple" 35 | path = "exercises/tuple.cairo" 36 | mode = "compile" 37 | hint = """ 38 | You'll need to make a pattern to bind `name` and `age` to the appropriate parts 39 | of the tuple. 40 | If you're familiar with Rust, you should know that Cairo has a similar syntax to 41 | destructure tuples into multiple variables. 42 | https://cairo-book.github.io/ch02-02-data-types.html?highlight=destructu#the-tuple-type 43 | You can do it!! 44 | """ 45 | 46 | [[exercises]] 47 | name = "conversions" 48 | path = "exercises/conversions.cairo" 49 | mode = "test" 50 | hint = """ 51 | There are multiple integer types in Cairo. You can read about them here: 52 | https://cairo-book.github.io/ch02-02-data-types.html#integer-types 53 | If you try to sum two integers and the result is bigger than the biggest integer of this type, you'll get a compilation error. 54 | You can convert integers to felts using the `.into()` method. Make sure that you imported the `Into` trait. 55 | You can convert felts to integers using the `.try_into()` method. Make sure that you imported the `TryInto` trait. 56 | This method will return an `Option` type, so you'll need to unwrap it. To use the `unwrap()` method, you'll need to import the `OptionTrait` trait. 57 | Take a look at the top of the file to see how these traits are imported. 58 | """ 59 | 60 | # OPERATIONS 61 | 62 | [[exercises]] 63 | name = "operations" 64 | path = "exercises/operations.cairo" 65 | mode = "test" 66 | hint = """Use % for modulus, / for division, and * for multiplication.""" -------------------------------------------------------------------------------- /cairo/src/project.rs: -------------------------------------------------------------------------------- 1 | use glob::glob; 2 | use serde::{Deserialize, Serialize}; 3 | use std::env; 4 | use std::error::Error; 5 | use std::process::Command; 6 | 7 | /// Contains the structure of resulting rust-project.json file 8 | /// and functions to build the data required to create the file 9 | #[derive(Serialize, Deserialize)] 10 | pub struct RustAnalyzerProject { 11 | sysroot_src: String, 12 | pub crates: Vec, 13 | } 14 | 15 | #[derive(Serialize, Deserialize)] 16 | pub struct Crate { 17 | root_module: String, 18 | edition: String, 19 | deps: Vec, 20 | cfg: Vec, 21 | } 22 | 23 | impl RustAnalyzerProject { 24 | pub fn new() -> RustAnalyzerProject { 25 | RustAnalyzerProject { 26 | sysroot_src: String::new(), 27 | crates: Vec::new(), 28 | } 29 | } 30 | 31 | /// Write rust-project.json to disk 32 | pub fn write_to_disk(&self) -> Result<(), std::io::Error> { 33 | std::fs::write( 34 | "./rust-project.json", 35 | serde_json::to_vec(&self).expect("Failed to serialize to JSON"), 36 | )?; 37 | Ok(()) 38 | } 39 | 40 | /// If path contains .rs extension, add a crate to `rust-project.json` 41 | fn path_to_json(&mut self, path: String) { 42 | if let Some((_, ext)) = path.split_once('.') { 43 | if ext == "rs" { 44 | self.crates.push(Crate { 45 | root_module: path, 46 | edition: "2021".to_string(), 47 | deps: Vec::new(), 48 | // This allows rust_analyzer to work inside #[test] blocks 49 | cfg: vec!["test".to_string()], 50 | }) 51 | } 52 | } 53 | } 54 | 55 | /// Parse the exercises folder for .rs files, any matches will create 56 | /// a new `crate` in rust-project.json which allows rust-analyzer to 57 | /// treat it like a normal binary 58 | pub fn exercises_to_json(&mut self) -> Result<(), Box> { 59 | for e in glob("./exercises/**/*")? { 60 | let path = e?.to_string_lossy().to_string(); 61 | self.path_to_json(path); 62 | } 63 | Ok(()) 64 | } 65 | 66 | /// Use `rustc` to determine the default toolchain 67 | pub fn get_sysroot_src(&mut self) -> Result<(), Box> { 68 | // check if RUST_SRC_PATH is set 69 | if let Ok(path) = env::var("RUST_SRC_PATH") { 70 | self.sysroot_src = path; 71 | return Ok(()); 72 | } 73 | 74 | let toolchain = Command::new("rustc") 75 | .arg("--print") 76 | .arg("sysroot") 77 | .output()? 78 | .stdout; 79 | 80 | let toolchain = String::from_utf8_lossy(&toolchain); 81 | let mut whitespace_iter = toolchain.split_whitespace(); 82 | 83 | let toolchain = whitespace_iter.next().unwrap_or(&toolchain); 84 | 85 | println!("Determined toolchain: {}\n", &toolchain); 86 | 87 | self.sysroot_src = (std::path::Path::new(toolchain) 88 | .join("lib") 89 | .join("rustlib") 90 | .join("src") 91 | .join("rust") 92 | .join("library") 93 | .to_string_lossy()) 94 | .to_string(); 95 | Ok(()) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /cairo/src/run.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | 3 | use crate::exercise::{Exercise, Mode}; 4 | use indicatif::ProgressBar; 5 | 6 | // Invoke the rust compiler on the path of the given exercise, 7 | // and run the ensuing binary. 8 | // The verbose argument helps determine whether or not to show 9 | // the output from the test harnesses (if the mode of the exercise is test) 10 | pub fn run(exercise: &Exercise) -> Result<(), ()> { 11 | match exercise.mode { 12 | Mode::Compile => run_cairo(exercise)?, 13 | Mode::Test => test_cairo(exercise)?, 14 | } 15 | Ok(()) 16 | } 17 | 18 | // Resets the exercise by stashing the changes. 19 | pub fn reset(exercise: &Exercise) -> Result<(), ()> { 20 | let command = Command::new("git") 21 | .args(["stash", "--"]) 22 | .arg(&exercise.path) 23 | .spawn(); 24 | 25 | match command { 26 | Ok(_) => Ok(()), 27 | Err(_) => Err(()), 28 | } 29 | } 30 | 31 | // Invoke the rust compiler on the path of the given exercise 32 | // and run the ensuing binary. 33 | // This is strictly for non-test binaries, so output is displayed 34 | fn run_cairo(exercise: &Exercise) -> Result<(), ()> { 35 | let progress_bar = ProgressBar::new_spinner(); 36 | progress_bar.set_message(format!("Running {exercise}...")); 37 | progress_bar.enable_steady_tick(100); 38 | let output = exercise.run_cairo(); 39 | 40 | if let Some(error) = output.as_ref().err() { 41 | progress_bar.finish_and_clear(); 42 | println!("{error}"); 43 | Err(()) 44 | } else { 45 | let message = output.unwrap(); 46 | println!("{message}"); 47 | success!("Successfully ran {}", exercise); 48 | Ok(()) 49 | } 50 | } 51 | 52 | // Invoke the rust compiler on the path of the given exercise 53 | // and run the ensuing binary. 54 | // This is strictly for non-test binaries, so output is displayed 55 | fn test_cairo(exercise: &Exercise) -> Result<(), ()> { 56 | let progress_bar = ProgressBar::new_spinner(); 57 | progress_bar.set_message(format!("Running {exercise}...")); 58 | progress_bar.enable_steady_tick(100); 59 | let output = exercise.test_cairo(); 60 | 61 | if let Some(error) = output.as_ref().err() { 62 | progress_bar.finish_and_clear(); 63 | println!("{error}"); 64 | Err(()) 65 | } else { 66 | let message = output.unwrap(); 67 | println!("{message}"); 68 | success!("Successfully ran {}", exercise); 69 | Ok(()) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /cairo/src/starklings_runner.rs: -------------------------------------------------------------------------------- 1 | //! Compiles and runs a Cairo program. 2 | 3 | use std::path::Path; 4 | 5 | use anyhow::{Context, Ok}; 6 | use cairo_lang_compiler::db::RootDatabase; 7 | use cairo_lang_compiler::diagnostics::DiagnosticsReporter; 8 | use cairo_lang_compiler::project::setup_project; 9 | use cairo_lang_diagnostics::ToOption; 10 | use cairo_lang_filesystem::db::init_dev_corelib; 11 | 12 | use cairo_lang_runner::{SierraCasmRunner, StarknetState}; 13 | 14 | use cairo_lang_sierra::extensions::gas::{ 15 | BuiltinCostWithdrawGasLibfunc, RedepositGasLibfunc, WithdrawGasLibfunc, 16 | }; 17 | use cairo_lang_sierra::extensions::NamedLibfunc; 18 | use cairo_lang_sierra_generator::db::SierraGenGroup; 19 | use cairo_lang_sierra_generator::replace_ids::{DebugReplacer, SierraIdReplacer}; 20 | use cairo_lang_starknet::contract::get_contracts_info; 21 | 22 | use clap::Parser; 23 | 24 | const CORELIB_DIR_NAME: &str = "corelib/src"; 25 | 26 | /// Command line args parser. 27 | /// Exits with 0/1 if the input is formatted correctly/incorrectly. 28 | #[derive(Parser, Debug)] 29 | #[clap(version, verbatim_doc_comment)] 30 | pub struct Args { 31 | /// The file to compile and run. 32 | pub path: String, 33 | /// In cases where gas is available, the amount of provided gas. 34 | #[arg(long)] 35 | pub available_gas: Option, 36 | /// Whether to print the memory. 37 | #[arg(long, default_value_t = false)] 38 | pub print_full_memory: bool, 39 | } 40 | 41 | pub fn main() -> anyhow::Result<()> { 42 | let args = Args::parse(); 43 | let res = run_cairo_program(&args); 44 | if let Err(e) = res { 45 | eprintln!("{e}"); 46 | std::process::exit(1); 47 | } 48 | Ok(()) 49 | } 50 | 51 | pub fn run_cairo_program(args: &Args) -> anyhow::Result { 52 | let db = &mut RootDatabase::builder().detect_corelib().build()?; 53 | let mut corelib_dir = std::env::current_exe() 54 | .unwrap_or_else(|e| panic!("Problem getting the executable path: {e:?}")); 55 | corelib_dir.pop(); 56 | corelib_dir.pop(); 57 | corelib_dir.pop(); 58 | corelib_dir.push(CORELIB_DIR_NAME); 59 | init_dev_corelib(db, corelib_dir); 60 | 61 | let main_crate_ids = setup_project(db, Path::new(&args.path))?; 62 | 63 | if DiagnosticsReporter::stderr().check(db) { 64 | anyhow::bail!("failed to compile: {}", args.path); 65 | } 66 | 67 | let mut ret_string = String::new(); 68 | 69 | let sierra_program = db 70 | .get_sierra_program(main_crate_ids.clone()) 71 | .to_option() 72 | .with_context(|| "Compilation failed without any diagnostics.")?; 73 | let replacer = DebugReplacer { db }; 74 | if args.available_gas.is_none() 75 | && sierra_program.type_declarations.iter().any(|decl| { 76 | matches!( 77 | decl.long_id.generic_id.0.as_str(), 78 | WithdrawGasLibfunc::STR_ID 79 | | BuiltinCostWithdrawGasLibfunc::STR_ID 80 | | RedepositGasLibfunc::STR_ID 81 | ) 82 | }) 83 | { 84 | anyhow::bail!("Program requires gas counter, please provide `--available_gas` argument."); 85 | } 86 | 87 | let contracts_info = get_contracts_info(db, main_crate_ids, &replacer)?; 88 | 89 | let runner = SierraCasmRunner::new( 90 | replacer.apply(&sierra_program), 91 | if args.available_gas.is_some() { 92 | Some(Default::default()) 93 | } else { 94 | None 95 | }, 96 | contracts_info, 97 | ) 98 | .with_context(|| "Failed setting up runner.")?; 99 | let result = runner 100 | .run_function_with_starknet_context( 101 | runner.find_function("::main")?, 102 | &[], 103 | args.available_gas, 104 | StarknetState::default(), 105 | ) 106 | .with_context(|| "Failed to run the function.")?; 107 | match result.value { 108 | cairo_lang_runner::RunResultValue::Success(values) => ret_string 109 | .push_str(format!("Run completed successfully, returning {values:?}").as_str()), 110 | cairo_lang_runner::RunResultValue::Panic(values) => { 111 | ret_string.push_str(format!("Run panicked with err values: {values:?}").as_str()); 112 | } 113 | } 114 | println!("{ret_string}"); 115 | Ok(ret_string) 116 | } 117 | -------------------------------------------------------------------------------- /cairo/src/ui.rs: -------------------------------------------------------------------------------- 1 | macro_rules! warn { 2 | ($fmt:literal, $ex:expr) => {{ 3 | use console::{style, Emoji}; 4 | use std::env; 5 | let formatstr = format!($fmt, $ex); 6 | if env::var("NO_EMOJI").is_ok() { 7 | println!("{} {}", style("!").red(), style(formatstr).red()); 8 | } else { 9 | println!( 10 | "{} {}", 11 | style(Emoji("⚠️ ", "!")).red(), 12 | style(formatstr).red() 13 | ); 14 | } 15 | }}; 16 | } 17 | 18 | macro_rules! success { 19 | ($fmt:literal, $ex:expr) => {{ 20 | use console::{style, Emoji}; 21 | use std::env; 22 | let formatstr = format!($fmt, $ex); 23 | if env::var("NO_EMOJI").is_ok() { 24 | println!("{} {}", style("✓").green(), style(formatstr).green()); 25 | } else { 26 | println!( 27 | "{} {}", 28 | style(Emoji("✅", "✓")).green(), 29 | style(formatstr).green() 30 | ); 31 | } 32 | }}; 33 | } 34 | -------------------------------------------------------------------------------- /cairo/src/verify.rs: -------------------------------------------------------------------------------- 1 | use crate::exercise::{Exercise, Mode, State}; 2 | use console::style; 3 | use indicatif::{ProgressBar, ProgressStyle}; 4 | use std::env; 5 | 6 | // Verify that the provided container of Exercise objects 7 | // can be compiled and run without any failures. 8 | // Any such failures will be reported to the end user. 9 | // If the Exercise being verified is a test, the verbose boolean 10 | // determines whether or not the test harness outputs are displayed. 11 | pub fn verify<'a>( 12 | exercises: impl IntoIterator, 13 | progress: (usize, usize), 14 | ) -> Result<(), &'a Exercise> { 15 | let (num_done, total) = progress; 16 | let bar = ProgressBar::new(total as u64); 17 | bar.set_style( 18 | ProgressStyle::default_bar() 19 | .template("Progress: [{bar:60.green/red}] {pos}/{len} {msg}") 20 | .progress_chars("#>-"), 21 | ); 22 | bar.set_position(num_done as u64); 23 | for exercise in exercises { 24 | let compile_result = match exercise.mode { 25 | Mode::Compile => compile_and_run_interactively(exercise), 26 | Mode::Test => compile_and_test_interactively(exercise), 27 | }; 28 | if !compile_result.unwrap_or(false) { 29 | return Err(exercise); 30 | } 31 | let percentage = num_done as f32 / total as f32 * 100.0; 32 | bar.set_message(format!("({percentage:.1} %)")); 33 | bar.inc(1); 34 | } 35 | Ok(()) 36 | } 37 | 38 | // Compile the given Exercise and run the resulting binary in an interactive mode 39 | fn compile_and_run_interactively(exercise: &Exercise) -> Result { 40 | let progress_bar = ProgressBar::new_spinner(); 41 | progress_bar.enable_steady_tick(100); 42 | 43 | progress_bar.set_message(format!("Running {exercise} exercise...")); 44 | 45 | let run_state = compile_and_run_cairo(exercise, &progress_bar)?; 46 | 47 | progress_bar.finish_and_clear(); 48 | 49 | Ok(prompt_for_completion(exercise, Some(run_state))) 50 | } 51 | 52 | // Tests the given Exercise and run the resulting binary in an interactive mode 53 | fn compile_and_test_interactively(exercise: &Exercise) -> Result { 54 | let progress_bar = ProgressBar::new_spinner(); 55 | progress_bar.enable_steady_tick(100); 56 | 57 | progress_bar.set_message(format!("Testing {exercise} exercise...")); 58 | 59 | let run_state = compile_and_test_cairo(exercise, &progress_bar)?; 60 | 61 | progress_bar.finish_and_clear(); 62 | 63 | Ok(prompt_for_completion(exercise, Some(run_state))) 64 | } 65 | 66 | // Compile the given Exercise and return an object with information 67 | // about the state of the compilation 68 | fn compile_and_run_cairo(exercise: &Exercise, progress_bar: &ProgressBar) -> Result { 69 | let compilation_result = exercise.run_cairo(); 70 | 71 | if let Err(error) = compilation_result { 72 | progress_bar.finish_and_clear(); 73 | warn!( 74 | "Compiling of {} failed! Please try again. Here's the output:", 75 | exercise 76 | ); 77 | println!("{error}"); 78 | eprintln!("{error}"); 79 | Err(()) 80 | } else { 81 | Ok(compilation_result.unwrap()) 82 | } 83 | } 84 | 85 | // Tests the given Exercise and return an object with information 86 | // about the state of the tests 87 | fn compile_and_test_cairo(exercise: &Exercise, progress_bar: &ProgressBar) -> Result { 88 | let compilation_result = exercise.test_cairo(); 89 | 90 | if let Some(error) = compilation_result.as_ref().err() { 91 | progress_bar.finish_and_clear(); 92 | warn!( 93 | "Testing of {} failed! Please try again. Here's the output:", 94 | exercise 95 | ); 96 | println!("{error}"); 97 | Err(()) 98 | } else { 99 | Ok(compilation_result.unwrap()) 100 | } 101 | } 102 | 103 | fn prompt_for_completion(exercise: &Exercise, prompt_output: Option) -> bool { 104 | let context = match exercise.state() { 105 | State::Done => return true, 106 | State::Pending(context) => context, 107 | }; 108 | 109 | match exercise.mode { 110 | Mode::Compile => success!("Successfully ran {}!", exercise), 111 | Mode::Test => success!("Successfully tested {}!", exercise), 112 | // Mode::Clippy => success!("Successfully compiled {}!", exercise), 113 | } 114 | 115 | let no_emoji = env::var("NO_EMOJI").is_ok(); 116 | 117 | let _clippy_success_msg = "The code is compiling, and Clippy is happy!"; 118 | 119 | let success_msg = match exercise.mode { 120 | Mode::Compile => "The code is compiling!", 121 | Mode::Test => "The code is compiling, and the tests pass!", 122 | // Mode::Clippy => clippy_success_msg, 123 | }; 124 | 125 | println!(); 126 | if no_emoji { 127 | println!("~*~ {success_msg} ~*~") 128 | } else { 129 | println!("🎉 🎉 {success_msg} 🎉 🎉") 130 | } 131 | println!(); 132 | 133 | if let Some(output) = prompt_output { 134 | println!("Output:"); 135 | println!("{}", separator()); 136 | println!("{output}"); 137 | println!("{}", separator()); 138 | println!(); 139 | } 140 | 141 | println!("You can keep working on this exercise,"); 142 | println!( 143 | "or jump into the next one by removing the {} comment:", 144 | style("`I AM NOT DONE`").bold() 145 | ); 146 | println!(); 147 | for context_line in context { 148 | let formatted_line = if context_line.important { 149 | format!("{}", style(context_line.line).bold()) 150 | } else { 151 | context_line.line.to_string() 152 | }; 153 | 154 | println!( 155 | "{:>2} {} {}", 156 | style(context_line.number).blue().bold(), 157 | style("|").blue(), 158 | formatted_line 159 | ); 160 | } 161 | 162 | false 163 | } 164 | 165 | fn separator() -> console::StyledObject<&'static str> { 166 | style("====================").bold() 167 | } 168 | -------------------------------------------------------------------------------- /cairo/tests/fixture/cairo/compileFail.cairo: -------------------------------------------------------------------------------- 1 | fn main() -> felt252 { 2 | hi() 3 | } 4 | -------------------------------------------------------------------------------- /cairo/tests/fixture/cairo/compilePass.cairo: -------------------------------------------------------------------------------- 1 | fn main() -> felt252 { 2 | 25 3 | } 4 | -------------------------------------------------------------------------------- /cairo/tests/fixture/cairo/info.toml: -------------------------------------------------------------------------------- 1 | [[exercises]] 2 | name = "cairoPass" 3 | path = "compilePass.cairo" 4 | mode = "compile" 5 | hint = "" 6 | 7 | [[exercises]] 8 | name = "cairoFail" 9 | path = "compileFail.cairo" 10 | mode = "compile" 11 | hint = "" 12 | 13 | [[exercises]] 14 | name = "testPass" 15 | path = "testPass.cairo" 16 | mode = "test" 17 | hint = "" 18 | 19 | [[exercises]] 20 | name = "testFail" 21 | path = "testFail.cairo" 22 | mode = "test" 23 | hint = "" -------------------------------------------------------------------------------- /cairo/tests/fixture/cairo/testFails.cairo: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_0_is_1() { 3 | assert(0 == 1, '0 should be equal to 1'); 4 | } 5 | -------------------------------------------------------------------------------- /cairo/tests/fixture/cairo/testPass.cairo: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_0_is_0() { 3 | assert(0 == 0, 'should be equal to 0'); 4 | } 5 | -------------------------------------------------------------------------------- /cairo/tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::prelude::*; 2 | use glob::glob; 3 | 4 | use std::fs::File; 5 | use std::io::Read; 6 | use std::process::Command; 7 | 8 | #[test] 9 | fn runs_without_arguments() { 10 | let mut cmd = Command::cargo_bin("starklings").unwrap(); 11 | cmd.assert().success(); 12 | } 13 | 14 | #[test] 15 | fn fails_when_in_wrong_dir() { 16 | Command::cargo_bin("starklings") 17 | .unwrap() 18 | .current_dir("tests/") 19 | .assert() 20 | .code(1); 21 | } 22 | 23 | #[test] 24 | fn reset_single_exercise() { 25 | Command::cargo_bin("starklings") 26 | .unwrap() 27 | .args(["reset", "intro1"]) 28 | .assert() 29 | .code(0); 30 | } 31 | 32 | #[test] 33 | fn reset_no_exercise() { 34 | Command::cargo_bin("starklings") 35 | .unwrap() 36 | .arg("reset") 37 | .assert() 38 | .code(1) 39 | .stderr(predicates::str::contains( 40 | "positional arguments not provided", 41 | )); 42 | } 43 | 44 | #[test] 45 | fn all_exercises_require_confirmation() { 46 | for exercise in glob("exercises/**/*.cairo").unwrap() { 47 | let path = exercise.unwrap(); 48 | if path.file_name().unwrap() == "mod.cairo" { 49 | continue; 50 | } 51 | let source = { 52 | let mut file = File::open(&path).unwrap(); 53 | let mut s = String::new(); 54 | file.read_to_string(&mut s).unwrap(); 55 | s 56 | }; 57 | source 58 | .matches("// I AM NOT DONE") 59 | .next() 60 | .unwrap_or_else(|| panic!("There should be an `I AM NOT DONE` annotation in {path:?}")); 61 | } 62 | } 63 | 64 | #[test] 65 | fn exercise_paths_should_be_in_exercise_dir() { 66 | let output = Command::cargo_bin("starklings") 67 | .unwrap() 68 | .args(["paths"]) 69 | .output() 70 | .unwrap() 71 | .stdout; 72 | let output = String::from_utf8(output).unwrap(); 73 | // println!("{:#?}", output.split("\n")); 74 | 75 | output.split('\n').for_each(|path| { 76 | if !path.is_empty() && !path.starts_with("exercises/") { 77 | panic!("Exercise {path} must be in exercises directory."); 78 | } 79 | }); 80 | } 81 | 82 | // #[test] 83 | // fn run_compile_exercise_does_not_prompt() { 84 | // Command::cargo_bin("starklings") 85 | // .unwrap() 86 | // .args(&["run", "pending_exercise"]) 87 | // .current_dir("tests/fixture/state") 88 | // .assert() 89 | // .code(0) 90 | // .stdout(predicates::str::contains("I AM NOT DONE").not()); 91 | // } 92 | 93 | // #[test] 94 | // fn run_test_exercise_does_not_prompt() { 95 | // Command::cargo_bin("starklings") 96 | // .unwrap() 97 | // .args(&["run", "pending_test_exercise"]) 98 | // .current_dir("tests/fixture/state") 99 | // .assert() 100 | // .code(0) 101 | // .stdout(predicates::str::contains("I AM NOT DONE").not()); 102 | // } 103 | 104 | // #[test] 105 | // fn run_starklings_list() { 106 | // Command::cargo_bin("starklings") 107 | // .unwrap() 108 | // .args(&["list"]) 109 | // .current_dir("tests/fixture/success") 110 | // .assert() 111 | // .success(); 112 | // } 113 | 114 | // #[test] 115 | // fn run_starklings_list_no_pending() { 116 | // Command::cargo_bin("starklings") 117 | // .unwrap() 118 | // .args(&["list"]) 119 | // .current_dir("tests/fixture/success") 120 | // .assert() 121 | // .success() 122 | // .stdout(predicates::str::contains("Pending").not()); 123 | // } 124 | 125 | // #[test] 126 | // fn run_starklings_list_both_done_and_pending() { 127 | // Command::cargo_bin("starklings") 128 | // .unwrap() 129 | // .args(&["list"]) 130 | // .current_dir("tests/fixture/state") 131 | // .assert() 132 | // .success() 133 | // .stdout(predicates::str::contains("Done").and(predicates::str::contains("Pending"))); 134 | // } 135 | 136 | // #[test] 137 | // fn run_starklings_list_without_pending() { 138 | // Command::cargo_bin("starklings") 139 | // .unwrap() 140 | // .args(&["list", "--solved"]) 141 | // .current_dir("tests/fixture/state") 142 | // .assert() 143 | // .success() 144 | // .stdout(predicates::str::contains("Pending").not()); 145 | // } 146 | 147 | // #[test] 148 | // fn run_starklings_list_without_done() { 149 | // Command::cargo_bin("starklings") 150 | // .unwrap() 151 | // .args(&["list", "--unsolved"]) 152 | // .current_dir("tests/fixture/state") 153 | // .assert() 154 | // .success() 155 | // .stdout(predicates::str::contains("Done").not()); 156 | // } 157 | 158 | #[test] 159 | fn run_cairo_single_compile_success() { 160 | Command::cargo_bin("starklings") 161 | .unwrap() 162 | .args(["run", "cairoPass"]) 163 | .current_dir("tests/fixture/cairo/") 164 | .assert() 165 | .success(); 166 | } 167 | 168 | #[test] 169 | fn run_cairo_single_test_success() { 170 | Command::cargo_bin("starklings") 171 | .unwrap() 172 | .args(["run", "testPass"]) 173 | .current_dir("tests/fixture/cairo/") 174 | .assert() 175 | .success(); 176 | } 177 | 178 | #[test] 179 | fn run_cairo_single_test_failure() { 180 | Command::cargo_bin("starklings") 181 | .unwrap() 182 | .args(["run", "testFails"]) 183 | .current_dir("tests/fixture/cairo/") 184 | .assert() 185 | .code(1); 186 | } 187 | -------------------------------------------------------------------------------- /circom/README.md: -------------------------------------------------------------------------------- 1 | `<--` - sets signal values 2 | `===` - creates constraints 3 | - Must be rank-1 (R1CS) 4 | - one side - linear 5 | - other side - quadratic 6 | 7 | `<==` - sets signal values and creates constraints 8 | -------------------------------------------------------------------------------- /circom/exrx-is-zero/README.md: -------------------------------------------------------------------------------- 1 | Using this code in the [zkREPL](https://zkrepl.dev/), complete the IsZero template. It should output 1 if input is 0 and output 0 if input is not 0. 2 | -------------------------------------------------------------------------------- /circom/exrx-is-zero/is-zero.task.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.4; 2 | 3 | template IsZero() { 4 | ... 5 | } 6 | 7 | component main = IsZero(); 8 | 9 | /* INPUT = { 10 | "in": "5" 11 | } */ 12 | -------------------------------------------------------------------------------- /circom/exrx-square/README.md: -------------------------------------------------------------------------------- 1 | Using this code in the [zkREPL](https://zkrepl.dev/), complete both constraints and add some appropriate inputs in the input section. 2 | Test that it creates a proof, and show that an incorrect proof fails. 3 | -------------------------------------------------------------------------------- /circom/exrx-square/square.task.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.4; 2 | 3 | template SquareNTimes (n) { 4 | signal input base; 5 | signal output result; 6 | 7 | signal square[n+1]; 8 | square[0] <== base; 9 | for(var i =0; i < n; i++) { 10 | // constraint 1 11 | square[i+1] <== 12 | } 13 | // constraint 2 14 | result <== 15 | } 16 | 17 | component main { public [ base ] } = SquareNTimes(2); 18 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "password-checker" 3 | version = "0.12.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | password-checker-core = { path = "core" } 8 | password-checker-methods = { path = "methods" } 9 | rand = "0.8" 10 | risc0-zkvm = { path = "../../risc0/zkvm" } 11 | 12 | [features] 13 | cuda = ["risc0-zkvm/cuda"] 14 | default = [] 15 | metal = ["risc0-zkvm/metal"] 16 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/README.md: -------------------------------------------------------------------------------- 1 | # password_checker 2 | 3 | This simple password checker is implemented in Rust. The program is implemented in two parts: a policy checker (that runs in the zkVM) and a host driver (an ordinary command-line program that uses the zkVM to run the policy checker). 4 | 5 | The policy checker accepts a password string and a salt from the host driver and checks the validity of the password. A password validity-checking function then examines the password and panics if criteria are not met. If the password meets validity criteria, execution proceeds and the zkVM appends a hash of the salted password to the journal. The journal is a readable record of all values committed by code in the zkVM; it is attached to the receipt (a record of correct execution). 6 | 7 | # Why use zkVM to run this? 8 | 9 | Our goal is to run our own password check locally without having to share our password directly with a recipient, preferring instead to share only a SHA-256 password hash. Because the validity-checking and hashing functionality runs on the zkVM, it generates a receipt that identifies which binary was executed (via the method ID), associates shared results with this particular execution (via the journal), and confirms its own integrity (via the cryptographic seal). 10 | 11 | # Project organization 12 | 13 | The main program that calls a method in the guest ZKVM is in [cli/src/main.rs](cli/src/main.rs). The code that runs inside the ZKVM is in [methods/guest/src/bin/pw_checker.rs](methods/guest/src/bin/pw_checker.rs). The rest of the project is build support. 14 | 15 | For the main RISC Zero project, see [here](https://github.com/risc0/risc0) 16 | 17 | # Run this example 18 | 19 | To build and run this example, use: 20 | 21 | ``` 22 | cargo run --release 23 | ``` 24 | 25 | # And now, some fine print 26 | 27 | This is example code meant to illustrate the fundamentals of programming with the zkVM. The password policy (and broader protocol) implemented here is intended for educational purposes only. 28 | 29 | 30 | ## Video Tutorial 31 | 32 | For a walk-through of the fundamentals of this example, check out this [excerpt from our workshop at ZK HACK III](https://www.youtube.com/watch?v=Yg_BGqj_6lg&list=PLcPzhUaCxlCgig7ofeARMPwQ8vbuD6hC5&index=5). 33 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "password-checker-core" 3 | version = "0.12.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde = "1.0" 8 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use serde::{Deserialize, Serialize}; 16 | 17 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 18 | pub struct PasswordRequest { 19 | pub password: String, 20 | pub salt: [u8; 32], 21 | } 22 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/methods/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "password-checker-methods" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | risc0-build = { path = "../../../risc0/build" } 8 | 9 | [package.metadata.risc0] 10 | methods = ["guest"] 11 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/methods/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | fn main() { 16 | risc0_build::embed_methods(); 17 | } 18 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/methods/guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pw_checker" 3 | version = "0.12.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | 8 | [dependencies] 9 | password-checker-core = { path = "../../core" } 10 | risc0-zkvm = { path = "../../../../risc0/zkvm", default-features = false, features = [ "std" ] } 11 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/methods/guest/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | 17 | use password_checker_core::PasswordRequest; 18 | use risc0_zkvm::guest::env; 19 | use risc0_zkvm::sha::{Impl, Sha256}; 20 | 21 | risc0_zkvm::guest::entry!(main); 22 | 23 | pub fn main() { 24 | let request: PasswordRequest = env::read(); 25 | 26 | let policy = PasswordPolicy { 27 | min_length: 3, 28 | max_length: 64, 29 | min_numeric: 2, 30 | min_uppercase: 2, 31 | min_lowercase: 2, 32 | min_special_chars: 1, 33 | }; 34 | 35 | if !policy.is_valid(&request.password) { 36 | panic!("Password invalid. Please try again."); 37 | } 38 | 39 | let mut salted_password = request.password.as_bytes().to_vec(); 40 | salted_password.extend(request.salt); 41 | let password_hash = Impl::hash_bytes(&salted_password[..]); 42 | 43 | env::commit(&*password_hash); 44 | env::commit(&request.salt); 45 | } 46 | 47 | struct PasswordPolicy { 48 | pub min_length: usize, 49 | pub max_length: usize, 50 | pub min_uppercase: usize, 51 | pub min_lowercase: usize, 52 | pub min_numeric: usize, 53 | pub min_special_chars: usize, 54 | } 55 | 56 | impl PasswordPolicy { 57 | pub fn is_valid(&self, pw: &str) -> bool { 58 | let metrics = PasswordMetrics::new(pw); 59 | self.correct_length(pw) 60 | && (metrics.numeric >= self.min_numeric) 61 | && (metrics.uppercase >= self.min_uppercase) 62 | && (metrics.lowercase >= self.min_lowercase) 63 | && (metrics.special >= self.min_special_chars) 64 | } 65 | 66 | fn correct_length(&self, password: &str) -> bool { 67 | password.len() > (self.min_length - 1) && password.len() < (self.max_length + 1) 68 | } 69 | } 70 | 71 | struct PasswordMetrics { 72 | pub numeric: usize, 73 | pub special: usize, 74 | pub uppercase: usize, 75 | pub lowercase: usize, 76 | } 77 | 78 | impl PasswordMetrics { 79 | pub fn new(password: &str) -> Self { 80 | let mut numeric = 0; 81 | let mut special = 0; 82 | let mut uppercase = 0; 83 | let mut lowercase = 0; 84 | for ch in password.chars() { 85 | if ch.is_ascii_digit() { 86 | numeric += 1; 87 | } 88 | if ch.is_ascii_punctuation() { 89 | special += 1; 90 | } 91 | if ch.is_ascii_uppercase() { 92 | uppercase += 1; 93 | } 94 | if ch.is_ascii_lowercase() { 95 | lowercase += 1; 96 | } 97 | } 98 | PasswordMetrics { 99 | numeric, 100 | special, 101 | uppercase, 102 | lowercase, 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/methods/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | include!(concat!(env!("OUT_DIR"), "/methods.rs")); 16 | -------------------------------------------------------------------------------- /risc0/examples/password-checker/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use password_checker_core::PasswordRequest; 16 | use password_checker_methods::PW_CHECKER_ELF; 17 | use rand::prelude::*; 18 | use risc0_zkvm::{ 19 | serde::{from_slice, to_vec}, 20 | sha::Digest, 21 | Executor, ExecutorEnv, 22 | }; 23 | 24 | fn main() { 25 | let mut rng = StdRng::from_entropy(); 26 | let mut salt = [0u8; 32]; 27 | rng.fill_bytes(&mut salt); 28 | 29 | let request = PasswordRequest { 30 | password: "S00perSecr1t!!!".into(), 31 | salt, 32 | }; 33 | 34 | let password_hash = password_checker(request); 35 | println!("Password hash is: {}", &password_hash); 36 | } 37 | 38 | fn password_checker(request: PasswordRequest) -> Digest { 39 | let env = ExecutorEnv::builder() 40 | .add_input(&to_vec(&request).unwrap()) 41 | .build(); 42 | 43 | let mut exec = Executor::from_elf(env, PW_CHECKER_ELF).unwrap(); 44 | let session = exec.run().unwrap(); 45 | 46 | let receipt = session.prove().unwrap(); 47 | 48 | from_slice(&receipt.journal).unwrap() 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use password_checker_core::PasswordRequest; 54 | 55 | #[test] 56 | fn main() { 57 | const TEST_SALT: [u8; 32] = [0u8; 32]; 58 | const TEST_PASSWORD: &str = "S00perSecr1t!!!"; 59 | 60 | let request = PasswordRequest { 61 | password: TEST_PASSWORD.into(), 62 | salt: TEST_SALT, 63 | }; 64 | 65 | super::password_checker(request); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Cargo.lock 3 | methods/guest/Cargo.lock 4 | target/ 5 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "methods", 4 | "host", 5 | "board" 6 | ] 7 | 8 | # Always optimize; building and running the guest takes much longer without optimization. 9 | [profile.dev] 10 | opt-level = 3 11 | 12 | [profile.dev.build-override] 13 | opt-level = 3 14 | 15 | [profile.release] 16 | debug = 1 17 | lto = true 18 | 19 | [profile.release.build-override] 20 | opt-level = 3 21 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/board/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "board" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | serde = {version="1.0", default-features=false} -------------------------------------------------------------------------------- /risc0/examples/sudoku/board/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] // std support is experimental 2 | 3 | const GRID_SIZE: usize = 9; 4 | const SUBGRID_SIZE: usize = 3; 5 | 6 | use serde::{Deserialize, Serialize}; 7 | 8 | #[derive(Serialize, Deserialize)] 9 | pub struct Sudoku { 10 | grid: [[u32; GRID_SIZE]; GRID_SIZE], 11 | } 12 | 13 | impl Sudoku { 14 | pub fn new(grid: [[u32; GRID_SIZE]; GRID_SIZE]) -> Self { 15 | Sudoku { grid } 16 | } 17 | 18 | pub fn new0() -> Self { 19 | Sudoku { grid: [[1; 9]; 9] } 20 | } 21 | 22 | pub fn get(&self, row: usize, col: usize) -> u32 { 23 | self.grid[row][col] 24 | } 25 | 26 | pub fn set(&mut self, row: usize, col: usize, value: u32) { 27 | self.grid[row][col] = value; 28 | } 29 | 30 | pub fn is_valid(&self) -> bool { 31 | self.check_rows() && self.check_columns() && self.check_subgrids() 32 | } 33 | 34 | // Returns true if provided sudoku is a subset of this one 35 | pub fn is_subset(&self, sub_sudoku: &Sudoku) -> bool { 36 | // Iterate over a subset 37 | for r in 0..GRID_SIZE { 38 | for c in 0..GRID_SIZE { 39 | if sub_sudoku.grid[r][c] != 0 { 40 | if sub_sudoku.grid[r][c] != self.grid[r][c] { 41 | return false; 42 | } 43 | } 44 | } 45 | } 46 | true 47 | } 48 | 49 | fn check_rows(&self) -> bool { 50 | for row in 0..GRID_SIZE { 51 | let mut seen = [false; GRID_SIZE]; 52 | for col in 0..GRID_SIZE { 53 | let value = self.get(row, col) as usize; 54 | if seen[value - 1] { 55 | return false; 56 | } 57 | seen[value - 1] = true; 58 | } 59 | } 60 | true 61 | } 62 | 63 | fn check_columns(&self) -> bool { 64 | for col in 0..GRID_SIZE { 65 | let mut seen = [false; GRID_SIZE]; 66 | for row in 0..GRID_SIZE { 67 | let value = self.get(row, col) as usize; 68 | if seen[value - 1] { 69 | return false; 70 | } 71 | seen[value - 1] = true; 72 | } 73 | } 74 | true 75 | } 76 | 77 | fn check_subgrids(&self) -> bool { 78 | for row in (0..GRID_SIZE).step_by(SUBGRID_SIZE) { 79 | for col in (0..GRID_SIZE).step_by(SUBGRID_SIZE) { 80 | if !self.check_subgrid(row, col) { 81 | return false; 82 | } 83 | } 84 | } 85 | true 86 | } 87 | 88 | fn check_subgrid(&self, start_row: usize, start_col: usize) -> bool { 89 | let mut seen = [false; GRID_SIZE]; 90 | for row in start_row..start_row + SUBGRID_SIZE { 91 | for col in start_col..start_col + SUBGRID_SIZE { 92 | let value = self.get(row, col) as usize; 93 | if seen[value - 1] { 94 | return false; 95 | } 96 | seen[value - 1] = true; 97 | } 98 | } 99 | true 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/host/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "starter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | methods = { path = "../methods" } 8 | risc0-zkvm = { version = "0.14.0" } 9 | serde = "1.0" 10 | board = {path="../board"} 11 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/host/src/main.rs: -------------------------------------------------------------------------------- 1 | use methods::{LAYOUT_ELF, LAYOUT_ID}; 2 | use risc0_zkvm::serde::to_vec; 3 | use risc0_zkvm::Prover; 4 | 5 | use board; 6 | 7 | fn main() { 8 | let solved_sudoku_arr = [ 9 | [5, 3, 4, 6, 7, 8, 9, 1, 2], 10 | [6, 7, 2, 1, 9, 5, 3, 4, 8], 11 | [1, 9, 8, 3, 4, 2, 5, 6, 7], 12 | [8, 5, 9, 7, 6, 1, 4, 2, 3], 13 | [4, 2, 6, 8, 5, 3, 7, 9, 1], 14 | [7, 1, 3, 9, 2, 4, 8, 5, 6], 15 | [9, 6, 1, 5, 3, 7, 2, 8, 4], 16 | [2, 8, 7, 4, 1, 9, 6, 3, 5], 17 | [3, 4, 5, 2, 8, 6, 1, 7, 9], 18 | ]; 19 | 20 | let partial_sudoku_arr = [ 21 | [5, 3, 4, 0, 7, 0, 9, 0, 2], 22 | [0, 0, 2, 1, 0, 5, 0, 4, 8], 23 | [1, 9, 8, 3, 4, 2, 5, 6, 0], 24 | [8, 5, 0, 7, 0, 1, 0, 2, 3], 25 | [4, 2, 6, 8, 5, 3, 7, 9, 1], 26 | [7, 1, 3, 9, 0, 4, 8, 0, 6], 27 | [0, 0, 1, 5, 3, 7, 2, 8, 0], 28 | [2, 8, 0, 4, 0, 9, 6, 3, 5], 29 | [3, 4, 5, 2, 8, 6, 1, 7, 0], 30 | ]; 31 | 32 | let mut solved_sudoku = board::Sudoku::new(solved_sudoku_arr); 33 | let mut partial_sudoku = board::Sudoku::new(partial_sudoku_arr); 34 | 35 | // Uncommenting will break it 36 | // partial_sudoku.set(0, 0, 3); 37 | 38 | // Make the prover. 39 | let mut prover = 40 | Prover::new(LAYOUT_ELF).expect("Prover should be constructed from valid ELF binary"); 41 | 42 | // Communication with the guest 43 | 44 | prover.add_input_u32_slice(&to_vec(&solved_sudoku).unwrap()); 45 | prover.add_input_u32_slice(&to_vec(&partial_sudoku).unwrap()); 46 | 47 | // Run prover & generate receipt 48 | let receipt = prover.run().expect( 49 | "Code should be provable unless it had an error or exceeded the maximum cycle limit", 50 | ); 51 | 52 | // Verify your receipt 53 | receipt.verify(&LAYOUT_ID).expect( 54 | "Code you have proven should successfully verify; did you specify the correct method ID?", 55 | ); 56 | 57 | println!( 58 | "I know the sudoku combination that solves {:?}", 59 | partial_sudoku_arr 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/methods/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "methods" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | risc0-build = { version = "0.14.0" } 8 | board = {path="../board"} 9 | 10 | [package.metadata.risc0] 11 | methods = ["guest"] 12 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/methods/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | risc0_build::embed_methods(); 3 | } 4 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/methods/guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "methods-guest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | 8 | [dependencies] 9 | # If you want to try (experimental) std support, add `features = [ "std" ]` to risc0-zkvm 10 | risc0-zkvm = { version = "0.14.0", default-features = false } 11 | board = {path="../../board"} 12 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/methods/guest/src/bin/layout.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_std] 3 | 4 | use risc0_zkvm::guest::env; 5 | risc0_zkvm::guest::entry!(main); 6 | use board; 7 | 8 | pub fn main() { 9 | let board_solved: board::Sudoku = env::read(); 10 | let board_unsolved: board::Sudoku = env::read(); 11 | 12 | // Assert solved one is correct 13 | assert!(board_solved.is_valid(), "Not solved correctly"); 14 | 15 | // Assert unsolved one is a subset of the solved one 16 | assert!( 17 | board_solved.is_subset(&board_unsolved), 18 | "Not part of the subset" 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/methods/src/lib.rs: -------------------------------------------------------------------------------- 1 | include!(concat!(env!("OUT_DIR"), "/methods.rs")); 2 | -------------------------------------------------------------------------------- /risc0/examples/sudoku/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2022-10-28" 3 | components = [ "rustfmt", "rust-src" ] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /risc0/examples/wordle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wordle" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "0.8.5" 8 | risc0-zkvm = { path = "../../risc0/zkvm" } 9 | serde = "1.0" 10 | wordle-core = { path = "core" } 11 | wordle-methods = { path = "methods" } 12 | 13 | [features] 14 | cuda = ["risc0-zkvm/cuda"] 15 | default = [] 16 | metal = ["risc0-zkvm/metal"] 17 | -------------------------------------------------------------------------------- /risc0/examples/wordle/README.md: -------------------------------------------------------------------------------- 1 | # Wordle in the zkVM 2 | 3 | ## Quick Start 4 | 5 | First, make sure [rustup](https://rustup.rs) is installed. This project uses a [nightly](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html) version of [Rust](https://doc.rust-lang.org/book/ch01-01-installation.html). The [`rust-toolchain`](rust-toolchain) file will be used by `cargo` to automatically install the correct version. 6 | 7 | To build all methods and play Wordle within the zkVM, run the following command: 8 | 9 | ``` 10 | cargo run 11 | ``` 12 | 13 | Then, start guessing 5 letter words! 14 | 15 | ## About the game 16 | 17 | The [game](https://github.com/risc0/risc0/blob/main/examples/wordle/src/main.rs) consists of two agents: a player and a server. 18 | The zkVM enforces that the server plays fairly. 19 | 20 | The game begins with the server choosing a secret word from the [wordlist](https://github.com/risc0/risc0/blob/main/examples/wordle/src/wordlist.rs), and sending the player the hash of the secret word. 21 | 22 | The player then submits guesses. After each guess, the server returns which letters from the guess match the letters in the secret word. 23 | In addition, the server returns a [receipt](https://www.risczero.com/docs/explainers/proof-system/) that attests to the logic of the letter-checking and [includes the hash](https://github.com/risc0/risc0/blob/main/examples/wordle/methods/guest/src/main.rs) of the secret word. 24 | 25 | ## Ensuring fair play 26 | 27 | The player ensures that server isn't cheating using the [`check_receipt` function](https://github.com/risc0/risc0/blob/main/examples/wordle/src/main.rs). 28 | This function first runs `receipt.verify(WORDLE_ID)` which ensures that the receipt is valid and was generated by the correct binary file. 29 | Then, the `check_receipt` function checks that the hash in the [journal contents](https://www.risczero.com/docs/explainers/zkvm/) match the hash of the secret word provided at the start of the game. 30 | -------------------------------------------------------------------------------- /risc0/examples/wordle/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wordle-core" 3 | version = "1.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | risc0-zkvm = { path = "../../../risc0/zkvm", default-features = false } 8 | serde = "1.0" 9 | -------------------------------------------------------------------------------- /risc0/examples/wordle/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use risc0_zkvm::sha::Digest; 16 | use serde::{Deserialize, Serialize}; 17 | 18 | pub const WORD_LENGTH: usize = 5; 19 | 20 | #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] 21 | pub enum LetterFeedback { 22 | Correct, 23 | Present, 24 | #[default] 25 | Miss, 26 | } 27 | 28 | #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] 29 | pub struct WordFeedback(pub [LetterFeedback; WORD_LENGTH]); 30 | 31 | #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] 32 | pub struct GameState { 33 | pub correct_word_hash: Digest, 34 | pub feedback: WordFeedback, 35 | } 36 | 37 | impl WordFeedback { 38 | pub fn game_is_won(&self) -> bool { 39 | self.0.iter().all(|x| *x == LetterFeedback::Correct) 40 | } 41 | 42 | #[cfg(not(target_os = "zkvm"))] 43 | pub fn print(&self, guess_word: &str) { 44 | print!("Your results: "); 45 | for i in 0..WORD_LENGTH { 46 | match self.0[i] { 47 | LetterFeedback::Correct => print!("\x1b[41m"), // green 48 | LetterFeedback::Present => print!("\x1b[43m"), // yellow 49 | LetterFeedback::Miss => print!("\x1b[40m"), // black 50 | } 51 | print!("{:}", guess_word.chars().nth(i).unwrap()); 52 | } 53 | println!("\x1b[0m"); 54 | println!(""); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /risc0/examples/wordle/methods/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wordle-methods" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | risc0-build = { path = "../../../risc0/build" } 8 | 9 | [package.metadata.risc0] 10 | methods = ["guest"] 11 | -------------------------------------------------------------------------------- /risc0/examples/wordle/methods/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | fn main() { 16 | risc0_build::embed_methods(); 17 | } 18 | -------------------------------------------------------------------------------- /risc0/examples/wordle/methods/guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wordle" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | 8 | [dependencies] 9 | risc0-zkvm = { path = "../../../../risc0/zkvm", default-features = false, features = ["std"] } 10 | wordle-core = { path = "../../core" } 11 | -------------------------------------------------------------------------------- /risc0/examples/wordle/methods/guest/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | 17 | use risc0_zkvm::guest::env; 18 | use risc0_zkvm::sha::{Impl, Sha256}; 19 | use wordle_core::{GameState, LetterFeedback, WordFeedback, WORD_LENGTH}; 20 | 21 | risc0_zkvm::guest::entry!(main); 22 | 23 | pub fn main() { 24 | let secret: String = env::read(); 25 | let guess: String = env::read(); 26 | 27 | assert_eq!( 28 | secret.chars().count(), 29 | WORD_LENGTH, 30 | "secret must have length 5!" 31 | ); 32 | 33 | assert_eq!( 34 | guess.chars().count(), 35 | WORD_LENGTH, 36 | "guess must have length 5!" 37 | ); 38 | 39 | let mut feedback: WordFeedback = WordFeedback::default(); 40 | 41 | // to avoid false positive partial matches, create a pool of only letters 42 | // that didn't have an exact match 43 | let mut secret_unmatched: String = String::from(""); 44 | 45 | for i in 0..WORD_LENGTH { 46 | if secret.as_bytes()[i] != guess.as_bytes()[i] { 47 | secret_unmatched.push(secret.as_bytes()[i] as char); 48 | } 49 | } 50 | 51 | // second round for distinguishing partial matches from misses 52 | for i in 0..WORD_LENGTH { 53 | feedback.0[i] = if secret.as_bytes()[i] == guess.as_bytes()[i] { 54 | LetterFeedback::Correct 55 | } else if secret_unmatched.as_bytes().contains(&guess.as_bytes()[i]) { 56 | LetterFeedback::Present 57 | } else { 58 | LetterFeedback::Miss 59 | } 60 | } 61 | 62 | let correct_word_hash = *Impl::hash_bytes(&secret.as_bytes()); 63 | let game_state = GameState { 64 | correct_word_hash, 65 | feedback, 66 | }; 67 | env::commit(&game_state); 68 | } 69 | -------------------------------------------------------------------------------- /risc0/examples/wordle/methods/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 RISC Zero, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | include!(concat!(env!("OUT_DIR"), "/methods.rs")); 16 | -------------------------------------------------------------------------------- /rust/.clog.toml: -------------------------------------------------------------------------------- 1 | [clog] 2 | 3 | repository = "https://github.com/rust-lang/rustlings" 4 | changelog = "CHANGELOG.md" -------------------------------------------------------------------------------- /rust/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.rs] 4 | end_of_line = lf 5 | insert_final_newfile = true 6 | indent_style = space 7 | indent_size = 4 8 | -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | target/ 3 | **/*.rs.bk 4 | .DS_Store 5 | *.pdb 6 | exercises/clippy/Cargo.toml 7 | exercises/clippy/Cargo.lock 8 | .idea 9 | .vscode 10 | *.iml 11 | -------------------------------------------------------------------------------- /rust/.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: /workspace/zustlings/install.sh 3 | command: /workspace/.cargo/bin/zustlings watch 4 | 5 | vscode: 6 | extensions: 7 | - rust-lang.rust@0.7.8:CvNqMTgDdt3UXt+6BCDTVg== 8 | -------------------------------------------------------------------------------- /rust/.replit: -------------------------------------------------------------------------------- 1 | language = "rust" 2 | run = "[ -x ~/.cargo/bin/zustlings ] && ~/.cargo/bin/zustlings watch || ./install.sh" 3 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zustlings" 3 | version = "4.7.0" 4 | authors = ["mokou ", "Carol (Nichols || Goulding) "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | argh = "0.1.4" 9 | indicatif = "0.10.3" 10 | console = "0.7.7" 11 | notify = "4.0.15" 12 | toml = "0.4.10" 13 | regex = "1.1.6" 14 | serde = { version = "1.0.10", features = ["derive"] } 15 | 16 | [[bin]] 17 | name = "zustlings" 18 | path = "src/main.rs" 19 | 20 | [dev-dependencies] 21 | assert_cmd = "0.11.0" 22 | predicates = "1.0.1" 23 | glob = "0.3.0" 24 | 25 | [features] 26 | exercises = [] 27 | -------------------------------------------------------------------------------- /rust/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Carol (Nichols || Goulding) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /rust/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Installing Zustlings 3 | 4 | We have a customised version of rustlings 5 | 6 | 1. Make sure you have rust installed 7 | 2. In this directory run 8 | 9 | `cargo install --force --path .` 10 | 11 | 12 | 13 | ## Doing exercises 14 | 15 | Run 16 | 17 | 18 | `zustlings homework n` 19 | Where n is the number of the homework you are doing, i.e. 20 | 21 | `zustlings homework 4` 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/functions/README.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | Here, you'll learn how to write functions and how Rust's compiler can trace things way back. 4 | 5 | ## Further information 6 | 7 | - [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) 8 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/functions/functions1.rs: -------------------------------------------------------------------------------- 1 | // functions1.rs 2 | // Make me compile! Execute `zustlings hint functions1` for hints :) 3 | 4 | // I AM NOT DONE 5 | 6 | 7 | fn main() { 8 | call_me(); 9 | } 10 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/functions/functions2.rs: -------------------------------------------------------------------------------- 1 | // functions2.rs 2 | // Make me compile! Execute `zustlings hint functions2` for hints :) 3 | 4 | // I AM NOT DONE 5 | 6 | fn main() { 7 | call_this(3); 8 | } 9 | 10 | fn call_this(num:) { 11 | for i in 0..num { 12 | println!("Loop! number {}", i + 1); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/functions/functions3.rs: -------------------------------------------------------------------------------- 1 | // functions3.rs 2 | // Make me compile! Execute `zustlings hint functions3` for hints :) 3 | 4 | // I AM NOT DONE 5 | 6 | fn main() { 7 | call_this(); 8 | } 9 | 10 | fn call_this(num: u32) { 11 | for i in 0..num { 12 | println!("Loop now {}", i + 1); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/functions/functions4.rs: -------------------------------------------------------------------------------- 1 | // functions4.rs 2 | // Make me compile! Execute `zustlings hint functions4` for hints :) 3 | 4 | // This store is having a sale where if the price is an even number, you get 5 | // 10 Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. 6 | 7 | // I AM NOT DONE 8 | 9 | fn main() { 10 | let original_price = 51; 11 | println!("Your sale price is {}", sale_price(original_price)); 12 | } 13 | 14 | fn sale_price(price: i32) -> { 15 | if is_even(price) { 16 | price - 10 17 | } else { 18 | price - 3 19 | } 20 | } 21 | 22 | fn is_even(num: i32) -> bool { 23 | num % 2 == 0 24 | } 25 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/functions/functions5.rs: -------------------------------------------------------------------------------- 1 | // functions5.rs 2 | // Make me compile! Execute `zustlings hint functions5` for hints :) 3 | 4 | // I AM NOT DONE 5 | 6 | fn main() { 7 | let answer = square(3); 8 | println!("The answer is {}", answer); 9 | } 10 | 11 | fn square(num: i32) -> i32 { 12 | num * num; 13 | } 14 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/functions/mod.rs: -------------------------------------------------------------------------------- 1 | mod functions1; 2 | mod functions2; 3 | mod functions3; 4 | mod functions4; 5 | mod functions5; 6 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/if/README.md: -------------------------------------------------------------------------------- 1 | # If 2 | 3 | `if`, the most basic type of control flow, is what you'll learn here. 4 | 5 | ## Further information 6 | 7 | - [Control Flow - if expressions](https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions) 8 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/if/if1.rs: -------------------------------------------------------------------------------- 1 | // if1.rs 2 | 3 | // I AM NOT DONE 4 | 5 | pub fn bigger(a: i32, b: i32) -> i32 { 6 | // Complete this function to return the bigger number! 7 | // Do not use: 8 | // - another function call 9 | // - additional variables 10 | // Execute `zustlings hint if1` for hints 11 | 12 | } 13 | 14 | // Don't mind this for now :) 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | 19 | #[test] 20 | fn ten_is_bigger_than_eight() { 21 | assert_eq!(10, bigger(10, 8)); 22 | } 23 | 24 | #[test] 25 | fn fortytwo_is_bigger_than_thirtytwo() { 26 | assert_eq!(42, bigger(32, 42)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/if/if2.rs: -------------------------------------------------------------------------------- 1 | // if2.rs 2 | 3 | // Step 1: Make me compile! 4 | // Step 2: Get the bar_for_fuzz and default_to_baz tests passing! 5 | // Execute the command `zustlings hint if2` if you want a hint :) 6 | 7 | // I AM NOT DONE 8 | 9 | pub fn fizz_if_foo(fizzish: &str) -> &str { 10 | if fizzish == "fizz" { 11 | "foo" 12 | } 13 | else { 14 | 1 15 | } 16 | } 17 | 18 | // No test changes needed! 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn foo_for_fizz() { 25 | assert_eq!(fizz_if_foo("fizz"), "foo") 26 | } 27 | 28 | #[test] 29 | fn bar_for_fuzz() { 30 | assert_eq!(fizz_if_foo("fuzz"), "bar") 31 | } 32 | 33 | #[test] 34 | fn default_to_baz() { 35 | assert_eq!(fizz_if_foo("literally anything"), "baz") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/if/mod.rs: -------------------------------------------------------------------------------- 1 | mod if1; 2 | mod if2; 3 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/README.md: -------------------------------------------------------------------------------- 1 | # Primitive Types 2 | 3 | Rust has a couple of basic types that are directly implemented into the 4 | compiler. In this section, we'll go through the most important ones. 5 | 6 | ## Further information 7 | 8 | - [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html) 9 | - [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html) 10 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/mod.rs: -------------------------------------------------------------------------------- 1 | mod primitive_types1; 2 | mod primitive_types2; 3 | mod primitive_types3; 4 | mod primitive_types4; 5 | mod primitive_types5; 6 | mod primitive_types6; 7 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/primitive_types1.rs: -------------------------------------------------------------------------------- 1 | // primitive_types1.rs 2 | // Fill in the rest of the line that has code missing! 3 | // No hints, there's no tricks, just get used to typing these :) 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() { 8 | // Booleans (`bool`) 9 | 10 | let is_morning = true; 11 | if is_morning { 12 | println!("Good morning!"); 13 | } 14 | 15 | let // Finish the rest of this line like the example! Or make it be false! 16 | if is_evening { 17 | println!("Good evening!"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/primitive_types2.rs: -------------------------------------------------------------------------------- 1 | // primitive_types2.rs 2 | // Fill in the rest of the line that has code missing! 3 | // No hints, there's no tricks, just get used to typing these :) 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() { 8 | // Characters (`char`) 9 | 10 | // Note the _single_ quotes, these are different from the double quotes 11 | // you've been seeing around. 12 | let my_first_initial = 'C'; 13 | if my_first_initial.is_alphabetic() { 14 | println!("Alphabetical!"); 15 | } else if my_first_initial.is_numeric() { 16 | println!("Numerical!"); 17 | } else { 18 | println!("Neither alphabetic nor numeric!"); 19 | } 20 | 21 | let // Finish this line like the example! What's your favorite character? 22 | // Try a letter, try a number, try a special character, try a character 23 | // from a different language than your own, try an emoji! 24 | if your_character.is_alphabetic() { 25 | println!("Alphabetical!"); 26 | } else if your_character.is_numeric() { 27 | println!("Numerical!"); 28 | } else { 29 | println!("Neither alphabetic nor numeric!"); 30 | } 31 | } -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/primitive_types3.rs: -------------------------------------------------------------------------------- 1 | // primitive_types3.rs 2 | // Create an array with at least 100 elements in it where the ??? is. 3 | // Execute `zustlings hint primitive_types3` for hints! 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() { 8 | let a = ??? 9 | 10 | if a.len() >= 100 { 11 | println!("Wow, that's a big array!"); 12 | } else { 13 | println!("Meh, I eat arrays like that for breakfast."); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/primitive_types4.rs: -------------------------------------------------------------------------------- 1 | // primitive_types4.rs 2 | // Get a slice out of Array a where the ??? is so that the test passes. 3 | // Execute `zustlings hint primitive_types4` for hints!! 4 | 5 | // I AM NOT DONE 6 | 7 | #[test] 8 | fn slice_out_of_array() { 9 | let a = [1, 2, 3, 4, 5]; 10 | 11 | let nice_slice = ??? 12 | 13 | assert_eq!([2, 3, 4], nice_slice) 14 | } 15 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/primitive_types5.rs: -------------------------------------------------------------------------------- 1 | // primitive_types5.rs 2 | // Destructure the `cat` tuple so that the println will work. 3 | // Execute `zustlings hint primitive_types5` for hints! 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() { 8 | let cat = ("Furry McFurson", 3.5); 9 | let /* your pattern here */ = cat; 10 | 11 | println!("{} is {} years old.", name, age); 12 | } 13 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/primitive_types/primitive_types6.rs: -------------------------------------------------------------------------------- 1 | // primitive_types6.rs 2 | // Use a tuple index to access the second element of `numbers`. 3 | // You can put the expression for the second element where ??? is so that the test passes. 4 | // Execute `zustlings hint primitive_types6` for hints! 5 | 6 | // I AM NOT DONE 7 | 8 | #[test] 9 | fn indexing_tuple() { 10 | let numbers = (1, 2, 3); 11 | // Replace below ??? with the tuple indexing syntax. 12 | let second = ???; 13 | 14 | assert_eq!(2, second, 15 | "This is not the 2nd number in the tuple!") 16 | } 17 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/strings/README.md: -------------------------------------------------------------------------------- 1 | # Strings 2 | 3 | Rust has two string types, a string slice (`&str`) and an owned string (`String`). 4 | We're not going to dictate when you should use which one, but we'll show you how 5 | to identify and create them, as well as use them. 6 | 7 | ## Further information 8 | 9 | - [Strings](https://doc.rust-lang.org/book/ch08-02-strings.html) 10 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/strings/mod.rs: -------------------------------------------------------------------------------- 1 | mod strings1; 2 | mod strings2; 3 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/strings/strings1.rs: -------------------------------------------------------------------------------- 1 | // strings1.rs 2 | // Make me compile without changing the function signature! 3 | // Execute `zustlings hint strings1` for hints ;) 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() { 8 | let answer = current_favorite_course(); 9 | println!("My course is {}", answer); 10 | } 11 | 12 | fn current_favorite_course() -> String { 13 | "Solana" 14 | } 15 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/strings/strings2.rs: -------------------------------------------------------------------------------- 1 | // strings2.rs 2 | // Make me compile without changing the function signature! 3 | // Execute `zustlings hint strings2` for hints :) 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() { 8 | let word = String::from("green"); // Try not changing this line :) 9 | if is_a_color_word(word) { 10 | println!("That is a color word I know!"); 11 | } else { 12 | println!("That is not a color word I know."); 13 | } 14 | } 15 | 16 | fn is_a_color_word(attempt: &str) -> bool { 17 | attempt == "green" || attempt == "blue" || attempt == "red" 18 | } 19 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/README.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | In Rust, variables are immutable by default. 4 | When a variable is immutable, once a value is bound to a name, you can’t change that value. 5 | You can make them mutable by adding mut in front of the variable name. 6 | 7 | ## Further information 8 | 9 | - [Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) 10 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/mod.rs: -------------------------------------------------------------------------------- 1 | mod variables1; 2 | mod variables2; 3 | mod variables3; 4 | mod variables4; 5 | mod variables5; 6 | mod variables6; 7 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/variables1.rs: -------------------------------------------------------------------------------- 1 | // variables1.rs 2 | // Make me compile! 3 | // Execute the command `zustlings hint variables1` if you want a hint :) 4 | 5 | // I AM NOT DONE 6 | 7 | fn main() { 8 | y = 5; 9 | println!("y has the value {}", y); 10 | } 11 | -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/variables2.rs: -------------------------------------------------------------------------------- 1 | // variables2.rs 2 | // Make me compile! Execute the command `zustlings hint variables2` if you want a hint :) 3 | 4 | // I AM NOT DONE 5 | 6 | fn main() { 7 | let x; 8 | if x == 10 { 9 | println!("x is ten!"); 10 | } else { 11 | println!("x is not ten!"); 12 | } 13 | } -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/variables3.rs: -------------------------------------------------------------------------------- 1 | // variables3.rs 2 | // Make me compile! Execute the command `zustlings hint variables3` if you want a hint :) 3 | 4 | // I AM NOT DONE 5 | 6 | fn main() { 7 | let x: i32; 8 | println!("Number {}", x); 9 | } -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/variables4.rs: -------------------------------------------------------------------------------- 1 | // variables4.rs 2 | // Make me compile! Execute the command `zustlings hint variables4` if you want a hint :) 3 | 4 | // I AM NOT DONE 5 | 6 | fn main() { 7 | let x = 3; 8 | println!("Number {}", x); 9 | x = 5; // don't change this line 10 | println!("Number {}", x); 11 | } -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/variables5.rs: -------------------------------------------------------------------------------- 1 | // variables5.rs 2 | // Make me compile! Execute the command `zustlings hint variables5` if you want a hint :) 3 | 4 | // I AM NOT DONE 5 | 6 | fn main() { 7 | let number = "T-H-R-E-E"; // don't change this line 8 | println!("Spell a Number : {}", number); 9 | number = 3; // don't rename this variable 10 | println!("Number plus two is : {}", number + 2); 11 | } -------------------------------------------------------------------------------- /rust/homeworks/homework4/variables/variables6.rs: -------------------------------------------------------------------------------- 1 | // variables6.rs 2 | // Make me compile! Execute the command `zustlings hint variables6` if you want a hint :) 3 | 4 | // I AM NOT DONE 5 | 6 | const NUMBER = 3; 7 | fn main() { 8 | println!("Number {}", NUMBER); 9 | } 10 | -------------------------------------------------------------------------------- /rust/homeworks/mod.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | mod if; 3 | mod intro; 4 | mod primitive_types; 5 | mod strings; 6 | mod variables; 7 | -------------------------------------------------------------------------------- /rust/install.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | #Requires -Version 5 4 | param($path = "$pwd/zustlings") 5 | 6 | Write-Host "Let's get you set up with Zustlings!" 7 | 8 | Write-Host "Checking requirements..." 9 | if (Get-Command git -ErrorAction SilentlyContinue) { 10 | Write-Host "SUCCESS: Git is installed" 11 | } else { 12 | Write-Host "WARNING: Git does not seem to be installed." 13 | Write-Host "Please download Git using your package manager or over https://git-scm.com/!" 14 | exit 1 15 | } 16 | 17 | if (Get-Command rustc -ErrorAction SilentlyContinue) { 18 | Write-Host "SUCCESS: Rust is installed" 19 | } else { 20 | Write-Host "WARNING: Rust does not seem to be installed." 21 | Write-Host "Please download Rust using https://rustup.rs!" 22 | exit 1 23 | } 24 | 25 | if (Get-Command cargo -ErrorAction SilentlyContinue) { 26 | Write-Host "SUCCESS: Cargo is installed" 27 | } else { 28 | Write-Host "WARNING: Cargo does not seem to be installed." 29 | Write-Host "Please download Rust and Cargo using https://rustup.rs!" 30 | exit 1 31 | } 32 | 33 | # Function that compares two versions strings v1 and v2 given in arguments (e.g 1.31 and 1.33.0). 34 | # Returns 1 if v1 > v2, 0 if v1 == v2, 2 if v1 < v2. 35 | function vercomp($v1, $v2) { 36 | if ($v1 -eq $v2) { 37 | return 0 38 | } 39 | 40 | $v1 = $v1.Replace(".", "0") 41 | $v2 = $v2.Replace(".", "0") 42 | if ($v1.Length -gt $v2.Length) { 43 | $v2 = $v2.PadRight($v1.Length, "0") 44 | } else { 45 | $v1 = $v1.PadRight($v2.Length, "0") 46 | } 47 | 48 | if ($v1 -gt $v2) { 49 | return 1 50 | } else { 51 | return 2 52 | } 53 | } 54 | 55 | $rustVersion = $(rustc --version).Split(" ")[1] 56 | $minRustVersion = "1.39" 57 | if ((vercomp $rustVersion $minRustVersion) -eq 2) { 58 | Write-Host "WARNING: Rust version is too old: $rustVersion - needs at least $minRustVersion" 59 | Write-Host "Please update Rust with 'rustup update'" 60 | exit 1 61 | } else { 62 | Write-Host "SUCCESS: Rust is up to date" 63 | } 64 | 65 | Write-Host "Cloning Zustlings at $path" 66 | git clone -q https://github.com/rust-lang/rustlings $path 67 | if (!($LASTEXITCODE -eq 0)) { 68 | exit 1 69 | } 70 | 71 | # UseBasicParsing is deprecated, pwsh 6 or above will automatically use it, 72 | # but anyone running pwsh 5 will have to pass the argument. 73 | $version = Invoke-WebRequest -UseBasicParsing https://api.github.com/repos/rust-lang/rustlings/releases/latest ` 74 | | ConvertFrom-Json | Select-Object -ExpandProperty tag_name 75 | 76 | Write-Host "Checking out version $version..." 77 | Set-Location $path 78 | git checkout -q tags/$version 79 | 80 | Write-Host "Installing the 'zustlings' executable..." 81 | cargo install --force --path . 82 | if (!(Get-Command zustlings -ErrorAction SilentlyContinue)) { 83 | Write-Host "WARNING: Please check that you have '~/.cargo/bin' in your PATH environment variable!" 84 | } 85 | 86 | # Checking whether Clippy is installed. 87 | # Due to a bug in Cargo, this must be done with Rustup: https://github.com/rust-lang/rustup/issues/1514 88 | $clippy = (rustup component list | Select-String "clippy" | Select-String "installed") | Out-String 89 | if (!$clippy) { 90 | Write-Host "Installing the 'cargo-clippy' executable..." 91 | rustup component add clippy 92 | } 93 | 94 | Write-Host "All done! Run 'zustlings' to get started." 95 | -------------------------------------------------------------------------------- /rust/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | echo "Let's get you set up with Zustlings!" 5 | 6 | echo "Checking requirements..." 7 | if [ -x "$(command -v git)" ] 8 | then 9 | echo "SUCCESS: Git is installed" 10 | else 11 | echo "ERROR: Git does not seem to be installed." 12 | echo "Please download Git using your package manager or over https://git-scm.com/!" 13 | exit 1 14 | fi 15 | 16 | if [ -x "$(command -v cc)" ] 17 | then 18 | echo "SUCCESS: cc is installed" 19 | else 20 | echo "ERROR: cc does not seem to be installed." 21 | echo "Please download (g)cc using your package manager." 22 | echo "OSX: xcode-select --install" 23 | echo "Deb: sudo apt install gcc" 24 | echo "Yum: sudo yum -y install gcc" 25 | exit 1 26 | fi 27 | 28 | if [ -x "$(command -v rustc)" ] 29 | then 30 | echo "SUCCESS: Rust is installed" 31 | else 32 | echo "ERROR: Rust does not seem to be installed." 33 | echo "Please download Rust using https://rustup.rs!" 34 | exit 1 35 | fi 36 | 37 | if [ -x "$(command -v cargo)" ] 38 | then 39 | echo "SUCCESS: Cargo is installed" 40 | else 41 | echo "ERROR: Cargo does not seem to be installed." 42 | echo "Please download Rust and Cargo using https://rustup.rs!" 43 | exit 1 44 | fi 45 | 46 | # Look up python installations, starting with 3 with a fallback of 2 47 | if [ -x "$(command -v python3)" ] 48 | then 49 | PY="$(command -v python3)" 50 | elif [ -x "$(command -v python)" ] 51 | then 52 | PY="$(command -v python)" 53 | elif [ -x "$(command -v python2)" ] 54 | then 55 | PY="$(command -v python2)" 56 | else 57 | echo "ERROR: No working python installation was found" 58 | echo "Please install python and add it to the PATH variable" 59 | exit 1 60 | fi 61 | 62 | # Function that compares two versions strings v1 and v2 given in arguments (e.g 1.31 and 1.33.0). 63 | # Returns 1 if v1 > v2, 0 if v1 == v2, 2 if v1 < v2. 64 | function vercomp() { 65 | if [[ $1 == $2 ]] 66 | then 67 | return 0 68 | fi 69 | v1=( ${1//./ } ) 70 | v2=( ${2//./ } ) 71 | len1=${#v1[@]} 72 | len2=${#v2[@]} 73 | max_len=$len1 74 | if [[ $max_len -lt $len2 ]] 75 | then 76 | max_len=$len2 77 | fi 78 | for i in `seq 0 $max_len` 79 | do 80 | # Fill empty fields with zeros in v1 81 | if [ -z "${v1[$i]}" ] 82 | then 83 | v1[$i]=0 84 | fi 85 | # And in v2 86 | if [ -z "${v2[$i]}" ] 87 | then 88 | v2[$i]=0 89 | fi 90 | if [ ${v1[$i]} -gt ${v2[$i]} ] 91 | then 92 | return 1 93 | fi 94 | if [ ${v1[$i]} -lt ${v2[$i]} ] 95 | then 96 | return 2 97 | fi 98 | done 99 | return 0 100 | } 101 | 102 | RustVersion=$(rustc --version | cut -d " " -f 2) 103 | MinRustVersion=1.39 104 | vercomp "$RustVersion" $MinRustVersion || ec=$? 105 | if [ ${ec:-0} -eq 2 ] 106 | then 107 | echo "ERROR: Rust version is too old: $RustVersion - needs at least $MinRustVersion" 108 | echo "Please update Rust with 'rustup update'" 109 | exit 1 110 | else 111 | echo "SUCCESS: Rust is up to date" 112 | fi 113 | 114 | Path=${1:-zustlings/} 115 | echo "Cloning Zustlings at $Path..." 116 | git clone -q https://github.com/rust-lang/rustlings "$Path" 117 | 118 | cd "$Path" 119 | 120 | Version=$(curl -s https://api.github.com/repos/rust-lang/rustlings/releases/latest | ${PY} -c "import json,sys;obj=json.load(sys.stdin);print(obj['tag_name']);") 121 | CargoBin="${CARGO_HOME:-$HOME/.cargo}/bin" 122 | 123 | if [[ -z ${Version} ]] 124 | then 125 | echo "The latest tag version could not be fetched remotely." 126 | echo "Using the local git repository..." 127 | Version=$(ls -tr .git/refs/tags/ | tail -1) 128 | if [[ -z ${Version} ]] 129 | then 130 | echo "No valid tag version found" 131 | echo "Zustlings will be installed using the main branch" 132 | Version="main" 133 | else 134 | Version="tags/${Version}" 135 | fi 136 | else 137 | Version="tags/${Version}" 138 | fi 139 | 140 | echo "Checking out version $Version..." 141 | git checkout -q ${Version} 142 | 143 | echo "Installing the 'zustlings' executable..." 144 | cargo install --force --path . 145 | 146 | if ! [ -x "$(command -v zustlings)" ] 147 | then 148 | echo "WARNING: Please check that you have '$CargoBin' in your PATH environment variable!" 149 | fi 150 | 151 | # Checking whether Clippy is installed. 152 | # Due to a bug in Cargo, this must be done with Rustup: https://github.com/rust-lang/rustup/issues/1514 153 | Clippy=$(rustup component list | grep "clippy" | grep "installed") 154 | if [ -z "$Clippy" ] 155 | then 156 | echo "Installing the 'cargo-clippy' executable..." 157 | rustup component add clippy 158 | fi 159 | 160 | echo "All done! Run 'zustlings' to get started." 161 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "exercises")] 2 | #[path = "../exercises/mod.rs"] 3 | mod exercises; 4 | -------------------------------------------------------------------------------- /rust/src/run.rs: -------------------------------------------------------------------------------- 1 | use crate::exercise::{Exercise, Mode}; 2 | use crate::verify::test; 3 | use indicatif::ProgressBar; 4 | 5 | // Invoke the rust compiler on the path of the given exercise, 6 | // and run the ensuing binary. 7 | // The verbose argument helps determine whether or not to show 8 | // the output from the test harnesses (if the mode of the exercise is test) 9 | pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> { 10 | match exercise.mode { 11 | Mode::Test => test(exercise, verbose)?, 12 | Mode::Compile => compile_and_run(exercise)?, 13 | Mode::Clippy => compile_and_run(exercise)?, 14 | } 15 | Ok(()) 16 | } 17 | 18 | // Invoke the rust compiler on the path of the given exercise 19 | // and run the ensuing binary. 20 | // This is strictly for non-test binaries, so output is displayed 21 | fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { 22 | let progress_bar = ProgressBar::new_spinner(); 23 | progress_bar.set_message(format!("Compiling {}...", exercise).as_str()); 24 | progress_bar.enable_steady_tick(100); 25 | 26 | 27 | println!("Compiling: {:?}", exercise.name); 28 | 29 | let compilation_result = exercise.compile(); 30 | let compilation = match compilation_result { 31 | Ok(compilation) => compilation, 32 | Err(output) => { 33 | progress_bar.finish_and_clear(); 34 | warn!( 35 | "Compilation of {} failed!, Compiler error message:\n", 36 | exercise 37 | ); 38 | println!("{}", output.stderr); 39 | return Err(()); 40 | } 41 | }; 42 | 43 | progress_bar.set_message(format!("Running {}...", exercise).as_str()); 44 | let result = compilation.run(); 45 | progress_bar.finish_and_clear(); 46 | 47 | match result { 48 | Ok(output) => { 49 | println!("{}", output.stdout); 50 | success!("Successfully ran {}", exercise); 51 | Ok(()) 52 | } 53 | Err(output) => { 54 | println!("{}", output.stdout); 55 | println!("{}", output.stderr); 56 | 57 | warn!("Ran {} with errors", exercise); 58 | Err(()) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /rust/src/ui.rs: -------------------------------------------------------------------------------- 1 | macro_rules! warn { 2 | ($fmt:literal, $ex:expr) => {{ 3 | use console::{style, Emoji}; 4 | use std::env; 5 | let formatstr = format!($fmt, $ex); 6 | if env::var("NO_EMOJI").is_ok() { 7 | println!("{} {}", style("!").red(), style(formatstr).red()); 8 | } else { 9 | println!( 10 | "{} {}", 11 | style(Emoji("⚠️ ", "!")).red(), 12 | style(formatstr).red() 13 | ); 14 | } 15 | }}; 16 | } 17 | 18 | macro_rules! success { 19 | ($fmt:literal, $ex:expr) => {{ 20 | use console::{style, Emoji}; 21 | use std::env; 22 | let formatstr = format!($fmt, $ex); 23 | if env::var("NO_EMOJI").is_ok() { 24 | println!("{} {}", style("✓").green(), style(formatstr).green()); 25 | } else { 26 | println!( 27 | "{} {}", 28 | style(Emoji("✅", "✓")).green(), 29 | style(formatstr).green() 30 | ); 31 | } 32 | }}; 33 | } 34 | -------------------------------------------------------------------------------- /rust/tests/fixture/failure/compFailure.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let 3 | } -------------------------------------------------------------------------------- /rust/tests/fixture/failure/compNoExercise.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | } 3 | -------------------------------------------------------------------------------- /rust/tests/fixture/failure/info.toml: -------------------------------------------------------------------------------- 1 | [[exercises]] 2 | name = "compFailure" 3 | path = "compFailure.rs" 4 | mode = "compile" 5 | hint = "" 6 | 7 | [[exercises]] 8 | name = "testFailure" 9 | path = "testFailure.rs" 10 | mode = "test" 11 | hint = "Hello!" 12 | -------------------------------------------------------------------------------- /rust/tests/fixture/failure/testFailure.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn passing() { 3 | asset!(true); 4 | } 5 | -------------------------------------------------------------------------------- /rust/tests/fixture/failure/testNotPassed.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn not_passing() { 3 | assert!(false); 4 | } 5 | -------------------------------------------------------------------------------- /rust/tests/fixture/state/finished_exercise.rs: -------------------------------------------------------------------------------- 1 | // fake_exercise 2 | 3 | fn main() { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /rust/tests/fixture/state/info.toml: -------------------------------------------------------------------------------- 1 | [[exercises]] 2 | name = "pending_exercise" 3 | path = "pending_exercise.rs" 4 | mode = "compile" 5 | hint = """""" 6 | 7 | [[exercises]] 8 | name = "pending_test_exercise" 9 | path = "pending_test_exercise.rs" 10 | mode = "test" 11 | hint = """""" 12 | 13 | [[exercises]] 14 | name = "finished_exercise" 15 | path = "finished_exercise.rs" 16 | mode = "compile" 17 | hint = """""" 18 | 19 | -------------------------------------------------------------------------------- /rust/tests/fixture/state/pending_exercise.rs: -------------------------------------------------------------------------------- 1 | // fake_exercise 2 | 3 | // I AM NOT DONE 4 | 5 | fn main() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /rust/tests/fixture/state/pending_test_exercise.rs: -------------------------------------------------------------------------------- 1 | // I AM NOT DONE 2 | 3 | #[test] 4 | fn it_works() {} 5 | -------------------------------------------------------------------------------- /rust/tests/fixture/success/compSuccess.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | } 3 | -------------------------------------------------------------------------------- /rust/tests/fixture/success/info.toml: -------------------------------------------------------------------------------- 1 | [[exercises]] 2 | name = "compSuccess" 3 | path = "compSuccess.rs" 4 | mode = "compile" 5 | hint = """""" 6 | 7 | [[exercises]] 8 | name = "testSuccess" 9 | path = "testSuccess.rs" 10 | mode = "test" 11 | hint = """""" 12 | -------------------------------------------------------------------------------- /rust/tests/fixture/success/testSuccess.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn passing() { 3 | println!("THIS TEST TOO SHALL PASS"); 4 | assert!(true); 5 | } 6 | --------------------------------------------------------------------------------