diff --git a/Cargo.lock b/Cargo.lock index 39c842a..c18aa0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,6 +649,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "bitcoin-private" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" + [[package]] name = "bitflags" version = "1.3.2" @@ -2562,11 +2568,18 @@ name = "node_core" version = "0.1.0" dependencies = [ "anyhow", + "bincode", "env_logger", + "k256", "log", + "monotree", + "rand 0.8.5", + "secp256k1-zkp", "serde", "serde_json", + "sha2 0.10.8", "storage", + "utxo", ] [[package]] @@ -3731,6 +3744,49 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "rand 0.8.5", + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-zkp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a44aed3002b5ae975f8624c5df3a949cfbf00479e18778b6058fcd213b76e3" +dependencies = [ + "bitcoin-private", + "rand 0.8.5", + "secp256k1", + "secp256k1-zkp-sys", + "serde", +] + +[[package]] +name = "secp256k1-zkp-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f08b2d0b143a22e07f798ae4f0ab20d5590d7c68e0d090f2088a48a21d1654" +dependencies = [ + "cc", + "secp256k1-sys", +] + [[package]] name = "semver" version = "1.0.23" diff --git a/Cargo.toml b/Cargo.toml index 3bfae89..6b93096 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,8 @@ monotree = "0.1.5" hex = "0.4.3" aes-gcm = "0.10.3" toml = "0.7.4" +secp256k1-zkp = "0.11.0" +bincode = "1.3.3" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index a04cc37..5ce19a7 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -83,10 +83,17 @@ impl Account { self.balance = new_balance; } - pub fn add_asset(&mut self, asset: Asset) -> Result<()> { + pub fn add_asset( + &mut self, + asset: Asset, + amount: u128, + privacy_flag: bool, + ) -> Result<()> { let payload_with_asset = UTXOPayload { owner: self.address, asset: serde_json::to_vec(&asset)?, + amount, + privacy_flag, }; let asset_utxo = UTXO::create_utxo_from_payload(payload_with_asset); diff --git a/node_core/Cargo.toml b/node_core/Cargo.toml index 17b45b4..e80981f 100644 --- a/node_core/Cargo.toml +++ b/node_core/Cargo.toml @@ -9,6 +9,18 @@ serde_json.workspace = true env_logger.workspace = true log.workspace = true serde.workspace = true +rand.workspace = true +k256.workspace = true +sha2.workspace = true +monotree.workspace = true +bincode.workspace = true [dependencies.storage] path = "../storage" + +[dependencies.utxo] +path = "../utxo" + +[dependencies.secp256k1-zkp] +workspace = true +features = ["std", "rand-std", "rand", "serde", "global-context"] diff --git a/node_core/src/executions/de.rs b/node_core/src/executions/de.rs new file mode 100644 index 0000000..662bb25 --- /dev/null +++ b/node_core/src/executions/de.rs @@ -0,0 +1,198 @@ +use bincode; +use k256::Scalar; +use monotree::hasher::Blake3; +use monotree::{Hasher, Monotree, Proof}; +use rand::thread_rng; +use secp256k1_zkp::{ + compute_adaptive_blinding_factor, verify_commitments_sum_to_equal, CommitmentSecrets, + Generator, PedersenCommitment, Tag, Tweak, SECP256K1, +}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use storage::{ + commitment::Commitment, commitments_sparse_merkle_tree::CommitmentsSparseMerkleTree, + nullifier::UTXONullifier, nullifier_sparse_merkle_tree::NullifierSparseMerkleTree, +}; +use utxo::{ + utxo_core::{UTXOPayload, UTXO}, + utxo_tree::UTXOSparseMerkleTree, +}; + +fn commitment_secrets_random(value: u64) -> CommitmentSecrets { + CommitmentSecrets { + value, + value_blinding_factor: Tweak::new(&mut thread_rng()), + generator_blinding_factor: Tweak::new(&mut thread_rng()), + } +} + +pub fn tag_random() -> Tag { + use rand::thread_rng; + use rand::RngCore; + + let mut bytes = [0u8; 32]; + thread_rng().fill_bytes(&mut bytes); + + Tag::from(bytes) +} + +pub fn commit(comm: &CommitmentSecrets, tag: Tag) -> PedersenCommitment { + let generator = Generator::new_blinded(SECP256K1, tag, comm.generator_blinding_factor); + + PedersenCommitment::new(SECP256K1, comm.value, comm.value_blinding_factor, generator) +} + +fn hash(input: &[u8]) -> Vec { + Sha256::digest(input).to_vec() +} + +// Generate nullifiers + +// takes the input_utxo and nsk +// returns the nullifiers[i], where the nullifier[i] = hash(in_commitments[i] || nsk) where the hash function +pub fn generate_nullifiers(input_utxo: &UTXO, nsk: &[u8]) -> Vec { + let mut input = bincode::serialize(input_utxo).unwrap().to_vec(); + input.extend_from_slice(nsk); + hash(&input) +} + +// Generate commitments for output UTXOs + +// uses the list of input_utxos[] +// returns in_commitments[] where each in_commitments[i] = Commitment(in_utxos[i]) where the commitment +pub fn generate_commitments(input_utxos: &[UTXO]) -> Vec> { + input_utxos + .iter() + .map(|utxo| { + let serialized = bincode::serialize(utxo).unwrap(); // Serialize UTXO. + hash(&serialized) + }) + .collect() +} + +// Validate inclusion proof for in_commitments + +// takes the in_commitments[i] as a leaf, the root hash root_commitment and the path in_commitments_proofs[i][], +// returns True if the in_commitments[i] is in the tree with root hash root_commitment otherwise returns False, as membership proof. +pub fn validate_in_commitments_proof( + in_commitment: &Vec, + root_commitment: Vec, + in_commitments_proof: &[Vec], +) -> bool { + // Placeholder implementation. + // Replace with Merkle proof verification logic. + // hash(&[pedersen_commitment.serialize().to_vec(), in_commitments_proof.concat()].concat()) == root_commitment + + let mut nsmt = CommitmentsSparseMerkleTree { + curr_root: Option::Some(root_commitment), + tree: Monotree::default(), + hasher: Blake3::new(), + }; + + let commitments: Vec<_> = in_commitments_proof + .into_iter() + .map(|n_p| Commitment { + commitment_hash: n_p.clone(), + }) + .collect(); + nsmt.insert_items(commitments).unwrap(); + + nsmt.get_non_membership_proof(in_commitment.clone()) + .unwrap() + .1 + .is_some() +} + +// Validate non-membership proof for nullifiers + +// takes the nullifiers[i], path nullifiers_proof[i][] and the root hash root_nullifier, +// returns True if the nullifiers[i] is not in the tree with root hash root_nullifier otherwise returns False, as non-membership proof. +pub fn validate_nullifiers_proof( + nullifier: [u8; 32], + root_nullifier: [u8; 32], + nullifiers_proof: &[[u8; 32]], +) -> bool { + let mut nsmt = NullifierSparseMerkleTree { + curr_root: Option::Some(root_nullifier), + tree: Monotree::default(), + hasher: Blake3::new(), + }; + + let nullifiers: Vec<_> = nullifiers_proof + .into_iter() + .map(|n_p| UTXONullifier { utxo_hash: *n_p }) + .collect(); + nsmt.insert_items(nullifiers).unwrap(); + + nsmt.get_non_membership_proof(nullifier) + .unwrap() + .1 + .is_none() +} + +// Check balances + +// takes the public_info and output_utxos[], +// returns the True if the token amount in public_info matches the sum of all output_utxos[], otherwise return False. +pub fn check_balances(public_info: u128, output_utxos: &[UTXO]) -> bool { + let total_output: u128 = output_utxos.iter().map(|utxo| utxo.amount).sum(); + public_info == total_output +} + +// Verify Pedersen commitment + +// takes the public_info, secret_r and pedersen_commitment and +// checks that commitment(public_info,secret_r) is equal pedersen_commitment where the commitment is pedersen commitment. +pub fn verify_commitment( + public_info: u64, + secret_r: &[u8], + pedersen_commitment: &PedersenCommitment, +) -> bool { + let commitment_secrets = CommitmentSecrets { + value: public_info, + value_blinding_factor: Tweak::from_slice(secret_r).unwrap(), + generator_blinding_factor: Tweak::new(&mut thread_rng()), + }; + + let tag = tag_random(); + let commitment = commit(&commitment_secrets, tag); + + commitment == *pedersen_commitment +} + +fn de_kernel( + root_commitment: &[u8], + root_nullifier: [u8; 32], + public_info: u64, + input_utxos: &[UTXO], + in_commitments_proof: &[Vec], + nullifiers_proof: &[[u8; 32]], + nullifier_secret_key: Scalar, +) -> (Vec, Vec>) { + check_balances(public_info as u128, input_utxos); + + let nullifiers: Vec<_> = input_utxos + .into_iter() + .map(|utxo| generate_nullifiers(&utxo, &nullifier_secret_key.to_bytes())) + .collect(); + + let in_commitments = generate_commitments(&input_utxos); + + for in_commitment in in_commitments { + validate_in_commitments_proof( + &in_commitment, + root_commitment.to_vec(), + in_commitments_proof, + ); + } + + for nullifier in nullifiers.iter() { + validate_nullifiers_proof( + nullifier[0..32].try_into().unwrap(), + root_nullifier, + nullifiers_proof, + ); + } + + (vec![], nullifiers) +} diff --git a/node_core/src/executions/mod.rs b/node_core/src/executions/mod.rs new file mode 100644 index 0000000..712e3e2 --- /dev/null +++ b/node_core/src/executions/mod.rs @@ -0,0 +1,2 @@ +mod de; +mod se; diff --git a/node_core/src/executions/se.rs b/node_core/src/executions/se.rs new file mode 100644 index 0000000..5ae5a1d --- /dev/null +++ b/node_core/src/executions/se.rs @@ -0,0 +1,191 @@ +use bincode; +use k256::Scalar; +use monotree::hasher::Blake3; +use monotree::{Hasher, Monotree, Proof}; +use rand::thread_rng; +use secp256k1_zkp::{ + compute_adaptive_blinding_factor, verify_commitments_sum_to_equal, CommitmentSecrets, + Generator, PedersenCommitment, Tag, Tweak, SECP256K1, +}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use storage::{ + commitment::Commitment, commitments_sparse_merkle_tree::CommitmentsSparseMerkleTree, + nullifier::UTXONullifier, nullifier_sparse_merkle_tree::NullifierSparseMerkleTree, +}; +use utxo::{ + utxo_core::{UTXOPayload, UTXO}, + utxo_tree::UTXOSparseMerkleTree, +}; + +fn commitment_secrets_random(value: u64) -> CommitmentSecrets { + CommitmentSecrets { + value, + value_blinding_factor: Tweak::new(&mut thread_rng()), + generator_blinding_factor: Tweak::new(&mut thread_rng()), + } +} + +pub fn tag_random() -> Tag { + use rand::thread_rng; + use rand::RngCore; + + let mut bytes = [0u8; 32]; + thread_rng().fill_bytes(&mut bytes); + + Tag::from(bytes) +} + +pub fn commit(comm: &CommitmentSecrets, tag: Tag) -> PedersenCommitment { + let generator = Generator::new_blinded(SECP256K1, tag, comm.generator_blinding_factor); + + PedersenCommitment::new(SECP256K1, comm.value, comm.value_blinding_factor, generator) +} + +fn hash(input: &[u8]) -> Vec { + Sha256::digest(input).to_vec() +} + +// Generate nullifiers + +// takes the pedersen_commitment and nsk then +// returns a list of nullifiers, where the nullifier = hash(pedersen_commitment || nsk) where the hash function will be determined + +pub fn generate_nullifiers(pedersen_commitment: &PedersenCommitment, nsk: &[u8]) -> Vec { + let mut input = pedersen_commitment.serialize().to_vec(); + input.extend_from_slice(nsk); + hash(&input) +} + +// Generate commitments for output UTXOs + +// uses the list of output_utxos[] and +// returns out_commitments[] where each out_commitments[i] = Commitment(output_utxos[i]) +// where the commitment will be determined +pub fn generate_commitments(output_utxos: &[UTXO]) -> Vec> { + output_utxos + .iter() + .map(|utxo| { + let serialized = bincode::serialize(utxo).unwrap(); // Serialize UTXO. + hash(&serialized) + }) + .collect() +} + +// Validate inclusion proof for in_commitments + +// takes the pedersen_commitment as a leaf, the root hash root_commitment and the path in_commitments_proof[], +// returns True if the pedersen_commitment is in the tree with root hash root_commitment +// otherwise +// returns False, as membership proof. +pub fn validate_in_commitments_proof( + pedersen_commitment: &PedersenCommitment, + root_commitment: Vec, + in_commitments_proof: &[Vec], +) -> bool { + let mut nsmt = CommitmentsSparseMerkleTree { + curr_root: Option::Some(root_commitment), + tree: Monotree::default(), + hasher: Blake3::new(), + }; + + let commitments: Vec<_> = in_commitments_proof + .into_iter() + .map(|n_p| Commitment { + commitment_hash: n_p.clone(), + }) + .collect(); + nsmt.insert_items(commitments).unwrap(); + + nsmt.get_non_membership_proof(pedersen_commitment.serialize().to_vec()) + .unwrap() + .1 + .is_some() +} + +// Validate non-membership proof for nullifiers + +// takes the nullifier, path nullifiers_proof[] and the root hash root_nullifier, +// returns True if the nullifier is not in the tree with root hash root_nullifier +// otherwise +// returns False, as non-membership proof. +pub fn validate_nullifiers_proof( + nullifier: [u8; 32], + root_nullifier: [u8; 32], + nullifiers_proof: &[[u8; 32]], +) -> bool { + let mut nsmt = NullifierSparseMerkleTree { + curr_root: Option::Some(root_nullifier), + tree: Monotree::default(), + hasher: Blake3::new(), + }; + + let nullifiers: Vec<_> = nullifiers_proof + .into_iter() + .map(|n_p| UTXONullifier { utxo_hash: *n_p }) + .collect(); + nsmt.insert_items(nullifiers).unwrap(); + + nsmt.get_non_membership_proof(nullifier) + .unwrap() + .1 + .is_none() +} + +// Check balances + +// takes the public_info and output_utxos[], +// returns the True if the token amount in public_info matches the sum of all output_utxos[], otherwise return False. +pub fn check_balances(public_info: u128, output_utxos: &[UTXO]) -> bool { + let total_output: u128 = output_utxos.iter().map(|utxo| utxo.amount).sum(); + public_info == total_output +} + +// Verify Pedersen commitment + +// takes the public_info, secret_r and pedersen_commitment and +// checks that commitment(public_info,secret_r) is equal pedersen_commitment where the commitment is pedersen commitment. +pub fn verify_commitment( + public_info: u64, + secret_r: &[u8], + pedersen_commitment: &PedersenCommitment, +) -> bool { + let commitment_secrets = CommitmentSecrets { + value: public_info, + value_blinding_factor: Tweak::from_slice(secret_r).unwrap(), + generator_blinding_factor: Tweak::new(&mut thread_rng()), + }; + + let tag = tag_random(); + let commitment = commit(&commitment_secrets, tag); + + commitment == *pedersen_commitment +} + +fn se_kernel( + root_commitment: &[u8], + root_nullifier: [u8; 32], + public_info: u64, + pedersen_commitment: PedersenCommitment, + secret_r: &[u8], + output_utxos: &[UTXO], + in_commitments_proof: &[Vec], + nullifiers_proof: &[[u8; 32]], + nullifier_secret_key: Scalar, +) -> (Vec, Vec>, Vec) { + check_balances(public_info as u128, output_utxos); + + let out_commitments = generate_commitments(output_utxos); + + let nullifier = generate_nullifiers(&pedersen_commitment, &nullifier_secret_key.to_bytes()); + + validate_in_commitments_proof( + &pedersen_commitment, + root_commitment.to_vec(), + in_commitments_proof, + ); + + verify_commitment(public_info, secret_r, &pedersen_commitment); + + (vec![], out_commitments, nullifier) +} diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 7f92203..ed9a835 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -1 +1 @@ -//ToDo: Add node_core module +pub mod executions; diff --git a/storage/src/commitment.rs b/storage/src/commitment.rs new file mode 100644 index 0000000..0874bc7 --- /dev/null +++ b/storage/src/commitment.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +use crate::merkle_tree_public::CommitmentHashType; + +#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] +pub struct Commitment { + pub commitment_hash: CommitmentHashType, +} diff --git a/storage/src/commitments_sparse_merkle_tree.rs b/storage/src/commitments_sparse_merkle_tree.rs new file mode 100644 index 0000000..96f7eac --- /dev/null +++ b/storage/src/commitments_sparse_merkle_tree.rs @@ -0,0 +1,284 @@ +use monotree::database::MemoryDB; +use monotree::hasher::Blake3; +use monotree::{Hasher, Monotree, Proof}; + +use crate::commitment::Commitment; +use crate::merkle_tree_public::CommitmentHashType; +use crate::nullifier::UTXONullifier; + +pub struct CommitmentsSparseMerkleTree { + pub curr_root: Option, + pub tree: Monotree, + pub hasher: Blake3, +} + +impl CommitmentsSparseMerkleTree { + pub fn new() -> Self { + CommitmentsSparseMerkleTree { + curr_root: None, + tree: Monotree::default(), + hasher: Blake3::new(), + } + } + + pub fn insert_item(&mut self, commitment: Commitment) -> Result<(), monotree::Errors> { + let root = self + .curr_root + .as_ref() + .map(|val| val[0..32].try_into().unwrap()); + + let new_root = self.tree.insert( + root, + &commitment.commitment_hash[0..32].try_into().unwrap(), + &commitment.commitment_hash[0..32].try_into().unwrap(), + )?; + + self.curr_root = new_root.map(|val| val.to_vec()); + + Ok(()) + } + + pub fn insert_items(&mut self, commitments: Vec) -> Result<(), monotree::Errors> { + let root = self + .curr_root + .as_ref() + .map(|val| val[0..32].try_into().unwrap()); + + let hashes: Vec<_> = commitments + .iter() + .map(|val| val.commitment_hash[0..32].try_into().unwrap()) + .collect::>(); + + let new_root = self.tree.inserts(root, &hashes, &hashes)?; + + self.curr_root = new_root.map(|val| val[0..32].try_into().unwrap()); + + Ok(()) + } + + pub fn search_item_inclusion( + &mut self, + commitment_hash: CommitmentHashType, + ) -> Result { + self.tree + .get( + self.curr_root + .as_ref() + .map(|val| val[0..32].try_into().unwrap()), + &commitment_hash[0..32].try_into().unwrap(), + ) + .map(|data| data.is_some()) + } + + pub fn search_item_inclusions( + &mut self, + commitment_hashes: &[CommitmentHashType], + ) -> Result, monotree::Errors> { + let mut inclusions = vec![]; + + for nullifier_hash in commitment_hashes { + let is_included = self + .tree + .get( + self.curr_root + .as_ref() + .map(|val| val[0..32].try_into().unwrap()), + nullifier_hash[0..32].try_into().unwrap(), + ) + .map(|data| data.is_some())?; + + inclusions.push(is_included); + } + + Ok(inclusions) + } + + pub fn get_non_membership_proof( + &mut self, + commitment_hash: CommitmentHashType, + ) -> Result<(Option, Option), monotree::Errors> { + let is_member = self.search_item_inclusion(commitment_hash.clone())?; + + if is_member { + Err(monotree::Errors::new("Is a member")) + } else { + Ok(( + self.tree.get_merkle_proof( + self.curr_root + .as_ref() + .map(|val| val[0..32].try_into().unwrap()), + &commitment_hash, + )?, + self.curr_root.clone(), + )) + } + } + + #[allow(clippy::type_complexity)] + pub fn get_non_membership_proofs( + &mut self, + commitment_hashes: &[CommitmentHashType], + ) -> Result, Option)>, monotree::Errors> { + let mut non_membership_proofs = vec![]; + + for commitment_hash in commitment_hashes { + let is_member = self.search_item_inclusion(commitment_hash.clone())?; + + if is_member { + return Err(monotree::Errors::new( + format!("{commitment_hash:?} Is a member").as_str(), + )); + } else { + non_membership_proofs.push(( + self.tree.get_merkle_proof( + self.curr_root + .as_ref() + .map(|val| val[0..32].try_into().unwrap()), + commitment_hash, + )?, + self.curr_root.clone(), + )) + }; + } + + Ok(non_membership_proofs) + } +} + +impl Default for CommitmentsSparseMerkleTree { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::nullifier::UTXONullifier; + use monotree::database::MemoryDB; + use monotree::hasher::Blake3; + use monotree::Monotree; + + fn create_nullifier(hash: CommitmentHashType) -> Commitment { + Commitment { + commitment_hash: hash, + } + } + + #[test] + fn test_new_tree_initialization() { + let tree = CommitmentsSparseMerkleTree::new(); + assert!(tree.curr_root.is_none()); + } + + #[test] + fn test_insert_single_item() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let nullifier = create_nullifier([1u8; 32].to_vec()); // Sample 32-byte hash + + let result = tree.insert_item(nullifier); + assert!(result.is_ok()); + assert!(tree.curr_root.is_some()); + } + + #[test] + fn test_insert_multiple_items() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let nullifiers = vec![ + create_nullifier([1u8; 32].to_vec()), + create_nullifier([2u8; 32].to_vec()), + create_nullifier([3u8; 32].to_vec()), + ]; + + let result = tree.insert_items(nullifiers); + assert!(result.is_ok()); + assert!(tree.curr_root.is_some()); + } + + #[test] + fn test_search_item_inclusion() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let nullifier = create_nullifier([1u8; 32].to_vec()); + + tree.insert_item(nullifier.clone()).unwrap(); + + let result = tree.search_item_inclusion([1u8; 32].to_vec()); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), true); + + let non_existing = tree.search_item_inclusion([99u8; 32].to_vec()); + assert!(non_existing.is_ok()); + assert_eq!(non_existing.unwrap(), false); + } + + #[test] + fn test_search_multiple_item_inclusions() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let nullifiers = vec![ + create_nullifier([1u8; 32].to_vec()), + create_nullifier([2u8; 32].to_vec()), + create_nullifier([3u8; 32].to_vec()), + ]; + + tree.insert_items(nullifiers).unwrap(); + + let search_hashes = vec![[1u8; 32].to_vec(), [2u8; 32].to_vec(), [99u8; 32].to_vec()]; + let result = tree.search_item_inclusions(&search_hashes); + assert!(result.is_ok()); + + let expected_results = vec![true, true, false]; + assert_eq!(result.unwrap(), expected_results); + } + + #[test] + fn test_non_membership_proof() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let non_member_hash = [5u8; 32].to_vec(); + + let result = tree.get_non_membership_proof(non_member_hash); + assert!(result.is_ok()); + + let (proof, root) = result.unwrap(); + assert!(root.is_none()); + } + + #[test] + fn test_non_membership_proofs_multiple() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let non_member_hashes = vec![[5u8; 32].to_vec(), [6u8; 32].to_vec(), [7u8; 32].to_vec()]; + + let result = tree.get_non_membership_proofs(&non_member_hashes); + assert!(result.is_ok()); + + let proofs = result.unwrap(); + for (proof, root) in proofs { + assert!(root.is_none()); + } + } + + #[test] + fn test_insert_and_get_proof_of_existing_item() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let nullifier = create_nullifier([1u8; 32].to_vec()); + + tree.insert_item(nullifier.clone()).unwrap(); + + let proof_result = tree.get_non_membership_proof([1u8; 32].to_vec()); + assert!(proof_result.is_err()); + } + + #[test] + fn test_insert_and_get_proofs_of_existing_items() { + let mut tree = CommitmentsSparseMerkleTree::new(); + let nullifiers = vec![ + create_nullifier([1u8; 32].to_vec()), + create_nullifier([2u8; 32].to_vec()), + ]; + + tree.insert_items(nullifiers).unwrap(); + + let proof_result = + tree.get_non_membership_proofs(&[[1u8; 32].to_vec(), [2u8; 32].to_vec()]); + assert!(proof_result.is_err()); + } +} diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 684dcc8..2e87492 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -9,6 +9,8 @@ use rocksdb::{ }; pub mod block; +pub mod commitment; +pub mod commitments_sparse_merkle_tree; pub mod error; pub mod merkle_tree_public; pub mod nullifier; diff --git a/storage/src/merkle_tree_public/mod.rs b/storage/src/merkle_tree_public/mod.rs index 8a34631..f1fc89e 100644 --- a/storage/src/merkle_tree_public/mod.rs +++ b/storage/src/merkle_tree_public/mod.rs @@ -3,3 +3,4 @@ pub mod merkle_tree; pub mod tree_leav_item; pub type TreeHashType = [u8; 32]; +pub type CommitmentHashType = Vec; diff --git a/utxo/src/utxo_core.rs b/utxo/src/utxo_core.rs index 834c4c1..44a3a5d 100644 --- a/utxo/src/utxo_core.rs +++ b/utxo/src/utxo_core.rs @@ -6,19 +6,25 @@ use storage::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier, Accoun ///Raw asset data pub type Asset = Vec; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] ///Container for raw utxo payload pub struct UTXO { pub hash: TreeHashType, pub owner: AccountId, pub nullifier: Option, pub asset: Asset, + // TODO: change to u256 + pub amount: u128, + pub privacy_flag: bool, } #[derive(Debug, Clone, Serialize)] pub struct UTXOPayload { pub owner: AccountId, pub asset: Asset, + // TODO: change to u256 + pub amount: u128, + pub privacy_flag: bool, } impl UTXO { @@ -36,6 +42,8 @@ impl UTXO { owner: payload_with_asset.owner, nullifier: None, asset: payload_with_asset.asset, + amount: payload_with_asset.amount, + privacy_flag: payload_with_asset.privacy_flag, } } @@ -85,6 +93,8 @@ mod tests { name: "Test".to_string(), }) .unwrap(), + amount: 10, + privacy_flag: false, } } diff --git a/utxo/src/utxo_tree.rs b/utxo/src/utxo_tree.rs index b391fc0..89df4f4 100644 --- a/utxo/src/utxo_tree.rs +++ b/utxo/src/utxo_tree.rs @@ -83,6 +83,8 @@ mod tests { UTXOPayload { owner: AccountId::default(), asset: vec![1, 2, 3], + amount: 10, + privacy_flag: false, } }