diff --git a/sc_core/src/lib.rs b/sc_core/src/lib.rs index ab39e8d..ed7f610 100644 --- a/sc_core/src/lib.rs +++ b/sc_core/src/lib.rs @@ -2,3 +2,4 @@ pub mod cryptography; pub mod proofs_circuits; pub mod traits; pub mod transaction_payloads_tools; +pub mod public_context; diff --git a/sc_core/src/public_context.rs b/sc_core/src/public_context.rs new file mode 100644 index 0000000..438fa50 --- /dev/null +++ b/sc_core/src/public_context.rs @@ -0,0 +1,180 @@ +use std::collections::BTreeMap; + +use accounts::account_core::{AccountAddress, AccountPublicMask}; +use common::merkle_tree_public::TreeHashType; +use serde::{ser::SerializeStruct, Serialize}; + +pub const PUBLIC_SC_CONTEXT: &str = "PublicSCContext"; +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 COMMITMENT_STORE_ROOT: &str = "commitment_store_root"; +pub const PUT_TX_STORE_ROOT: &str = "put_tx_store_root"; + +///Strucutre, representing context, given to a smart contract on a call +pub struct PublicSCContext { + pub caller_address: AccountAddress, + pub caller_balance: u64, + pub account_masks: BTreeMap, + 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(PUBLIC_SC_CONTEXT, 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(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 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, + 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); + } + + #[test] + fn correct_u64_production_from_fit_vec() { + let le_vec = vec![1, 1, 1, 1, 2, 1, 1, 1]; + + let num = PublicSCContext::produce_u64_from_fit_vec(le_vec); + + assert_eq!(num, 72340177133043969); + } + + #[test] + fn correct_u64_production_from_small_vec() { + //7 items instead of 8 + let le_vec = vec![1, 1, 1, 1, 2, 1, 1]; + + let num = PublicSCContext::produce_u64_from_fit_vec(le_vec); + + assert_eq!(num, 282583095116033); + } + + #[test] + fn correct_u64_production_from_small_vec_le_bytes() { + //7 items instead of 8 + let le_vec = vec![1, 1, 1, 1, 2, 1, 1]; + let le_vec_res = [1, 1, 1, 1, 2, 1, 1, 0]; + + let num = PublicSCContext::produce_u64_from_fit_vec(le_vec); + + assert_eq!(num.to_le_bytes(), le_vec_res); + } + + #[test] + #[should_panic] + fn correct_u64_production_from_unfit_vec_should_panic() { + //9 items instead of 8 + let le_vec = vec![1, 1, 1, 1, 2, 1, 1, 1, 1]; + + PublicSCContext::produce_u64_from_fit_vec(le_vec); + } + + #[test] + fn consistent_len_of_context_commitments() { + let test_context = create_test_context(); + + let context_num_vec1 = test_context.produce_u64_list_from_context().unwrap(); + let context_num_vec2 = test_context.produce_u64_list_from_context().unwrap(); + + assert_eq!(context_num_vec1.len(), context_num_vec2.len()); + } +} diff --git a/template/zk_test_template/test_methods/guest/src/bin/template_sc.rs b/template/zk_test_template/test_methods/guest/src/bin/template_sc.rs index 839e092..86eb512 100644 --- a/template/zk_test_template/test_methods/guest/src/bin/template_sc.rs +++ b/template/zk_test_template/test_methods/guest/src/bin/template_sc.rs @@ -6,9 +6,6 @@ use {sc_name}::{SmartContract, InputParameters, PublicOutputs, PrivateOutputs}; use sc_core::traits::{IContract, IInputParameters, IPublicOutput, IPrivateOutput}; use sc_core::{{execution_type_trait}}; -//Not sure, how path will look like, so -//ToDo: Make it available from sc_core -//Curently in node_core use sc_core::PublicSCContext; //Not sure, how path will look like, so