Skip to content

Commit

Permalink
Merge branch 'update-cairo' into improvement/append
Browse files Browse the repository at this point in the history
  • Loading branch information
fmkra committed Mar 21, 2024
2 parents 01e7d3a + 3aaed82 commit f41cbc5
Show file tree
Hide file tree
Showing 20 changed files with 1,564 additions and 320 deletions.
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
scarb 0.7.0
scarb 2.6.4
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
![](/banner.png)

# `cairo-lib`: Comprehensive Library for Cairo 🐺
[![CI](https://github.com/HerodotusDev/cairo-lib/actions/workflows/ci.yml/badge.svg)](https://github.com/HerodotusDev/cairo-lib/actions/workflows/ci.yml)

Expand Down
6 changes: 6 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "cairo_lib"
version = "0.2.0"
Binary file added audit/ChainSecurity_Herodotus_CairoLib_audit.pdf
Binary file not shown.
Binary file added banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
249 changes: 127 additions & 122 deletions src/data_structures/eth_mpt.cairo

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions src/data_structures/mmr/mmr.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ impl MMRImpl of MMRTrait {
// @param peaks The peaks of the MMR
// @return Result with the new root and new peaks of the MMR
fn append(ref self: MMR, hash: felt252, peaks: Peaks) -> Result<(felt252, Peaks), felt252> {
let leaf_count = mmr_size_to_leaf_count(self.last_pos);
let leaf_count = mmr_size_to_leaf_count(self.last_pos.into());
let peaks_count = peaks.len();

if leaf_count_to_peaks_count(leaf_count) != peaks_count {
if leaf_count_to_peaks_count(leaf_count) != peaks_count.into() {
return Result::Err('Invalid peaks count');
}
if !peaks.valid(self.last_pos, self.root) {
Expand Down Expand Up @@ -91,9 +91,14 @@ impl MMRImpl of MMRTrait {
fn verify_proof(
self: @MMR, index: usize, hash: felt252, peaks: Peaks, proof: Proof
) -> Result<bool, felt252> {
let leaf_count = mmr_size_to_leaf_count((*self.last_pos).into());
if leaf_count_to_peaks_count(leaf_count) != peaks.len().into() {
return Result::Err('Invalid peaks count');
}
if !peaks.valid(*self.last_pos, *self.root) {
return Result::Err('Invalid peaks');
}

let (peak_index, peak_height) = get_peak_info(*self.last_pos, index);

if proof.len() != peak_height {
Expand Down
171 changes: 122 additions & 49 deletions src/data_structures/mmr/tests/test_mmr.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,122 +23,195 @@ fn test_append_initial() {
let mut mmr: MMR = Default::default();

let peaks = array![].span();
mmr.append(*elems.at(0), peaks);
let (new_root, new_peaks) = mmr.append(*elems.at(0), peaks).unwrap();

let root = PoseidonHasher::hash_double(1, *elems.at(0));
let expected_root = PoseidonHasher::hash_double(1, *elems.at(0));
assert(mmr.last_pos == 1, 'Wrong last_pos');
assert(mmr.root == root, 'Wrong root');
assert(mmr.root == expected_root, 'Wrong updated root');
assert(new_root == expected_root, 'Wrong returned root');

assert(new_peaks == array![*elems.at(0)].span(), 'Wrong new_peaks');
}

#[test]
#[available_gas(99999999)]
fn test_append_1() {
let elems = helper_test_get_elements();
let mut mmr: MMR = Default::default();
let mmr_peaks_0 = array![].span();

let (mmr_root_1, mmr_peaks_1) = mmr.append(*elems.at(0), mmr_peaks_0).unwrap();

let expected_peaks_1 = array![*elems.at(0)].span();
let expected_root_1 = PoseidonHasher::hash_double(1, *elems.at(0));
assert(expected_peaks_1 == mmr_peaks_1, 'Wrong peaks after 1 append');
assert(mmr.root == expected_root_1, 'Wrong updated root after 2 a.');
assert(mmr_root_1 == expected_root_1, 'Wrong returned root after 1 a.');

let mut peaks = array![].span();
mmr.append(*elems.at(0), peaks);
let (mmr_root_2, mmr_peaks_2) = mmr.append(*elems.at(1), mmr_peaks_1).unwrap();

peaks = array![*elems.at(0)].span();
mmr.append(*elems.at(1), peaks);
let expected_peaks_2 = array![*elems.at(2)].span();
let expected_root_2 = PoseidonHasher::hash_double(3, *elems.at(2));
assert(expected_peaks_2 == mmr_peaks_2, 'Wrong peaks after 2 appends');
assert(mmr.root == expected_root_2, 'Wrong updated root after 2 a.');
assert(mmr_root_2 == expected_root_2, 'Wrong reeturned root after 2 a.');

let root = PoseidonHasher::hash_double(3, *elems.at(2));
assert(mmr.last_pos == 3, 'Wrong last_pos');
assert(mmr.root == root, 'Wrong root');
}

#[test]
#[available_gas(99999999)]
fn test_append_2() {
let elems = helper_test_get_elements();
let mut mmr: MMR = Default::default();
let mmr_peaks_0 = array![].span();

let mut peaks = array![].span();
mmr.append(*elems.at(0), peaks);
let (mmr_root_1, mmr_peaks_1) = mmr.append(*elems.at(0), mmr_peaks_0).unwrap();

peaks = array![*elems.at(0)].span();
mmr.append(*elems.at(1), peaks);
let expected_peaks_1 = array![*elems.at(0)].span();
let expected_root_1 = PoseidonHasher::hash_double(1, *elems.at(0));
assert(expected_peaks_1 == mmr_peaks_1, 'Wrong peaks after 1 append');
assert(mmr.root == expected_root_1, 'Wrong updated root after 2 a.');
assert(mmr_root_1 == expected_root_1, 'Wrong returned root after 1 a.');

peaks = array![*elems.at(2)].span();
mmr.append(*elems.at(3), peaks);
let (mmr_root_2, mmr_peaks_2) = mmr.append(*elems.at(1), mmr_peaks_1).unwrap();

let root = PoseidonHasher::hash_double(
let expected_peaks_2 = array![*elems.at(2)].span();
let expected_root_2 = PoseidonHasher::hash_double(3, *elems.at(2));
assert(expected_peaks_2 == mmr_peaks_2, 'Wrong peaks after 2 appends');
assert(mmr.root == expected_root_2, 'Wrong updated root after 2 a.');
assert(mmr_root_2 == expected_root_2, 'Wrong reeturned root after 2 a.');

let (mmr_root_3, mmr_peaks_3) = mmr.append(*elems.at(3), mmr_peaks_2).unwrap();

let expected_peaks_3 = array![*elems.at(2), *elems.at(3)].span();
let expected_root_3 = PoseidonHasher::hash_double(
4, PoseidonHasher::hash_double(*elems.at(2), *elems.at(3))
);
assert(expected_peaks_3 == mmr_peaks_3, 'Wrong peaks after 3 appends');
assert(mmr.root == expected_root_3, 'Wrong updated root after 3 a.');
assert(mmr_root_3 == expected_root_3, 'Wrong reeturned root after 3 a.');

assert(mmr.last_pos == 4, 'Wrong last_pos');
assert(mmr.root == root, 'Wrong root');
}

#[test]
#[available_gas(99999999)]
fn test_append_3() {
let elems = helper_test_get_elements();
let mut mmr: MMR = Default::default();
let mmr_peaks_0 = array![].span();

let mut peaks = array![].span();
mmr.append(*elems.at(0), peaks);
let (mmr_root_1, mmr_peaks_1) = mmr.append(*elems.at(0), mmr_peaks_0).unwrap();

peaks = array![*elems.at(0)].span();
mmr.append(*elems.at(1), peaks);
let expected_peaks_1 = array![*elems.at(0)].span();
let expected_root_1 = PoseidonHasher::hash_double(1, *elems.at(0));
assert(expected_peaks_1 == mmr_peaks_1, 'Wrong peaks after 1 append');
assert(mmr.root == expected_root_1, 'Wrong updated root after 2 a.');
assert(mmr_root_1 == expected_root_1, 'Wrong returned root after 1 a.');

peaks = array![*elems.at(2)].span();
mmr.append(*elems.at(3), peaks);
let (mmr_root_2, mmr_peaks_2) = mmr.append(*elems.at(1), mmr_peaks_1).unwrap();

let expected_peaks_2 = array![*elems.at(2)].span();
let expected_root_2 = PoseidonHasher::hash_double(3, *elems.at(2));
assert(expected_peaks_2 == mmr_peaks_2, 'Wrong peaks after 2 appends');
assert(mmr.root == expected_root_2, 'Wrong updated root after 2 a.');
assert(mmr_root_2 == expected_root_2, 'Wrong reeturned root after 2 a.');

let (mmr_root_3, mmr_peaks_3) = mmr.append(*elems.at(3), mmr_peaks_2).unwrap();

let expected_peaks_3 = array![*elems.at(2), *elems.at(3)].span();
let expected_root_3 = PoseidonHasher::hash_double(
4, PoseidonHasher::hash_double(*elems.at(2), *elems.at(3))
);
assert(expected_peaks_3 == mmr_peaks_3, 'Wrong peaks after 3 appends');
assert(mmr.root == expected_root_3, 'Wrong updated root after 3 a.');
assert(mmr_root_3 == expected_root_3, 'Wrong reeturned root after 3 a.');

peaks = array![*elems.at(2), *elems.at(3)].span();
mmr.append(*elems.at(4), peaks);
let (mmr_root_4, mmr_peaks_4) = mmr.append(*elems.at(4), mmr_peaks_3).unwrap();

let expected_peaks_4 = array![*elems.at(6)].span();
let expected_root_4 = PoseidonHasher::hash_double(7, *elems.at(6));
assert(expected_peaks_4 == mmr_peaks_4, 'Wrong peaks after 4 appends');
assert(mmr.root == expected_root_4, 'Wrong updated root after 4 a.');
assert(mmr_root_4 == expected_root_4, 'Wrong reeturned root after 4 a.');

let root = PoseidonHasher::hash_double(7, *elems.at(6));
assert(mmr.last_pos == 7, 'Wrong last_pos');
assert(mmr.root == root, 'Wrong root');
}

#[test]
#[available_gas(99999999)]
fn test_append_4() {
let elems = helper_test_get_elements();
let mut mmr: MMR = Default::default();
let mmr_peaks_0 = array![].span();

let (mmr_root_1, mmr_peaks_1) = mmr.append(*elems.at(0), mmr_peaks_0).unwrap();

let expected_peaks_1 = array![*elems.at(0)].span();
let expected_root_1 = PoseidonHasher::hash_double(1, *elems.at(0));
assert(expected_peaks_1 == mmr_peaks_1, 'Wrong peaks after 1 append');
assert(mmr.root == expected_root_1, 'Wrong updated root after 2 a.');
assert(mmr_root_1 == expected_root_1, 'Wrong returned root after 1 a.');

let (mmr_root_2, mmr_peaks_2) = mmr.append(*elems.at(1), mmr_peaks_1).unwrap();

let expected_peaks_2 = array![*elems.at(2)].span();
let expected_root_2 = PoseidonHasher::hash_double(3, *elems.at(2));
assert(expected_peaks_2 == mmr_peaks_2, 'Wrong peaks after 2 appends');
assert(mmr.root == expected_root_2, 'Wrong updated root after 2 a.');
assert(mmr_root_2 == expected_root_2, 'Wrong reeturned root after 2 a.');

let mut peaks = array![].span();
mmr.append(*elems.at(0), peaks);
let (mmr_root_3, mmr_peaks_3) = mmr.append(*elems.at(3), mmr_peaks_2).unwrap();

peaks = array![*elems.at(0)].span();
mmr.append(*elems.at(1), peaks);
let expected_peaks_3 = array![*elems.at(2), *elems.at(3)].span();
let expected_root_3 = PoseidonHasher::hash_double(
4, PoseidonHasher::hash_double(*elems.at(2), *elems.at(3))
);
assert(expected_peaks_3 == mmr_peaks_3, 'Wrong peaks after 3 appends');
assert(mmr.root == expected_root_3, 'Wrong updated root after 3 a.');
assert(mmr_root_3 == expected_root_3, 'Wrong reeturned root after 3 a.');

peaks = array![*elems.at(2)].span();
mmr.append(*elems.at(3), peaks);
let (mmr_root_4, mmr_peaks_4) = mmr.append(*elems.at(4), mmr_peaks_3).unwrap();

peaks = array![*elems.at(2), *elems.at(3)].span();
mmr.append(*elems.at(4), peaks);
let expected_peaks_4 = array![*elems.at(6)].span();
let expected_root_4 = PoseidonHasher::hash_double(7, *elems.at(6));
assert(expected_peaks_4 == mmr_peaks_4, 'Wrong peaks after 4 appends');
assert(mmr.root == expected_root_4, 'Wrong updated root after 4 a.');
assert(mmr_root_4 == expected_root_4, 'Wrong reeturned root after 4 a.');

peaks = array![*elems.at(6)].span();
mmr.append(*elems.at(7), peaks);
let (mmr_root_5, mmr_peaks_5) = mmr.append(*elems.at(7), mmr_peaks_4).unwrap();

let root = PoseidonHasher::hash_double(
let expected_peaks_5 = array![*elems.at(6), *elems.at(7)].span();
let expected_root_5 = PoseidonHasher::hash_double(
8, PoseidonHasher::hash_double(*elems.at(6), *elems.at(7))
);
assert(expected_peaks_5 == mmr_peaks_5, 'Wrong peaks after 5 appends');
assert(mmr.root == expected_root_5, 'Wrong updated root after 5 a.');
assert(mmr_root_5 == expected_root_5, 'Wrong reeturned root after 5 a.');

assert(mmr.last_pos == 8, 'Wrong last_pos');
assert(mmr.root == root, 'Wrong root');
}

#[test]
#[available_gas(99999999)]
fn test_append_wrong_peaks() {
let elems = helper_test_get_elements();
let mut mmr: MMR = Default::default();
let peaks = array![].span();

let (_, peaks) = mmr.append(*elems.at(0), peaks).unwrap();

let mut peaks = array![].span();
mmr.append(*elems.at(0), peaks);
let (_, peaks) = mmr.append(*elems.at(1), peaks).unwrap();

peaks = array![*elems.at(0)].span();
mmr.append(*elems.at(1), peaks);
let (_, peaks) = mmr.append(*elems.at(3), peaks).unwrap();

peaks = array![*elems.at(2)].span();
mmr.append(*elems.at(3), peaks);
assert(peaks == array![*elems.at(2), *elems.at(3)].span(), 'Wrong peaks returned by append');

peaks = array![*elems.at(2), *elems.at(4)].span();
let res = mmr.append(*elems.at(4), peaks);
let wrong_peaks = array![*elems.at(2), *elems.at(4)].span();
let res = mmr.append(*elems.at(4), wrong_peaks);

assert(res.is_err(), 'Wrong peaks');
assert(res.is_err(), 'Appnd accepted with wrong peaks');
}

#[test]
Expand Down Expand Up @@ -241,7 +314,7 @@ fn test_attack_forge_peaks() {

// add the next element normally to mmr_real and get the root;
let peaks_real = array![*elems.at(6), *elems.at(7)].span();
mmr_real.append(9, peaks_real);
let _ = mmr_real.append(9, peaks_real);

// add the next element abnormally to mmr_real and get the root;
let forged_peak = PoseidonHasher::hash_double(*elems.at(6), *elems.at(7));
Expand Down
14 changes: 14 additions & 0 deletions src/data_structures/mmr/tests/test_utils.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use cairo_lib::data_structures::mmr::utils::{
get_height, compute_root, count_ones, leaf_index_to_mmr_index, get_peak_info,
mmr_size_to_leaf_count,
};
use cairo_lib::hashing::poseidon::PoseidonHasher;
use cairo_lib::data_structures::mmr::peaks::PeaksTrait;
Expand Down Expand Up @@ -73,6 +74,19 @@ fn test_leaf_index_to_mmr_index() {
assert(leaf_index_to_mmr_index(11) == 19, 'leaf_..._index(11) != 19');
}

#[test]
#[available_gas(999999999)]
fn test_mmr_size_to_leaf_count() {
assert(mmr_size_to_leaf_count(1) == 1, 'mmr_size_to_leaf_count(1) != 1');
assert(mmr_size_to_leaf_count(3) == 2, 'mmr_size_to_leaf_count(3) != 2');
assert(mmr_size_to_leaf_count(4) == 3, 'mmr_size_to_leaf_count(4) != 3');
assert(mmr_size_to_leaf_count(7) == 4, 'mmr_size_to_leaf_count(7) != 4');
assert(mmr_size_to_leaf_count(8) == 5, 'mmr_size_to_leaf_count(8) != 5');
assert(mmr_size_to_leaf_count(10) == 6, 'mmr_size_to_leaf_count(10) != 6');
assert(mmr_size_to_leaf_count(11) == 7, 'mmr_size_to_leaf_count(11) != 7');
assert(mmr_size_to_leaf_count(15) == 8, 'mmr_size_to_leaf_count(15) != 8');
}

#[test]
#[available_gas(999999999)]
fn test_get_peak_info() {
Expand Down
18 changes: 9 additions & 9 deletions src/data_structures/mmr/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn compute_root(last_pos: felt252, peaks: Peaks) -> felt252 {
}

// @notice Count the number of bits set to 1 in a 256-bit unsigned integer
// @param n The 256-bit unsigned integer
// @param arg The 256-bit unsigned integer
// @return The number of bits set to 1 in n
fn count_ones(n: u32) -> u32 {
let mut n = n;
Expand All @@ -54,19 +54,19 @@ fn leaf_index_to_mmr_index(n: u32) -> u32 {
// @result Number of leaves
fn mmr_size_to_leaf_count(n: u32) -> u32 {
let mut mmr_size = n;
let bits = bit_length(mmr_size);
let mut i = pow(2, bits);
let bits = bit_length(mmr_size + 1);
let mut mountain_leaf_count = pow(2, bits - 1);
let mut leaf_count = 0;
loop {
if i == 0 {
if mountain_leaf_count == 0 {
break leaf_count;
}
let x = 2 * i - 1;
if x <= mmr_size {
mmr_size -= x;
leaf_count += i;
let mountain_size = 2 * mountain_leaf_count - 1;
if mountain_size <= mmr_size {
mmr_size -= mountain_size;
leaf_count += mountain_leaf_count;
}
i /= 2;
mountain_leaf_count /= 2;
}
}

Expand Down
Loading

0 comments on commit f41cbc5

Please sign in to comment.