Skip to content

Commit

Permalink
Poseidon merkle hasher (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
spapinistarkware authored May 16, 2024
1 parent f1b8d89 commit 9e0451a
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cairo-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ jobs:
- uses: actions/checkout@v3
- uses: software-mansion/setup-scarb@v1
with:
scarb-version: "2.6.4"
scarb-version: "nightly-2024-04-24"
- run: scarb fmt --check
- run: scarb test
1 change: 1 addition & 0 deletions stwo_cairo_verifier/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scarb nightly-2024-04-24
2 changes: 2 additions & 0 deletions stwo_cairo_verifier/src/fields.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pub mod m31;
pub mod cm31;

pub type BaseField = m31::M31;
2 changes: 1 addition & 1 deletion stwo_cairo_verifier/src/fields/m31.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const P64NZ: NonZero<u64> = 0x7fffffff;

#[derive(Copy, Drop, Debug, PartialEq, Eq)]
pub struct M31 {
inner: u32
pub inner: u32
}

#[generate_trait]
Expand Down
3 changes: 3 additions & 0 deletions stwo_cairo_verifier/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
mod fields;
mod vcs;

pub use fields::BaseField;

fn main() {}
74 changes: 74 additions & 0 deletions stwo_cairo_verifier/src/vcs.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use core::array::ArrayTrait;
use core::option::OptionTrait;
use core::poseidon::poseidon_hash_span;
use stwo_cairo_verifier::BaseField;

// A Merkle node hash is a hash of:
// [left_child_hash, right_child_hash], column0_value, column1_value, ...
// "[]" denotes optional values.
// The largest Merkle layer has no left and right child hashes. The rest of the layers have
// children hashes.
// At each layer, the tree may have multiple columns of the same length as the layer.
// Each node in that layer contains one value from each column.
pub trait MerkleHasher {
type Hash;
// Hashes a single Merkle node.
fn hash_node(
children_hashes: Option<(Self::Hash, Self::Hash)>, column_values: Array<BaseField>,
) -> Self::Hash;
}

// 8 M31 elements fit in a hash, since 31*8 = 242 < 252.
const M31_ELS_IN_HASH: usize = 8;
const M31_ELS_IN_HASH_MINUS1: usize = M31_ELS_IN_HASH - 1;
const M31_IN_HASH_SHIFT: felt252 = 0x80000000; // 2**31.
pub impl PoseidonMerkleHasher of MerkleHasher {
type Hash = felt252;

fn hash_node(
children_hashes: Option<(Self::Hash, Self::Hash)>, mut column_values: Array<BaseField>,
) -> Self::Hash {
let mut hash_array: Array<felt252> = Default::default();
if let Option::Some((x, y)) = children_hashes {
hash_array.append(x);
hash_array.append(y);
}

// Pad column_values to a multiple of 8.
let mut pad_len = M31_ELS_IN_HASH_MINUS1
- ((column_values.len() + M31_ELS_IN_HASH_MINUS1) % M31_ELS_IN_HASH);
while pad_len > 0 {
column_values.append(core::num::traits::Zero::zero());
pad_len = M31_ELS_IN_HASH_MINUS1
- ((column_values.len() + M31_ELS_IN_HASH_MINUS1) % M31_ELS_IN_HASH);
};

while !column_values.is_empty() {
let mut word = 0;
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
word = word * M31_IN_HASH_SHIFT + column_values.pop_front().unwrap().inner.into();
};

poseidon_hash_span(hash_array.span())
}
}

#[cfg(test)]
mod tests {
use super::PoseidonMerkleHasher;
use stwo_cairo_verifier::fields::m31::{m31};

#[test]
fn test_m31() {
assert_eq!(
PoseidonMerkleHasher::hash_node(Option::None, array![m31(0), m31(1)]),
973835572668429495915136902981656666590582180872133591629269551720657739196
);
}
}

0 comments on commit 9e0451a

Please sign in to comment.