Skip to content

Commit

Permalink
[feat] Batch inclusion proof verification (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
TAdev0 authored Oct 14, 2024
1 parent cad75c0 commit 207dd76
Show file tree
Hide file tree
Showing 9 changed files with 405 additions and 7 deletions.
3 changes: 3 additions & 0 deletions Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ version = "0.1.0"
[[package]]
name = "utreexo"
version = "0.1.0"
dependencies = [
"utils",
]
1 change: 0 additions & 1 deletion packages/consensus/src/validation/script.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use shinigami_engine::errors::byte_array_err;
use shinigami_engine::engine::EngineTrait;
use shinigami_engine::engine::EngineImpl;
use shinigami_engine::hash_cache::HashCacheImpl;
Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod double_sha256;
pub mod hash;
pub mod merkle_tree;
pub mod numeric;
pub mod sort;

// Let's use core non provable functions for now. Much faster.
// pub mod sha256;
Expand Down
84 changes: 83 additions & 1 deletion packages/utils/src/numeric.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::bit_shifts::shr;

/// Reverses the byte order of a `u32`.
///
/// This function takes a 32-bit unsigned integer and reverses the order of its bytes.
Expand All @@ -10,9 +12,26 @@ pub fn u32_byte_reverse(word: u32) -> u32 {
return byte0 + byte1 + byte2 + byte3;
}

/// Computes the next power of two of a u64 variable.
pub fn u64_next_power_of_two(mut n: u64) -> u64 {
if n == 0 {
return 1;
}

n -= 1;
n = n | shr(n, 1_u64);
n = n | shr(n, 2_u64);
n = n | shr(n, 4_u64);
n = n | shr(n, 8_u64);
n = n | shr(n, 16_u64);
n = n | shr(n, 32_u64);

n + 1
}

#[cfg(test)]
mod tests {
use super::u32_byte_reverse;
use super::{u32_byte_reverse, u64_next_power_of_two};

#[test]
fn test_u32_byte_reverse() {
Expand Down Expand Up @@ -41,4 +60,67 @@ mod tests {
let result = u32_byte_reverse(input);
assert_eq!(result, expected_output);
}

#[test]
fn test_u64_next_power_of_two() {
let input: u64 = 3;
let expected_output: u64 = 4;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 5;
let expected_output: u64 = 8;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 11;
let expected_output: u64 = 16;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 20;
let expected_output: u64 = 32;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 61;
let expected_output: u64 = 64;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 100;
let expected_output: u64 = 128;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 189;
let expected_output: u64 = 256;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 480;
let expected_output: u64 = 512;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 777;
let expected_output: u64 = 1024;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 1025;
let expected_output: u64 = 2048;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 4095;
let expected_output: u64 = 4096;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);

let input: u64 = 1500000000000000000;
let expected_output: u64 = 2305843009213693952;
let result = u64_next_power_of_two(input);
assert_eq!(result, expected_output);
}
}
39 changes: 39 additions & 0 deletions packages/utils/src/sort.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/// Bubble sort from
/// https://github.com/keep-starknet-strange/alexandria/blob/main/packages/sorting/src/bubble_sort.cairo
pub fn bubble_sort<T, +Copy<T>, +Drop<T>, +PartialOrd<T>>(mut array: Span<T>) -> Array<T> {
if array.len() == 0 {
return array![];
}
if array.len() == 1 {
return array![*array[0]];
}
let mut idx1 = 0;
let mut idx2 = 1;
let mut sorted_iteration = true;
let mut sorted_array = array![];

loop {
if idx2 == array.len() {
sorted_array.append(*array[idx1]);
if sorted_iteration {
break;
}
array = sorted_array.span();
sorted_array = array![];
idx1 = 0;
idx2 = 1;
sorted_iteration = true;
} else {
if *array[idx1] <= *array[idx2] {
sorted_array.append(*array[idx1]);
idx1 = idx2;
idx2 += 1;
} else {
sorted_array.append(*array[idx2]);
idx2 += 1;
sorted_iteration = false;
}
};
};
sorted_array
}
3 changes: 3 additions & 0 deletions packages/utreexo/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name = "utreexo"
version = "0.1.0"
edition = "2024_07"

[dependencies]
utils = { path = "../utils" }

[dev-dependencies]
cairo_test.workspace = true

Expand Down
121 changes: 118 additions & 3 deletions packages/utreexo/src/stump/accumulator.cairo
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
use crate::stump::state::UtreexoStumpState;
use crate::stump::proof::UtreexoBatchProof;
use crate::stump::proof::{UtreexoBatchProof, UtreexoBatchProofTrait};

#[generate_trait]
pub impl StumpUtreexoAccumulatorImpl of StumpUtreexoAccumulator {
/// Adds multiple items to the accumulator
/// Adds multiple items to the accumulator.
fn add(self: @UtreexoStumpState, hashes: Span<felt252>) -> UtreexoStumpState {
// TODO: check if vanilla implementation is compatible with rustreexo, add tests
// https://github.com/mit-dci/rustreexo/blob/6a8fe53fa545df8f1a30733fa6ca9f7b0077d051/src/accumulator/stump.rs#L252
*self
}

/// Verifies that one or multiple leaves hashes are part of the utreexo forest given a proof.
fn verify(
self: @UtreexoStumpState, proof: @UtreexoBatchProof, del_hashes: Span<felt252>
) -> Result<(), ByteArray> {
// TODO
let computed_roots: Span<felt252> = proof.compute_roots(del_hashes, *self.num_leaves)?;
let mut number_matched_roots: u32 = 0;

for i in 0
..computed_roots
.len() {
for root in *self
.roots {
match root {
Option::Some(root) => {
if (computed_roots[i] == root) {
number_matched_roots += 1;
};
},
Option::None => {},
};
};
};

let computed_roots_len = computed_roots.len();

if (computed_roots_len != number_matched_roots && computed_roots_len != 0) {
return Result::Err("Proof verification failed");
}

Result::Ok(())
}

Expand All @@ -22,3 +47,93 @@ pub impl StumpUtreexoAccumulatorImpl of StumpUtreexoAccumulator {
*self
}
}

#[cfg(test)]
mod tests {
use super::{UtreexoStumpState, StumpUtreexoAccumulator, UtreexoBatchProof};

#[test]
fn test_verification_1() {
let state = UtreexoStumpState {
roots: array![
Option::Some(0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7)
]
.span(),
num_leaves: 2
};
let batch_proof = UtreexoBatchProof { targets: array![0].span(), proof: array![2].span() };
let del_hashes = array![1];

let result = state.verify(@batch_proof, del_hashes.span(),);
assert_eq!(result, Result::Ok(()));
}

#[test]
fn test_verification_2() {
let state = UtreexoStumpState {
roots: array![
Option::Some(0x1702d734e291ad551b886a70b96446b99e19e405511e71fb5edfc4d2d83ce92),
Option::Some(0x770ad1be69d195e821c8c35051b32492e71592e230b950a99ebf87e98967ca),
Option::Some(0x2392042cbfda7371c81c9d7b456563533c2d6998b9e690a0d97421e6ae51a98),
Option::Some(0xf),
]
.span(),
num_leaves: 15
};
let batch_proof = UtreexoBatchProof {
targets: array![1, 3, 10, 13].span(),
proof: array![
0x1,
0x3,
0xC,
0xD,
0x436e91732c0a83fa238d71460463f4b1fe0dc0b1ebcbc10967a84cec9d13154,
0xdc9cc50aff0bdadd82a05bbab54015a07fccf2a4e30fa528fdca5a35d5423f
]
.span()
};
let del_hashes = array![2, 4, 11, 14];

let result = state.verify(@batch_proof, del_hashes.span(),);
assert_eq!(result, Result::Ok(()));
}

#[test]
fn test_verification_3() {
let state = UtreexoStumpState {
roots: array![
Option::Some(0x519631921e4905a63203f0cca7f6e6917082f30cef0930aa05bdc4323f6a398),
Option::Some(0x5198dcd61c969dfa8396dd27439ab776d120c2d67294fbcded0aa5f658f9150),
Option::Some(0x21d7ab8efac0146b5b47c8ad5431c3d14d9210319b0be7428fb2382ef115671),
Option::Some(0x74f794e653e00357d8a8ed45fcb74659841190c0821aa4e20bc4e30b2f3dd20),
]
.span(),
num_leaves: 30
};
let batch_proof = UtreexoBatchProof {
targets: array![4, 8, 12, 16, 20, 24, 28].span(),
proof: array![
0x6,
0xA,
0xE,
0x12,
0x16,
0x1A,
0x1E,
0x2797a40dbb8ea4b69a4e3bb4a9ccaa21a9585fcc71f3e5bb053ccae27910f90,
0x7877cc14d4c8e76cc51aa4c49aa7aadaade0cf475ad63bb37c27c324e145393,
0x556ea8bad1db13c6bdc3150a8289cd12044fb7e03cf201f35924a8afd4265a6,
0x41a4ec75a27497daa51261588a60f0956d3fd61e521634bbf36bba6343c3a1b,
0x3ba731d3734536d7cd5382cb4004ca4c24f1325b6fbeae27bcd6b4f9c0ed714,
0x117ed04a65093683f13c16cf73d2855f1f099a96581d1dad74eaf34c9a343c8,
0x79b32f615bbd57783700ae5f8e7b1ef79677c3545c4c69dc31b3aecce1d8fa6
]
.span()
};

let del_hashes = array![5, 9, 13, 17, 21, 25, 29];

let result = state.verify(@batch_proof, del_hashes.span(),);
assert_eq!(result, Result::Ok(()));
}
}
Loading

0 comments on commit 207dd76

Please sign in to comment.