Skip to content

Commit

Permalink
Feat: hashing (#161)
Browse files Browse the repository at this point in the history
* Added hashing

* Fixed typo and used ANCHOR

* added hash_trait

* added hash_trait tests

* removed text for link to remix url

* fix: hashing

---------

Co-authored-by: Giuseppe Fontanella <gfont@MacBook-Pro-M1-di-Giuseppe.local>
Co-authored-by: Evans <mr.evans0075@gmail.com>
  • Loading branch information
3 people committed Jan 5, 2024
1 parent d1924c4 commit 28a2599
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 28 deletions.
4 changes: 4 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ version = "0.1.0"
name = "hash_solidity_compatible"
version = "0.1.0"

[[package]]
name = "hash_trait"
version = "0.1.0"

[[package]]
name = "interfaces_traits"
version = "0.1.0"
Expand Down
1 change: 1 addition & 0 deletions listings/advanced-concepts/hash_trait/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
11 changes: 11 additions & 0 deletions listings/advanced-concepts/hash_trait/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "hash_trait"
version.workspace = true

[dependencies]
starknet.workspace = true

[scripts]
test.workspace = true

[[target.starknet-contract]]
118 changes: 118 additions & 0 deletions listings/advanced-concepts/hash_trait/src/hash_trait.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#[starknet::interface]
trait IHashTrait<T> {
fn save_user_with_poseidon(
ref self: T, id: felt252, username: felt252, password: felt252
) -> felt252;
fn save_user_with_pedersen(
ref self: T, id: felt252, username: felt252, password: felt252
) -> felt252;
}

// ANCHOR: hash
#[starknet::contract]
mod HashTraits {
use core::hash::{HashStateTrait, HashStateExTrait};
use core::{pedersen::PedersenTrait, poseidon::PoseidonTrait};

#[storage]
struct Storage {
user_hash_poseidon: felt252,
user_hash_pedersen: felt252,
}

#[derive(Drop, Hash)]
struct LoginDetails {
username: felt252,
password: felt252,
}

#[derive(Drop, Hash)]
struct UserDetails {
id: felt252,
login: LoginDetails,
}

#[abi(embed_v0)]
impl HashTrait of super::IHashTrait<ContractState> {
fn save_user_with_poseidon(
ref self: ContractState, id: felt252, username: felt252, password: felt252
) -> felt252 {
let login = LoginDetails { username, password };
let user = UserDetails { id, login };

let poseidon_hash = PoseidonTrait::new().update_with(user).finalize();

self.user_hash_poseidon.write(poseidon_hash);
poseidon_hash
}

fn save_user_with_pedersen(
ref self: ContractState, id: felt252, username: felt252, password: felt252
) -> felt252 {
let login = LoginDetails { username, password };
let user = UserDetails { id, login };

let pedersen_hash = PedersenTrait::new(0).update_with(user).finalize();

self.user_hash_pedersen.write(pedersen_hash);
pedersen_hash
}
}
}
// ANCHOR_END: hash

#[cfg(test)]
mod tests {
use super::{HashTraits, IHashTraitDispatcher, IHashTraitDispatcherTrait};

use core::hash::{HashStateTrait, HashStateExTrait};
use core::{pedersen::PedersenTrait, poseidon::PoseidonTrait};
use starknet::{deploy_syscall};

use debug::PrintTrait;

fn deploy() -> IHashTraitDispatcher {
let mut calldata = ArrayTrait::new();
let (address, _) = deploy_syscall(
HashTraits::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false
)
.unwrap();
IHashTraitDispatcher { contract_address: address }
}


#[test]
#[available_gas(20000000)]
fn test_pedersen_hash() {
let mut contract = deploy();

let id = 0x1;
let username = 'A.stark';
let password = 'password.stark';
let test_hash = contract.save_user_with_pedersen(id, username, password);

assert(
test_hash == 0x6da4b4d0489989f5483d179643dafb3405b0e3b883a6c8efe5beb824ba9055a,
'Incorrect hash output'
);
}

#[test]
#[available_gas(20000000)]
fn test_poseidon_hash() {
let mut contract = deploy();

let id = 0x1;
let username = 'A.stark';
let password = 'password.stark';

let test_hash = contract.save_user_with_poseidon(id, username, password);

test_hash.print();

assert(
test_hash == 0x4d165e1d398ae4864854518d3c58c3d7a21ed9c1f8f3618fbb0031d208aab7b,
'Incorrect hash output'
);
}
}
1 change: 1 addition & 0 deletions listings/advanced-concepts/hash_trait/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod hash_trait;
56 changes: 28 additions & 28 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,33 @@ Summary
# Getting Started

<!-- - [Local environnement setup](./ch00/env_setup.md) -->

- [Basics of a Starknet contract](./ch00/basics/introduction.md)
- [Storage](./ch00/basics/storage.md)
- [Constructor](./ch00/basics/constructor.md)
- [Variables](./ch00/basics/variables.md)
- [Visibility and Mutability](./ch00/basics/visibility-mutability.md)
- [Counter Example](./ch00/basics/counter.md)
- [Mappings](./ch00/basics/mappings.md)
- [Errors](./ch00/basics/errors.md)
- [Events](./ch00/basics/events.md)
- [Syscalls](./ch00/basics/syscalls.md)
- [Storing Custom Types](./ch00/basics/storing-custom-types.md)
- [Custom types in entrypoints](./ch00/basics/custom-types-in-entrypoints.md)
- [Documentation](./ch00/basics/documentation.md)
- [Deploy and interact with contracts](./ch00/interacting/interacting.md)
- [Contract interfaces and Traits generation](./ch00/interacting/interfaces-traits.md)
- [Calling other contracts](./ch00/interacting/calling_other_contracts.md)
- [Factory pattern](./ch00/interacting/factory.md)
- [Testing contracts](./ch00/testing/contract-testing.md)
- [Cairo cheatsheet](./ch00/cairo_cheatsheet/cairo_cheatsheet.md)
- [Felt](./ch00/cairo_cheatsheet/felt.md)
- [LegacyMap](./ch00/cairo_cheatsheet/mapping.md)
- [Arrays](./ch00/cairo_cheatsheet/arrays.md)
- [Loop](./ch00/cairo_cheatsheet/loop.md)
- [Match](./ch00/cairo_cheatsheet/match.md)
- [Tuples](./ch00/cairo_cheatsheet/tuples.md)
- [Struct](./ch00/cairo_cheatsheet/struct.md)
- [Type casting](./ch00/cairo_cheatsheet/type_casting.md)
- [Basics of a Starknet contract](./ch00/basics/introduction.md)
- [Storage](./ch00/basics/storage.md)
- [Constructor](./ch00/basics/constructor.md)
- [Variables](./ch00/basics/variables.md)
- [Visibility and Mutability](./ch00/basics/visibility-mutability.md)
- [Counter Example](./ch00/basics/counter.md)
- [Mappings](./ch00/basics/mappings.md)
- [Errors](./ch00/basics/errors.md)
- [Events](./ch00/basics/events.md)
- [Syscalls](./ch00/basics/syscalls.md)
- [Storing Custom Types](./ch00/basics/storing-custom-types.md)
- [Custom types in entrypoints](./ch00/basics/custom-types-in-entrypoints.md)
- [Documentation](./ch00/basics/documentation.md)
- [Deploy and interact with contracts](./ch00/interacting/interacting.md)
- [Contract interfaces and Traits generation](./ch00/interacting/interfaces-traits.md)
- [Calling other contracts](./ch00/interacting/calling_other_contracts.md)
- [Factory pattern](./ch00/interacting/factory.md)
- [Testing contracts](./ch00/testing/contract-testing.md)
- [Cairo cheatsheet](./ch00/cairo_cheatsheet/cairo_cheatsheet.md)
- [Felt](./ch00/cairo_cheatsheet/felt.md)
- [LegacyMap](./ch00/cairo_cheatsheet/mapping.md)
- [Arrays](./ch00/cairo_cheatsheet/arrays.md)
- [Loop](./ch00/cairo_cheatsheet/loop.md)
- [Match](./ch00/cairo_cheatsheet/match.md)
- [Tuples](./ch00/cairo_cheatsheet/tuples.md)
- [Struct](./ch00/cairo_cheatsheet/struct.md)
- [Type casting](./ch00/cairo_cheatsheet/type_casting.md)

# Components

Expand All @@ -59,6 +58,7 @@ Summary
- [Writing to any storage slot](./ch02/write_to_any_slot.md)
- [Storing Arrays](./ch02/storing_arrays.md)
- [Struct as mapping key](./ch02/struct-mapping-key.md)
- [Hashing](./ch02/hashing.md)
- [Hash Solidity Compatible](./ch02/hash-solidity-compatible.md)
- [Optimisations](./ch02/optimisations/optimisations.md)
- [Storage Optimisations](./ch02/optimisations/store_using_packing.md)
Expand Down
16 changes: 16 additions & 0 deletions src/ch02/hashing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Hashing

Hashing is a cryptographic technique that allows you to transform a variable length input into a fixed length output.
The resulting output is called a hash and it's completely different from the input.
Hash functions are deterministic, meaning that the same input will always produce the same output.

The two hash functions provided by the Cairo library are `Poseidon` and `Pedersen`.
Pedersen hashes were used in the past (but still used in some scenario for backward compatibility) while Poseidon hashes are the standard nowadays since they were designed to be very efficient for Zero Knowledge proof systems.

In Cairo it's possible to hash all the types that can be converted to `felt252` since they implement natively the `Hash` trait. It's also possible to hash more complex types like structs by deriving the Hash trait with the attribute `#[derive(Hash)]` but only if all the struct's fields are themselves hashable.

You first need to initialize a hash state with the `new` method of the `HashStateTrait` and then you can update it with the `update` method. You can accumulate multiple updates. Then, the `finalize` method returns the final hash value as a `felt252`.

```rust
{{#rustdoc_include ../../listings/advanced-concepts/hash_trait/src/hash_trait.cairo:hash}}
```

0 comments on commit 28a2599

Please sign in to comment.