Skip to content

Commit

Permalink
Poseidon merkle hasher
Browse files Browse the repository at this point in the history
  • Loading branch information
spapinistarkware committed May 9, 2024
1 parent f1b8d89 commit 205303e
Show file tree
Hide file tree
Showing 5 changed files with 78 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: 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 type BaseField = fields::m31::M31;

fn main() {}
72 changes: 72 additions & 0 deletions stwo_cairo_verifier/src/vcs.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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;
}

const M31_ELS_IN_HASH_BLOCK: usize = 8;
const M31_ELS_IN_HASH_BLOCK_MINUS1: usize = 7;
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_BLOCK_MINUS1
- ((column_values.len() + 7) % M31_ELS_IN_HASH_BLOCK);
while pad_len > 0 {
column_values.append(core::num::traits::Zero::zero());
pad_len = M31_ELS_IN_HASH_BLOCK_MINUS1
- ((column_values.len() + M31_ELS_IN_HASH_BLOCK_MINUS1) % M31_ELS_IN_HASH_BLOCK);
};

while !column_values.is_empty() {
let mut word = 0;
word = word * 0x80000000 + column_values.pop_front().unwrap().inner.into();
word = word * 0x80000000 + column_values.pop_front().unwrap().inner.into();
word = word * 0x80000000 + column_values.pop_front().unwrap().inner.into();
word = word * 0x80000000 + column_values.pop_front().unwrap().inner.into();
word = word * 0x80000000 + column_values.pop_front().unwrap().inner.into();
word = word * 0x80000000 + column_values.pop_front().unwrap().inner.into();
word = word * 0x80000000 + column_values.pop_front().unwrap().inner.into();
word = word * 0x80000000 + 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 205303e

Please sign in to comment.