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
.
#[starknet::interface]
pub 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;
}
#[starknet::contract]
pub 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
}
}
}