fix: smt/imt replaced with sets

This commit is contained in:
Oleksandr Pravdyvyi
2025-05-23 09:04:04 +03:00
parent 00297db3ab
commit cb22a95df2
17 changed files with 35 additions and 1127 deletions
+7 -8
View File
@@ -1,5 +1,5 @@
use std::{
collections::{BTreeMap, HashMap},
collections::{BTreeMap, HashMap, HashSet},
path::Path,
};
@@ -8,7 +8,6 @@ use anyhow::Result;
use block_store::NodeBlockStore;
use common::{
block::Block,
indexed_merkle_tree::IndexedMerkleTreeWrapper,
merkle_tree_public::merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree},
nullifier::UTXONullifier,
utxo_commitment::UTXOCommitment,
@@ -26,7 +25,7 @@ pub mod public_context;
pub struct NodeChainStore {
pub acc_map: HashMap<AccountAddress, Account>,
pub block_store: NodeBlockStore,
pub nullifier_store: IndexedMerkleTreeWrapper,
pub nullifier_store: HashSet<UTXONullifier>,
pub utxo_commitments_store: UTXOCommitmentsMerkleTree,
pub pub_tx_store: PublicTransactionMerkleTree,
}
@@ -34,7 +33,7 @@ pub struct NodeChainStore {
impl NodeChainStore {
pub fn new_with_genesis(home_dir: &Path, genesis_block: Block) -> Self {
let acc_map = HashMap::new();
let nullifier_store = IndexedMerkleTreeWrapper::default();
let nullifier_store = HashSet::new();
let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]);
let pub_tx_store = PublicTransactionMerkleTree::new(vec![]);
@@ -54,7 +53,7 @@ impl NodeChainStore {
}
pub fn dissect_insert_block(&mut self, block: Block) -> Result<()> {
for (tx_id, tx) in block.transactions.iter().enumerate() {
for tx in &block.transactions {
if !tx.execution_input.is_empty() {
let public_action = serde_json::from_slice::<ActionData>(&tx.execution_input);
@@ -97,8 +96,9 @@ impl NodeChainStore {
.collect(),
);
self.nullifier_store
.insert_items(tx.nullifier_created_hashes.clone())?;
for nullifier in tx.nullifier_created_hashes.iter() {
self.nullifier_store.insert(UTXONullifier { utxo_hash: *nullifier });
}
if !tx.encoded_data.is_empty() {
let ephemeral_public_key_sender =
@@ -148,7 +148,6 @@ impl NodeChainStore {
caller_address: caller,
caller_balance: self.acc_map.get(&caller).unwrap().balance,
account_masks,
nullifier_store_root: self.nullifier_store.get_curr_root().unwrap_or([0; 32]),
comitment_store_root: self.utxo_commitments_store.get_root().unwrap_or([0; 32]),
pub_tx_store_root: self.pub_tx_store.get_root().unwrap_or([0; 32]),
}
@@ -9,7 +9,6 @@ pub const CALLER_ADDRESS: &str = "caller_address";
pub const CALLER_BALANCE: &str = "caller_balance";
pub const ACCOUNT_MASKS_KEYS_SORTED: &str = "account_masks_keys_sorted";
pub const ACCOUNT_MASKS_VALUES_SORTED: &str = "account_masks_values_sorted";
pub const NULLIFIER_STORE_ROOT: &str = "nullifier_store_root";
pub const COMMITMENT_STORE_ROOT: &str = "commitment_store_root";
pub const PUT_TX_STORE_ROOT: &str = "put_tx_store_root";
@@ -18,7 +17,6 @@ pub struct PublicSCContext {
pub caller_address: AccountAddress,
pub caller_balance: u64,
pub account_masks: BTreeMap<AccountAddress, AccountPublicMask>,
pub nullifier_store_root: TreeHashType,
pub comitment_store_root: TreeHashType,
pub pub_tx_store_root: TreeHashType,
}
@@ -41,7 +39,6 @@ impl Serialize for PublicSCContext {
s.serialize_field(CALLER_BALANCE, &self.caller_balance)?;
s.serialize_field(ACCOUNT_MASKS_KEYS_SORTED, &account_masks_keys)?;
s.serialize_field(ACCOUNT_MASKS_VALUES_SORTED, &account_mask_values)?;
s.serialize_field(NULLIFIER_STORE_ROOT, &self.nullifier_store_root)?;
s.serialize_field(COMMITMENT_STORE_ROOT, &self.comitment_store_root)?;
s.serialize_field(PUT_TX_STORE_ROOT, &self.pub_tx_store_root)?;
@@ -100,7 +97,6 @@ mod tests {
fn create_test_context() -> PublicSCContext {
let caller_address = [1; 32];
let nullifier_store_root = [2; 32];
let comitment_store_root = [3; 32];
let pub_tx_store_root = [4; 32];
@@ -118,7 +114,6 @@ mod tests {
caller_address,
caller_balance: 100,
account_masks,
nullifier_store_root,
comitment_store_root,
pub_tx_store_root,
}
-217
View File
@@ -1,217 +0,0 @@
use std::collections::BTreeMap;
use bincode;
use common::{
commitment::Commitment, indexed_merkle_tree::IndexedMerkleTreeWrapper, nullifier::UTXONullifier,
};
use k256::Scalar;
use rand::thread_rng;
use secp256k1_zkp::{CommitmentSecrets, Generator, PedersenCommitment, Tag, Tweak, SECP256K1};
use sha2::{Digest, Sha256};
use utxo::utxo_core::UTXO;
#[allow(unused)]
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<u8> {
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<u8> {
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<Vec<u8>> {
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<u8>,
root_commitment: Vec<u8>,
in_commitments_proof: &[Vec<u8>],
) -> 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 = IndexedMerkleTreeWrapper::new();
let commitments: Vec<_> = in_commitments_proof
.into_iter()
.map(|n_p| n_p.clone().try_into().unwrap())
.collect();
nsmt.insert_items(commitments).unwrap();
nsmt.get_non_membership_proof(in_commitment.clone().try_into().unwrap())
.is_ok()
}
// 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 = IndexedMerkleTreeWrapper::new();
let commitments: Vec<_> = nullifiers_proof
.into_iter()
.map(|n_p| n_p.clone().try_into().unwrap())
.collect();
nsmt.insert_items(commitments).unwrap();
nsmt.get_non_membership_proof(nullifier.clone().try_into().unwrap())
.is_ok()
}
// 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
}
// new_commitment
pub fn new_commitment(public_info: u64, secret_r: &[u8]) -> (Tweak, &[u8], PedersenCommitment) {
let generator_blinding_factor = Tweak::new(&mut thread_rng());
let commitment_secrets = CommitmentSecrets {
value: public_info,
value_blinding_factor: Tweak::from_slice(secret_r).unwrap(),
generator_blinding_factor,
};
let tag = tag_random();
let commitment = commit(&commitment_secrets, tag);
(generator_blinding_factor, secret_r, commitment)
}
// new_commitment for a Vec of values
pub fn new_commitment_vec(
public_info_vec: Vec<u64>,
secret_r: &[u8],
) -> (Tweak, &[u8], Vec<PedersenCommitment>) {
let generator_blinding_factor = Tweak::new(&mut thread_rng());
let tag = tag_random();
let vec_commitments = public_info_vec
.into_iter()
.map(|public_info| {
let commitment_secrets = CommitmentSecrets {
value: public_info,
value_blinding_factor: Tweak::from_slice(secret_r).unwrap(),
generator_blinding_factor,
};
commit(&commitment_secrets, tag)
})
.collect();
(generator_blinding_factor, secret_r, vec_commitments)
}
#[allow(unused)]
fn de_kernel(
root_commitment: &[u8],
root_nullifier: [u8; 32],
public_info: u64,
input_utxos: &[UTXO],
in_commitments_proof: &[Vec<u8>],
nullifiers_proof: &[[u8; 32]],
nullifier_secret_key: Scalar,
) -> (Vec<u8>, Vec<Vec<u8>>) {
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)
}
-3
View File
@@ -1,3 +0,0 @@
pub mod de;
pub mod private_exec;
pub mod se;
-118
View File
@@ -1,118 +0,0 @@
use std::collections::BTreeMap;
use bincode;
use common::{
commitment::Commitment, indexed_merkle_tree::IndexedMerkleTreeWrapper, nullifier::UTXONullifier,
};
use k256::Scalar;
use sha2::{Digest, Sha256};
use utxo::utxo_core::UTXO;
fn hash(input: &[u8]) -> Vec<u8> {
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<u8> {
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<Vec<u8>> {
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<u8>,
root_commitment: Vec<u8>,
in_commitments_proof: &[Vec<u8>],
) -> 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 = IndexedMerkleTreeWrapper::new();
let commitments: Vec<_> = in_commitments_proof
.into_iter()
.map(|n_p| n_p.clone().try_into().unwrap())
.collect();
nsmt.insert_items(commitments).unwrap();
nsmt.get_non_membership_proof(in_commitment.clone().try_into().unwrap())
.is_ok()
}
// 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 = IndexedMerkleTreeWrapper::new();
let commitments: Vec<_> = nullifiers_proof
.into_iter()
.map(|n_p| n_p.clone().try_into().unwrap())
.collect();
nsmt.insert_items(commitments).unwrap();
nsmt.get_non_membership_proof(nullifier.clone().try_into().unwrap())
.is_ok()
}
#[allow(unused)]
fn private_kernel(
root_commitment: &[u8],
root_nullifier: [u8; 32],
input_utxos: &[UTXO],
in_commitments_proof: &[Vec<u8>],
nullifiers_proof: &[[u8; 32]],
nullifier_secret_key: Scalar,
) -> (Vec<u8>, Vec<Vec<u8>>) {
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)
}
-171
View File
@@ -1,171 +0,0 @@
use std::collections::BTreeMap;
use bincode;
use common::{
commitment::Commitment, indexed_merkle_tree::IndexedMerkleTreeWrapper, nullifier::UTXONullifier,
};
use k256::Scalar;
use rand::thread_rng;
use secp256k1_zkp::{CommitmentSecrets, Generator, PedersenCommitment, Tag, Tweak, SECP256K1};
use sha2::{Digest, Sha256};
use utxo::utxo_core::UTXO;
#[allow(unused)]
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<u8> {
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<u8> {
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<Vec<u8>> {
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<u8>,
in_commitments_proof: &[Vec<u8>],
) -> bool {
let mut nsmt = IndexedMerkleTreeWrapper::new();
let commitments: Vec<_> = in_commitments_proof
.into_iter()
.map(|n_p| n_p.clone().try_into().unwrap())
.collect();
nsmt.insert_items(commitments).unwrap();
nsmt.get_non_membership_proof(pedersen_commitment.serialize().to_vec().try_into().unwrap())
.is_ok()
}
// 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 = IndexedMerkleTreeWrapper::new();
let commitments: Vec<_> = nullifiers_proof
.into_iter()
.map(|n_p| n_p.clone().try_into().unwrap())
.collect();
nsmt.insert_items(commitments).unwrap();
nsmt.get_non_membership_proof(nullifier.clone().try_into().unwrap())
.is_ok()
}
// 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
}
#[allow(unused)]
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<u8>],
nullifiers_proof: &[[u8; 32]],
nullifier_secret_key: Scalar,
) -> (Vec<u8>, Vec<Vec<u8>>, Vec<u8>) {
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)
}
+3 -6
View File
@@ -10,9 +10,8 @@ use anyhow::Result;
use chain_storage::NodeChainStore;
use common::transaction::{Transaction, TransactionPayload, TxKind};
use config::NodeConfig;
use executions::private_exec::{generate_commitments, generate_nullifiers};
use log::info;
use sc_core::proofs_circuits::pedersen_commitment_vec;
use sc_core::proofs_circuits::{generate_commitments, generate_nullifiers, generate_nullifiers_se, pedersen_commitment_vec};
use sequencer_client::{json::SendTxResponse, SequencerClient};
use serde::{Deserialize, Serialize};
use storage::sc_db_utils::DataBlobChangeVariant;
@@ -28,7 +27,6 @@ pub const BLOCK_GEN_DELAY_SECS: u64 = 20;
pub mod chain_storage;
pub mod config;
pub mod executions;
///Module, which includes pre start setup helperfunctions
pub mod pre_start;
pub mod sequencer_client;
@@ -164,10 +162,9 @@ impl NodeCore {
})
}
pub async fn get_roots(&self) -> [[u8; 32]; 3] {
pub async fn get_roots(&self) -> [[u8; 32]; 2] {
let storage = self.storage.read().await;
[
storage.nullifier_store.get_curr_root().unwrap_or([0; 32]),
storage.utxo_commitments_store.get_root().unwrap_or([0; 32]),
storage.pub_tx_store.get_root().unwrap_or([0; 32]),
]
@@ -651,7 +648,7 @@ impl NodeCore {
)
.unwrap();
let nullifier = executions::se::generate_nullifiers(
let nullifier = generate_nullifiers_se(
&commitment,
&account
.key_holder
+2 -2
View File
@@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct SendTxRequest {
pub transaction: Transaction,
///Nullifier Root, UTXO Commitment Root, Pub Tx Root
pub tx_roots: [[u8; 32]; 3],
///UTXO Commitment Root, Pub Tx Root
pub tx_roots: [[u8; 32]; 2],
}
//Responses
+1 -1
View File
@@ -73,7 +73,7 @@ impl SequencerClient {
pub async fn send_tx(
&self,
transaction: Transaction,
tx_roots: [[u8; 32]; 3],
tx_roots: [[u8; 32]; 2],
) -> Result<SendTxResponse, SequencerClientError> {
let tx_req = SendTxRequest {
transaction,