diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 3f6ac0d..1bedd55 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -29,7 +29,7 @@ pub struct Account { ///A strucure, which represents all the visible(public) information /// /// known to each node about account `address` -#[derive(Serialize)] +#[derive(Serialize, Clone)] pub struct AccountPublicMask { pub nullifier_public_key: AffinePoint, pub viewing_public_key: AffinePoint, diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 371360d..c008469 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -9,12 +9,9 @@ use ::storage::transaction::{Transaction, TransactionPayload, TxKind}; use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; use config::NodeConfig; -use executions::{ - de::new_commitment_vec, - private_exec::{generate_commitments, generate_nullifiers}, -}; +use executions::private_exec::{generate_commitments, generate_nullifiers}; use log::info; -use rand::Rng; +use sc_core::proofs_circuits::pedersen_commitment_vec; use sequencer_client::{json::SendTxResponse, SequencerClient}; use serde::{Deserialize, Serialize}; use storage::NodeChainStore; @@ -216,8 +213,6 @@ impl NodeCore { // TODO: fix address when correspoding method will be added let sc_addr = ""; - let mut rng = rand::thread_rng(); - let secret_r: [u8; 32] = rng.gen(); let sc_state = acc_map_read_guard .block_store @@ -231,15 +226,13 @@ impl NodeCore { let context = acc_map_read_guard.produce_context(account.address); - let serialized_context = serde_json::to_vec(&context).unwrap(); - - let serialized_context_u64 = vec_u8_to_vec_u64(serialized_context); - - vec_values_u64.push(serialized_context_u64); + //Will not panic, as PublicScContext is serializable + let context_public_info: Vec = context.produce_u64_list_from_context().unwrap(); + vec_values_u64.push(context_public_info); let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); - let (tweak, secret_r, commitment) = new_commitment_vec(vec_public_info, &secret_r); + let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( TransactionPayload { @@ -260,7 +253,7 @@ impl NodeCore { ephemeral_pub_key: eph_pub_key.to_vec(), commitment, tweak, - secret_r: secret_r.try_into().unwrap(), + secret_r, } .into(), result_hash, @@ -305,8 +298,6 @@ impl NodeCore { // TODO: fix address when correspoding method will be added let sc_addr = ""; - let mut rng = rand::thread_rng(); - let secret_r: [u8; 32] = rng.gen(); let sc_state = acc_map_read_guard .block_store @@ -320,15 +311,13 @@ impl NodeCore { let context = acc_map_read_guard.produce_context(account.address); - let serialized_context = serde_json::to_vec(&context).unwrap(); - - let serialized_context_u64 = vec_u8_to_vec_u64(serialized_context); - - vec_values_u64.push(serialized_context_u64); + //Will not panic, as PublicScContext is serializable + let context_public_info: Vec = context.produce_u64_list_from_context().unwrap(); + vec_values_u64.push(context_public_info); let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); - let (tweak, secret_r, commitment) = new_commitment_vec(vec_public_info, &secret_r); + let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( TransactionPayload { @@ -349,7 +338,7 @@ impl NodeCore { ephemeral_pub_key: eph_pub_key.to_vec(), commitment, tweak, - secret_r: secret_r.try_into().unwrap(), + secret_r, } .into(), result_hashes, @@ -414,8 +403,6 @@ impl NodeCore { // TODO: fix address when correspoding method will be added let sc_addr = ""; - let mut rng = rand::thread_rng(); - let secret_r: [u8; 32] = rng.gen(); let sc_state = acc_map_read_guard .block_store @@ -429,15 +416,13 @@ impl NodeCore { let context = acc_map_read_guard.produce_context(account.address); - let serialized_context = serde_json::to_vec(&context).unwrap(); - - let serialized_context_u64 = vec_u8_to_vec_u64(serialized_context); - - vec_values_u64.push(serialized_context_u64); + //Will not panic, as PublicScContext is serializable + let context_public_info: Vec = context.produce_u64_list_from_context().unwrap(); + vec_values_u64.push(context_public_info); let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); - let (tweak, secret_r, commitment) = new_commitment_vec(vec_public_info, &secret_r); + let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( TransactionPayload { @@ -458,7 +443,7 @@ impl NodeCore { ephemeral_pub_key: eph_pub_key.to_vec(), commitment, tweak, - secret_r: secret_r.try_into().unwrap(), + secret_r, } .into(), utxo_hashes, @@ -551,8 +536,6 @@ impl NodeCore { // TODO: fix address when correspoding method will be added let sc_addr = ""; - let mut rng = rand::thread_rng(); - let secret_r: [u8; 32] = rng.gen(); let sc_state = acc_map_read_guard .block_store @@ -566,15 +549,13 @@ impl NodeCore { let context = acc_map_read_guard.produce_context(account.address); - let serialized_context = serde_json::to_vec(&context).unwrap(); - - let serialized_context_u64 = vec_u8_to_vec_u64(serialized_context); - - vec_values_u64.push(serialized_context_u64); + //Will not panic, as PublicScContext is serializable + let context_public_info: Vec = context.produce_u64_list_from_context().unwrap(); + vec_values_u64.push(context_public_info); let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); - let (tweak, secret_r, commitment) = new_commitment_vec(vec_public_info, &secret_r); + let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( TransactionPayload { @@ -595,7 +576,7 @@ impl NodeCore { ephemeral_pub_key: eph_pub_key.to_vec(), commitment, tweak, - secret_r: secret_r.try_into().unwrap(), + secret_r, } .into(), utxo_hashes_receiver, @@ -668,9 +649,6 @@ impl NodeCore { // TODO: fix address when correspoding method will be added let sc_addr = ""; - let mut rng = rand::thread_rng(); - let secret_r: [u8; 32] = rng.gen(); - let sc_state = acc_map_read_guard .block_store .get_sc_sc_state(sc_addr) @@ -681,15 +659,15 @@ impl NodeCore { .map(|slice| vec_u8_to_vec_u64(slice.to_vec())) .collect(); - let serialized_context_u64 = vec_u8_to_vec_u64( - serde_json::to_vec(&acc_map_read_guard.produce_context(account.address)).unwrap(), - ); + let context = acc_map_read_guard.produce_context(account.address); - vec_values_u64.push(serialized_context_u64); + //Will not panic, as PublicScContext is serializable + let context_public_info: Vec = context.produce_u64_list_from_context().unwrap(); + vec_values_u64.push(context_public_info); let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); - let (tweak, secret_r, commitment) = new_commitment_vec(vec_public_info, &secret_r); + let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( TransactionPayload { @@ -716,7 +694,7 @@ impl NodeCore { ephemeral_pub_key: eph_pub_key.to_vec(), commitment, tweak, - secret_r: secret_r.try_into().unwrap(), + secret_r, } .into(), utxo_hashes, @@ -753,8 +731,6 @@ impl NodeCore { // TODO: fix address when correspoding method will be added let sc_addr = ""; - let mut rng = rand::thread_rng(); - let secret_r: [u8; 32] = rng.gen(); let sc_state = acc_map_read_guard .block_store @@ -768,15 +744,13 @@ impl NodeCore { let context = acc_map_read_guard.produce_context(account.address); - let serialized_context = serde_json::to_vec(&context).unwrap(); - - let serialized_context_u64 = vec_u8_to_vec_u64(serialized_context); - - vec_values_u64.push(serialized_context_u64); + //Will not panic, as PublicScContext is serializable + let context_public_info: Vec = context.produce_u64_list_from_context().unwrap(); + vec_values_u64.push(context_public_info); let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); - let (tweak, secret_r, commitment) = new_commitment_vec(vec_public_info, &secret_r); + let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(TransactionPayload { tx_kind: TxKind::Deshielded, @@ -796,7 +770,7 @@ impl NodeCore { ephemeral_pub_key: vec![], commitment, tweak, - secret_r: secret_r.try_into().unwrap(), + secret_r, } .into()) } @@ -862,6 +836,17 @@ impl NodeCore { //Considering proof time, needs to be done before proof let tx_roots = self.get_roots().await; + let public_context = { + let read_guard = self.storage.read().await; + + read_guard.produce_context(acc) + }; + + let (tweak, secret_r, commitment) = pedersen_commitment_vec( + //Will not panic, as public context is serializable + public_context.produce_u64_list_from_context().unwrap(), + ); + let tx: Transaction = sc_core::transaction_payloads_tools::create_public_transaction_payload( serde_json::to_vec(&ActionData::MintMoneyPublicTx(MintMoneyPublicTx { @@ -869,6 +854,9 @@ impl NodeCore { amount, })) .unwrap(), + commitment, + tweak, + secret_r, ) .into(); tx.log(); @@ -1367,8 +1355,6 @@ impl NodeCore { // TODO: fix address when correspoding method will be added let sc_addr = ""; - let mut rng = rand::thread_rng(); - let secret_r: [u8; 32] = rng.gen(); let sc_state = acc_map_read_guard .block_store @@ -1382,15 +1368,13 @@ impl NodeCore { let context = acc_map_read_guard.produce_context(account.address); - let serialized_context = serde_json::to_vec(&context).unwrap(); - - let serialized_context_u64 = vec_u8_to_vec_u64(serialized_context); - - vec_values_u64.push(serialized_context_u64); + //Will not panic, as PublicScContext is serializable + let context_public_info: Vec = context.produce_u64_list_from_context().unwrap(); + vec_values_u64.push(context_public_info); let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); - let (tweak, secret_r, commitment) = new_commitment_vec(vec_public_info, &secret_r); + let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( TransactionPayload { @@ -1412,7 +1396,7 @@ impl NodeCore { ephemeral_pub_key: eph_pub_key.to_vec(), commitment, tweak, - secret_r: secret_r.try_into().unwrap(), + secret_r, } .into(), utxo_hashes, diff --git a/node_core/src/storage/public_context.rs b/node_core/src/storage/public_context.rs index 560bf01..e21d261 100644 --- a/node_core/src/storage/public_context.rs +++ b/node_core/src/storage/public_context.rs @@ -1,11 +1,10 @@ use std::collections::BTreeMap; use accounts::account_core::{AccountAddress, AccountPublicMask}; -use serde::Serialize; +use serde::{ser::SerializeStruct, Serialize}; use storage::merkle_tree_public::TreeHashType; ///Strucutre, representing context, given to a smart contract on a call -#[derive(Serialize)] pub struct PublicSCContext { pub caller_address: AccountAddress, pub caller_balance: u64, @@ -14,3 +13,115 @@ pub struct PublicSCContext { pub comitment_store_root: TreeHashType, pub pub_tx_store_root: TreeHashType, } + +impl Serialize for PublicSCContext { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut account_masks_keys: Vec<[u8; 32]> = self.account_masks.keys().cloned().collect(); + account_masks_keys.sort(); + + let mut account_mask_values: Vec = + self.account_masks.values().cloned().collect(); + account_mask_values.sort_by(|left, right| left.address.cmp(&right.address)); + + let mut s = serializer.serialize_struct("PublicSCContext", 7)?; + + s.serialize_field("caller_address", &self.caller_address)?; + 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)?; + + s.end() + } +} + +impl PublicSCContext { + ///Produces `u64` from bytes in a vector + /// + /// Assumes, that vector of le_bytes + pub fn produce_u64_from_fit_vec(data: Vec) -> u64 { + let data_len = data.len(); + + assert!(data_len <= 8); + let mut le_bytes: [u8; 8] = [0; 8]; + + for (idx, item) in data.into_iter().enumerate() { + le_bytes[idx] = item + } + + u64::from_le_bytes(le_bytes) + } + + ///Produces vector of `u64` from context + pub fn produce_u64_list_from_context(&self) -> Result, serde_json::Error> { + let mut u64_list = vec![]; + + let ser_data = serde_json::to_vec(self)?; + + //`ToDo` Replace with `next_chunk` usage, when feature stabilizes in Rust + for i in 0..=(ser_data.len() / 8) { + let next_chunk: Vec; + + if (i + 1) * 8 < ser_data.len() { + next_chunk = ser_data[(i * 8)..((i + 1) * 8)].iter().cloned().collect(); + } else { + next_chunk = ser_data[(i * 8)..(ser_data.len())] + .iter() + .cloned() + .collect(); + } + + u64_list.push(PublicSCContext::produce_u64_from_fit_vec(next_chunk)); + } + + Ok(u64_list) + } +} + +#[cfg(test)] +mod tests { + use accounts::account_core::Account; + + use super::*; + + 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]; + + let mut account_masks = BTreeMap::new(); + + let acc_1 = Account::new(); + let acc_2 = Account::new(); + let acc_3 = Account::new(); + + account_masks.insert(acc_1.address, acc_1.make_account_public_mask()); + account_masks.insert(acc_2.address, acc_2.make_account_public_mask()); + account_masks.insert(acc_3.address, acc_3.make_account_public_mask()); + + PublicSCContext { + caller_address, + caller_balance: 100, + account_masks, + nullifier_store_root, + comitment_store_root, + pub_tx_store_root, + } + } + + #[test] + fn bin_ser_stability_test() { + let test_context = create_test_context(); + + let serialization_1 = serde_json::to_vec(&test_context).unwrap(); + let serialization_2 = serde_json::to_vec(&test_context).unwrap(); + + assert_eq!(serialization_1, serialization_2); + } +} diff --git a/sc_core/src/proofs_circuits.rs b/sc_core/src/proofs_circuits.rs index 7c646c2..6b2b367 100644 --- a/sc_core/src/proofs_circuits.rs +++ b/sc_core/src/proofs_circuits.rs @@ -2,7 +2,7 @@ use bincode; use k256::Scalar; use monotree::hasher::Blake3; use monotree::{Hasher, Monotree}; -use rand::thread_rng; +use rand::{thread_rng, RngCore}; use secp256k1_zkp::{CommitmentSecrets, Generator, PedersenCommitment, Tag, Tweak, SECP256K1}; use sha2::{Digest, Sha256}; use storage::{ @@ -168,6 +168,32 @@ pub fn check_balances(public_info: u128, output_utxos: &[UTXO]) -> bool { public_info == total_output } +// new_commitment for a Vec of values +pub fn pedersen_commitment_vec( + public_info_vec: Vec, +) -> (Tweak, [u8; 32], Vec) { + let mut random_val: [u8; 32] = [0; 32]; + thread_rng().fill_bytes(&mut random_val); + + 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(&random_val).unwrap(), + generator_blinding_factor, + }; + + commit(&commitment_secrets, tag) + }) + .collect(); + + (generator_blinding_factor, random_val, vec_commitments) +} + // Verify Pedersen commitment // takes the public_info, secret_r and pedersen_commitment and diff --git a/sc_core/src/transaction_payloads_tools.rs b/sc_core/src/transaction_payloads_tools.rs index 5c7d480..c84cc6f 100644 --- a/sc_core/src/transaction_payloads_tools.rs +++ b/sc_core/src/transaction_payloads_tools.rs @@ -2,18 +2,18 @@ use accounts::account_core::Account; use anyhow::Result; use rand::thread_rng; use risc0_zkvm::Receipt; -use secp256k1_zkp::{CommitmentSecrets, Generator, PedersenCommitment, Tweak, SECP256K1}; +use secp256k1_zkp::{CommitmentSecrets, PedersenCommitment, Tweak}; use storage::transaction::{TransactionPayload, TxKind}; use utxo::utxo_core::UTXO; use crate::proofs_circuits::{commit, generate_nullifiers, tag_random}; -pub fn create_public_transaction_payload(execution_input: Vec) -> TransactionPayload { - let tag = tag_random(); - let unblinded_gen = Generator::new_unblinded(SECP256K1, tag); - - let mut rng = rand::thread_rng(); - +pub fn create_public_transaction_payload( + execution_input: Vec, + commitment: Vec, + tweak: Tweak, + secret_r: [u8; 32], +) -> TransactionPayload { TransactionPayload { tx_kind: TxKind::Public, execution_input, @@ -24,13 +24,9 @@ pub fn create_public_transaction_payload(execution_input: Vec) -> Transactio execution_proof_private: "".to_string(), encoded_data: vec![], ephemeral_pub_key: vec![], - commitment: vec![PedersenCommitment::new_unblinded( - SECP256K1, - 0, - unblinded_gen, - )], - tweak: Tweak::new(&mut rng), - secret_r: [0; 32], + commitment, + tweak, + secret_r, } } diff --git a/storage/src/transaction.rs b/storage/src/transaction.rs index 0b7d26e..149ddc0 100644 --- a/storage/src/transaction.rs +++ b/storage/src/transaction.rs @@ -1,5 +1,5 @@ use log::info; -use secp256k1_zkp::{rand, PedersenCommitment, Tweak}; +use secp256k1_zkp::{PedersenCommitment, Tweak}; use serde::{Deserialize, Serialize}; use sha2::{digest::FixedOutput, Digest}; @@ -90,8 +90,6 @@ impl From for Transaction { let hash = ::from(hasher.finalize_fixed()); - let mut rng = rand::thread_rng(); - Self { hash, tx_kind: value.tx_kind, @@ -104,8 +102,8 @@ impl From for Transaction { encoded_data: value.encoded_data, ephemeral_pub_key: value.ephemeral_pub_key, commitment: value.commitment, - tweak: Tweak::new(&mut rng), - secret_r: [0; 32], + tweak: value.tweak, + secret_r: value.secret_r, } } }