فهرست منبع

sdk: Make SMT generic over fields.

parazyd 3 سال پیش
والد
کامیت
f62bcad9bb
1فایلهای تغییر یافته به همراه44 افزوده شده و 41 حذف شده
  1. 44 41
      src/sdk/src/crypto/smt.rs

+ 44 - 41
src/sdk/src/crypto/smt.rs

@@ -53,28 +53,31 @@ use std::collections::{BTreeMap, BTreeSet};
 
 
 use halo2_gadgets::poseidon::{
 use halo2_gadgets::poseidon::{
     primitives as poseidon,
     primitives as poseidon,
-    primitives::{ConstantLength, P128Pow5T3},
+    primitives::{ConstantLength, P128Pow5T3, Spec},
 };
 };
-use pasta_curves::{arithmetic::FieldExt, Fp};
+use pasta_curves::arithmetic::FieldExt;
 
 
 use crate::error::{ContractError, GenericResult};
 use crate::error::{ContractError, GenericResult};
 
 
-pub trait FieldHasher<const L: usize> {
-    fn hash(&self, inputs: [Fp; L]) -> GenericResult<Fp>;
+pub trait FieldHasher<F: FieldExt, const L: usize> {
+    fn hash(&self, inputs: [F; L]) -> GenericResult<F>;
     fn hasher() -> Self;
     fn hasher() -> Self;
 }
 }
 
 
 #[derive(Debug, Clone)]
 #[derive(Debug, Clone)]
-pub struct Poseidon<const L: usize>(PhantomData<Fp>);
+pub struct Poseidon<F: FieldExt, const L: usize>(PhantomData<F>);
 
 
-impl<const L: usize> Poseidon<L> {
+impl<F: FieldExt, const L: usize> Poseidon<F, L> {
     pub fn new() -> Self {
     pub fn new() -> Self {
         Poseidon(PhantomData::default())
         Poseidon(PhantomData::default())
     }
     }
 }
 }
 
 
-impl<const L: usize> FieldHasher<L> for Poseidon<L> {
-    fn hash(&self, inputs: [Fp; L]) -> GenericResult<Fp> {
+impl<F: FieldExt, const L: usize> FieldHasher<F, L> for Poseidon<F, L>
+where
+    P128Pow5T3: Spec<F, 3, 2>,
+{
+    fn hash(&self, inputs: [F; L]) -> GenericResult<F> {
         Ok(poseidon::Hash::<_, P128Pow5T3, ConstantLength<L>, 3, 2>::init().hash(inputs))
         Ok(poseidon::Hash::<_, P128Pow5T3, ConstantLength<L>, 3, 2>::init().hash(inputs))
     }
     }
 
 
@@ -89,17 +92,17 @@ impl<const L: usize> FieldHasher<L> for Poseidon<L> {
 /// Each pair is used to identify whether an incremental Merkle root construction
 /// Each pair is used to identify whether an incremental Merkle root construction
 /// is valid at each intermediate step.
 /// is valid at each intermediate step.
 #[derive(Debug, Copy, Clone)]
 #[derive(Debug, Copy, Clone)]
-pub struct Path<H: FieldHasher<2>, const N: usize> {
+pub struct Path<F: FieldExt, H: FieldHasher<F, 2>, const N: usize> {
     /// The path represented as a sequence of sibling pairs.
     /// The path represented as a sequence of sibling pairs.
-    pub path: [(Fp, Fp); N],
+    pub path: [(F, F); N],
     /// The phantom hasher type used to reconstruct the Merkle root.
     /// The phantom hasher type used to reconstruct the Merkle root.
     pub marker: PhantomData<H>,
     pub marker: PhantomData<H>,
 }
 }
 
 
-impl<H: FieldHasher<2>, const N: usize> Path<H, N> {
+impl<F: FieldExt, H: FieldHasher<F, 2>, const N: usize> Path<F, H, N> {
     /// Assumes leaf contains leaf-level data, i.e. hashes of secrets stored on
     /// Assumes leaf contains leaf-level data, i.e. hashes of secrets stored on
     /// leaf-level.
     /// leaf-level.
-    pub fn calculate_root(&self, leaf: &Fp, hasher: &H) -> GenericResult<Fp> {
+    pub fn calculate_root(&self, leaf: &F, hasher: &H) -> GenericResult<F> {
         if *leaf != self.path[0].0 && *leaf != self.path[0].1 {
         if *leaf != self.path[0].0 && *leaf != self.path[0].1 {
             return Err(ContractError::SmtInvalidLeaf)
             return Err(ContractError::SmtInvalidLeaf)
         }
         }
@@ -118,7 +121,7 @@ impl<H: FieldHasher<2>, const N: usize> Path<H, N> {
 
 
     /// Takes in an expected `root_hash` and leaf-level data (i.e. hashes of secrets)
     /// Takes in an expected `root_hash` and leaf-level data (i.e. hashes of secrets)
     /// for a leaf and checks that the leaf belongs to a tree having the expected hash.
     /// for a leaf and checks that the leaf belongs to a tree having the expected hash.
-    pub fn check_membership(&self, root_hash: &Fp, leaf: &Fp, hasher: &H) -> GenericResult<bool> {
+    pub fn check_membership(&self, root_hash: &F, leaf: &F, hasher: &H) -> GenericResult<bool> {
         let root = self.calculate_root(leaf, hasher)?;
         let root = self.calculate_root(leaf, hasher)?;
         Ok(root == *root_hash)
         Ok(root == *root_hash)
     }
     }
@@ -126,14 +129,14 @@ impl<H: FieldHasher<2>, const N: usize> Path<H, N> {
     /// Given leaf data, determine what the index of this leaf must be in the
     /// Given leaf data, determine what the index of this leaf must be in the
     /// Merkle tree it belongs to. Before doing so, check that the leaf does
     /// Merkle tree it belongs to. Before doing so, check that the leaf does
     /// indeed belong to a tree with the given `root_hash`.
     /// indeed belong to a tree with the given `root_hash`.
-    pub fn get_index(&self, root_hash: &Fp, leaf: &Fp, hasher: &H) -> GenericResult<Fp> {
+    pub fn get_index(&self, root_hash: &F, leaf: &F, hasher: &H) -> GenericResult<F> {
         if !self.check_membership(root_hash, leaf, hasher)? {
         if !self.check_membership(root_hash, leaf, hasher)? {
             return Err(ContractError::SmtInvalidLeaf)
             return Err(ContractError::SmtInvalidLeaf)
         }
         }
 
 
         let mut prev = *leaf;
         let mut prev = *leaf;
-        let mut index = Fp::zero();
-        let mut twopower = Fp::one();
+        let mut index = F::zero();
+        let mut twopower = F::one();
         // Check levels between leaf level and root
         // Check levels between leaf level and root
         for &(ref left_hash, ref right_hash) in &self.path {
         for &(ref left_hash, ref right_hash) in &self.path {
             // Check if the previous hash is for a left or right ndoe
             // Check if the previous hash is for a left or right ndoe
@@ -153,19 +156,19 @@ impl<H: FieldHasher<2>, const N: usize> Path<H, N> {
 ///
 ///
 /// SMT stores a set of leaves represented in a map and a set of empty
 /// SMT stores a set of leaves represented in a map and a set of empty
 /// hashes that it uses to represent the sparse areas of the tree.
 /// hashes that it uses to represent the sparse areas of the tree.
-pub struct SparseMerkleTree<H: FieldHasher<2>, const N: usize> {
+pub struct SparseMerkleTree<F: FieldExt, H: FieldHasher<F, 2>, const N: usize> {
     /// A map from leaf indices to leaf data stored as field elements.
     /// A map from leaf indices to leaf data stored as field elements.
-    pub tree: BTreeMap<u64, Fp>,
+    pub tree: BTreeMap<u64, F>,
     /// An array of default hashes hashed with themselves `N` times.
     /// An array of default hashes hashed with themselves `N` times.
-    empty_hashes: [Fp; N],
+    empty_hashes: [F; N],
     /// The phantom hasher type used to build the Merkle tree.
     /// The phantom hasher type used to build the Merkle tree.
     marker: PhantomData<H>,
     marker: PhantomData<H>,
 }
 }
 
 
-impl<H: FieldHasher<2>, const N: usize> SparseMerkleTree<H, N> {
+impl<F: FieldExt, H: FieldHasher<F, 2>, const N: usize> SparseMerkleTree<F, H, N> {
     /// Creates a new SMT from a map of indices to field elements.
     /// Creates a new SMT from a map of indices to field elements.
     pub fn new(
     pub fn new(
-        leaves: &BTreeMap<u32, Fp>,
+        leaves: &BTreeMap<u32, F>,
         hasher: &H,
         hasher: &H,
         empty_leaf: &[u8; 64],
         empty_leaf: &[u8; 64],
     ) -> GenericResult<Self> {
     ) -> GenericResult<Self> {
@@ -179,7 +182,7 @@ impl<H: FieldHasher<2>, const N: usize> SparseMerkleTree<H, N> {
         let tree = BTreeMap::new();
         let tree = BTreeMap::new();
         let empty_hashes = gen_empty_hashes(hasher, empty_leaf)?;
         let empty_hashes = gen_empty_hashes(hasher, empty_leaf)?;
 
 
-        let mut smt = SparseMerkleTree::<H, N> { tree, empty_hashes, marker: PhantomData };
+        let mut smt = SparseMerkleTree::<F, H, N> { tree, empty_hashes, marker: PhantomData };
 
 
         smt.insert_batch(leaves, hasher)?;
         smt.insert_batch(leaves, hasher)?;
 
 
@@ -187,8 +190,8 @@ impl<H: FieldHasher<2>, const N: usize> SparseMerkleTree<H, N> {
     }
     }
 
 
     /// Creates a new SMT from an array of field elements.
     /// Creates a new SMT from an array of field elements.
-    pub fn new_sequential(leaves: &[Fp], hasher: &H, empty_leaf: &[u8; 64]) -> GenericResult<Self> {
-        let pairs: BTreeMap<u32, Fp> =
+    pub fn new_sequential(leaves: &[F], hasher: &H, empty_leaf: &[u8; 64]) -> GenericResult<Self> {
+        let pairs: BTreeMap<u32, F> =
             leaves.iter().enumerate().map(|(i, l)| (i as u32, *l)).collect();
             leaves.iter().enumerate().map(|(i, l)| (i as u32, *l)).collect();
 
 
         let smt = Self::new(&pairs, hasher, empty_leaf)?;
         let smt = Self::new(&pairs, hasher, empty_leaf)?;
@@ -198,7 +201,7 @@ impl<H: FieldHasher<2>, const N: usize> SparseMerkleTree<H, N> {
 
 
     /// Takes a batch of field elements, inserts these hashes into the tree,
     /// Takes a batch of field elements, inserts these hashes into the tree,
     /// and updates the Merkle root.
     /// and updates the Merkle root.
-    pub fn insert_batch(&mut self, leaves: &BTreeMap<u32, Fp>, hasher: &H) -> GenericResult<()> {
+    pub fn insert_batch(&mut self, leaves: &BTreeMap<u32, F>, hasher: &H) -> GenericResult<()> {
         let last_level_index: u64 = (1u64 << N) - 1;
         let last_level_index: u64 = (1u64 << N) - 1;
 
 
         let mut level_idxs: BTreeSet<u64> = BTreeSet::new();
         let mut level_idxs: BTreeSet<u64> = BTreeSet::new();
@@ -233,14 +236,14 @@ impl<H: FieldHasher<2>, const N: usize> SparseMerkleTree<H, N> {
     }
     }
 
 
     /// Returns the Merkle tree root.
     /// Returns the Merkle tree root.
-    pub fn root(&self) -> Fp {
+    pub fn root(&self) -> F {
         self.tree.get(&0).cloned().unwrap_or(*self.empty_hashes.last().unwrap())
         self.tree.get(&0).cloned().unwrap_or(*self.empty_hashes.last().unwrap())
     }
     }
 
 
     /// Give the path leading from the leaf at `index` up to the root. This is
     /// Give the path leading from the leaf at `index` up to the root. This is
     /// a "proof" in the sense of "valid path in a Merkle tree", not a ZK argument.
     /// a "proof" in the sense of "valid path in a Merkle tree", not a ZK argument.
-    pub fn generate_membership_proof(&self, index: u64) -> Path<H, N> {
-        let mut path = [(Fp::zero(), Fp::zero()); N];
+    pub fn generate_membership_proof(&self, index: u64) -> Path<F, H, N> {
+        let mut path = [(F::zero(), F::zero()); N];
 
 
         let tree_index = convert_index_to_last_level(index, N);
         let tree_index = convert_index_to_last_level(index, N);
 
 
@@ -275,13 +278,13 @@ impl<H: FieldHasher<2>, const N: usize> SparseMerkleTree<H, N> {
 /// `default_leaf` hashed with itself and repeated `N` times with the
 /// `default_leaf` hashed with itself and repeated `N` times with the
 /// intermediate results. These are used to initialize the sparse portion
 /// intermediate results. These are used to initialize the sparse portion
 /// of the SMT.
 /// of the SMT.
-pub fn gen_empty_hashes<H: FieldHasher<2>, const N: usize>(
+pub fn gen_empty_hashes<F: FieldExt, H: FieldHasher<F, 2>, const N: usize>(
     hasher: &H,
     hasher: &H,
     default_leaf: &[u8; 64],
     default_leaf: &[u8; 64],
-) -> GenericResult<[Fp; N]> {
-    let mut empty_hashes = [Fp::zero(); N];
+) -> GenericResult<[F; N]> {
+    let mut empty_hashes = [F::zero(); N];
 
 
-    let mut empty_hash = Fp::from_bytes_wide(default_leaf);
+    let mut empty_hash = F::from_bytes_wide(default_leaf);
     for item in empty_hashes.iter_mut().take(N) {
     for item in empty_hashes.iter_mut().take(N) {
         *item = empty_hash;
         *item = empty_hash;
         empty_hash = hasher.hash([empty_hash, empty_hash])?;
         empty_hash = hasher.hash([empty_hash, empty_hash])?;
@@ -365,28 +368,28 @@ mod tests {
     use rand::rngs::OsRng;
     use rand::rngs::OsRng;
 
 
     /// Helper to change leaves array to BTreeMap and then create SMT.
     /// Helper to change leaves array to BTreeMap and then create SMT.
-    fn create_merkle_tree<H: FieldHasher<2>, const N: usize>(
+    fn create_merkle_tree<F: FieldExt, H: FieldHasher<F, 2>, const N: usize>(
         hasher: H,
         hasher: H,
-        leaves: &[Fp],
+        leaves: &[F],
         default_leaf: &[u8; 64],
         default_leaf: &[u8; 64],
-    ) -> SparseMerkleTree<H, N> {
-        SparseMerkleTree::<H, N>::new_sequential(leaves, &hasher, default_leaf).unwrap()
+    ) -> SparseMerkleTree<F, H, N> {
+        SparseMerkleTree::<F, H, N>::new_sequential(leaves, &hasher, default_leaf).unwrap()
     }
     }
 
 
     #[test]
     #[test]
     fn poseidon_smt() {
     fn poseidon_smt() {
-        let poseidon = Poseidon::<2>::new();
+        let poseidon = Poseidon::<Fp, 2>::new();
         let default_leaf = [0u8; 64];
         let default_leaf = [0u8; 64];
         let leaves = [Fp::random(&mut OsRng), Fp::random(&mut OsRng), Fp::random(&mut OsRng)];
         let leaves = [Fp::random(&mut OsRng), Fp::random(&mut OsRng), Fp::random(&mut OsRng)];
         const HEIGHT: usize = 3;
         const HEIGHT: usize = 3;
 
 
         let smt =
         let smt =
-            create_merkle_tree::<Poseidon<2>, HEIGHT>(poseidon.clone(), &leaves, &default_leaf);
+            create_merkle_tree::<Poseidon<Fp, 2>, HEIGHT>(poseidon.clone(), &leaves, &default_leaf);
 
 
         let root = smt.root();
         let root = smt.root();
 
 
         let empty_hashes =
         let empty_hashes =
-            gen_empty_hashes::<Poseidon<2>, HEIGHT>(&poseidon, &default_leaf).unwrap();
+            gen_empty_hashes::<Poseidon<Fp, 2>, HEIGHT>(&poseidon, &default_leaf).unwrap();
 
 
         let hash1 = leaves[0];
         let hash1 = leaves[0];
         let hash2 = leaves[1];
         let hash2 = leaves[1];
@@ -403,13 +406,13 @@ mod tests {
 
 
     #[test]
     #[test]
     fn poseidon_smt_incl_proof() {
     fn poseidon_smt_incl_proof() {
-        let poseidon = Poseidon::<2>::new();
+        let poseidon = Poseidon::<Fp, 2>::new();
         let default_leaf = [0u8; 64];
         let default_leaf = [0u8; 64];
         let leaves = [Fp::random(&mut OsRng), Fp::random(&mut OsRng), Fp::random(&mut OsRng)];
         let leaves = [Fp::random(&mut OsRng), Fp::random(&mut OsRng), Fp::random(&mut OsRng)];
         const HEIGHT: usize = 3;
         const HEIGHT: usize = 3;
 
 
         let smt =
         let smt =
-            create_merkle_tree::<Poseidon<2>, HEIGHT>(poseidon.clone(), &leaves, &default_leaf);
+            create_merkle_tree::<Poseidon<Fp, 2>, HEIGHT>(poseidon.clone(), &leaves, &default_leaf);
 
 
         let proof = smt.generate_membership_proof(0);
         let proof = smt.generate_membership_proof(0);
         let res = proof.check_membership(&smt.root(), &leaves[0], &poseidon).unwrap();
         let res = proof.check_membership(&smt.root(), &leaves[0], &poseidon).unwrap();