From 1f72b585df7375ad34c28c21c0d7de97afc1da52 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 3 Jan 2025 08:13:59 +0200 Subject: [PATCH 1/3] fix: logging fix 1 --- accounts/src/account_core/mod.rs | 2 +- .../key_management/ephemeral_key_holder.rs | 5 ++++ accounts/src/key_management/mod.rs | 25 +++++++++++-------- accounts/src/key_management/secret_holders.rs | 2 +- node_core/src/lib.rs | 25 ++++++++++--------- storage/src/transaction.rs | 8 +++--- utxo/src/utxo_core.rs | 2 +- 7 files changed, 39 insertions(+), 30 deletions(-) diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 5f13abf..b478550 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -118,7 +118,7 @@ impl Account { } pub fn log(&self) { - self.key_holder.log(); + info!("Keys generated"); info!("Account address is {:?}", hex::encode(self.address)); info!("Account balance is {:?}", self.balance); } diff --git a/accounts/src/key_management/ephemeral_key_holder.rs b/accounts/src/key_management/ephemeral_key_holder.rs index 83b4a95..c464e5c 100644 --- a/accounts/src/key_management/ephemeral_key_holder.rs +++ b/accounts/src/key_management/ephemeral_key_holder.rs @@ -2,6 +2,7 @@ use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, Key, KeyInit}; use elliptic_curve::group::GroupEncoding; use elliptic_curve::PrimeField; use k256::{AffinePoint, FieldBytes, Scalar}; +use log::info; use rand::{rngs::OsRng, RngCore}; use super::constants_types::{CipherText, Nonce}; @@ -51,4 +52,8 @@ impl EphemeralKeyHolder { (cipher.encrypt(&nonce, data).unwrap(), nonce) } + + pub fn log(&self) { + info!("Ephemeral private key is {:?}", hex::encode(self.ephemeral_secret_key.to_bytes())); + } } diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 68686da..ed3660f 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -78,21 +78,24 @@ impl AddressKeyHolder { pub fn log(&self) { info!( - "AddressKeyHolder top_secret_key_holder is {:?}", - self.top_secret_key_holder + "Secret spending key is {:?}", + hex::encode(self.top_secret_key_holder.secret_spending_key.to_bytes()), ); info!( - "AddressKeyHolder utxo_secret_key_holder is {:?}", - self.utxo_secret_key_holder - ); - info!("AddressKeyHolder address is {:?}", self.address); - info!( - "AddressKeyHolder nullifer_public_key is {:?}", - self.nullifer_public_key + "Nulifier secret key is {:?}", + hex::encode(self.utxo_secret_key_holder.nullifier_secret_key.to_bytes()), ); info!( - "AddressKeyHolder viewing_public_key is {:?}", - self.viewing_public_key + "Viewing secret key is {:?}", + hex::encode(self.utxo_secret_key_holder.viewing_secret_key.to_bytes()), + ); + info!( + "Nullifier public key is {:?}", + hex::encode(self.nullifer_public_key.to_bytes()), + ); + info!( + "Viewing public key is {:?}", + hex::encode(self.viewing_public_key.to_bytes()), ); } } diff --git a/accounts/src/key_management/secret_holders.rs b/accounts/src/key_management/secret_holders.rs index 25e1d86..c1b48b5 100644 --- a/accounts/src/key_management/secret_holders.rs +++ b/accounts/src/key_management/secret_holders.rs @@ -17,7 +17,7 @@ pub struct SeedHolder { #[derive(Debug, Clone)] ///Secret spending key holder. Produces `UTXOSecretKeyHolder` objects. pub struct TopSecretKeyHolder { - secret_spending_key: Scalar, + pub secret_spending_key: Scalar, } #[derive(Debug, Clone)] diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 5dc6e40..4a91bdc 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -165,9 +165,9 @@ impl NodeCore { let acc_map_read_guard = self.storage.read().await; let accout = acc_map_read_guard.acc_map.get(&acc).unwrap(); - accout.log(); let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + ephm_key_holder.log(); let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); @@ -190,7 +190,7 @@ impl NodeCore { .map(|hash_data| hash_data.try_into().unwrap()) .collect(), nullifier_created_hashes: vec![], - execution_proof_private: serde_json::to_string(&receipt).unwrap(), + execution_proof_private: hex::encode(serde_json::to_vec(&receipt).unwrap()), encoded_data: vec![(encoded_data.0, encoded_data.1.to_vec())], ephemeral_pub_key: eph_pub_key.to_vec(), } @@ -226,7 +226,6 @@ impl NodeCore { let acc_map_read_guard = self.storage.read().await; let accout = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); - accout.log(); let nullifier = generate_nullifiers( &utxo, @@ -250,6 +249,7 @@ impl NodeCore { .collect(); let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + ephm_key_holder.log(); let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); @@ -281,7 +281,7 @@ impl NodeCore { .map(|hash_data| hash_data.try_into().unwrap()) .collect(), nullifier_created_hashes: vec![nullifier.try_into().unwrap()], - execution_proof_private: serde_json::to_string(&receipt).unwrap(), + execution_proof_private: hex::encode(serde_json::to_vec(&receipt).unwrap()), encoded_data, ephemeral_pub_key: eph_pub_key.to_vec(), } @@ -299,7 +299,6 @@ impl NodeCore { let acc_map_read_guard = self.storage.read().await; let accout = acc_map_read_guard.acc_map.get(&acc).unwrap(); - accout.log(); let commitment_secrets = CommitmentSecrets { value: balance, @@ -340,6 +339,7 @@ impl NodeCore { .collect(); let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + ephm_key_holder.log(); let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); @@ -377,7 +377,7 @@ impl NodeCore { .map(|hash_data| hash_data.try_into().unwrap()) .collect(), nullifier_created_hashes: vec![nullifier.try_into().unwrap()], - execution_proof_private: serde_json::to_string(&receipt).unwrap(), + execution_proof_private: hex::encode(serde_json::to_vec(&receipt).unwrap()), encoded_data, ephemeral_pub_key: eph_pub_key.to_vec(), } @@ -401,7 +401,6 @@ impl NodeCore { .hash; let accout = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); - accout.log(); let nullifier = generate_nullifiers( &utxo, @@ -427,7 +426,7 @@ impl NodeCore { utxo_commitments_spent_hashes: vec![commitment_in], utxo_commitments_created_hashes: vec![], nullifier_created_hashes: vec![nullifier.try_into().unwrap()], - execution_proof_private: serde_json::to_string(&receipt).unwrap(), + execution_proof_private: hex::encode(serde_json::to_vec(&receipt).unwrap()), encoded_data: vec![], ephemeral_pub_key: vec![], } @@ -461,9 +460,12 @@ impl NodeCore { acc: AccountAddress, amount: u128, ) -> Result { + let tx = self.deposit_money_public(acc, amount); + tx.log(); + Ok(self .sequencer_client - .send_tx(self.deposit_money_public(acc, amount)) + .send_tx(tx) .await?) } @@ -523,7 +525,6 @@ impl NodeCore { ///Mint utxo, make it public pub async fn subscenario_1(&mut self) { let acc_addr = self.create_new_account().await; - info!("Account created {acc_addr:?}"); let (resp, new_utxo_hash, comm_gen_hash) = self.send_private_mint_tx(acc_addr, 100).await.unwrap(); @@ -536,7 +537,6 @@ impl NodeCore { let mut write_guard = self.storage.write().await; let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); - acc.log(); acc.utxo_tree .get_item(new_utxo_hash) @@ -560,7 +560,6 @@ impl NodeCore { let acc_map_read_guard = self.storage.read().await; let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); - acc.log(); acc.balance }; @@ -665,6 +664,7 @@ impl NodeCore { .unwrap() .clone() }; + new_utxo.log(); info!("User {acc_addr_rec:?} received new utxo {new_utxo:?}"); } @@ -711,6 +711,7 @@ impl NodeCore { .unwrap() .clone() }; + new_utxo.log(); info!("User {acc_addr_rec:?} received new utxo {new_utxo:?}"); } diff --git a/storage/src/transaction.rs b/storage/src/transaction.rs index 545b14e..ed8da27 100644 --- a/storage/src/transaction.rs +++ b/storage/src/transaction.rs @@ -108,28 +108,28 @@ impl Transaction { self.utxo_commitments_spent_hashes .iter() .map(|val| hex::encode(val.clone())) + .collect::>() ); info!( "Transaction utxo_commitments_created_hashes is {:?}", self.utxo_commitments_created_hashes .iter() .map(|val| hex::encode(val.clone())) + .collect::>() ); info!( "Transaction nullifier_created_hashes is {:?}", self.nullifier_created_hashes .iter() .map(|val| hex::encode(val.clone())) - ); - info!( - "Transaction execution_proof_private is {:?}", - self.execution_proof_private + .collect::>() ); info!( "Transaction encoded_data is {:?}", self.encoded_data .iter() .map(|val| (hex::encode(val.0.clone()), hex::encode(val.1.clone()))) + .collect::>() ); info!( "Transaction ephemeral_pub_key is {:?}", diff --git a/utxo/src/utxo_core.rs b/utxo/src/utxo_core.rs index 6a03ee4..745950c 100644 --- a/utxo/src/utxo_core.rs +++ b/utxo/src/utxo_core.rs @@ -73,7 +73,7 @@ impl UTXO { pub fn log(&self) { info!("UTXO hash is {:?}", hex::encode(self.hash)); - info!("UTXO owner is {:?}", self.owner); + info!("UTXO owner is {:?}", hex::encode(self.owner)); info!( "UTXO nullifier is {:?}", self.nullifier.clone().map(|val| hex::encode(val.utxo_hash)) From 31f782f2140e8dbf339f0855f62275587c0eebd0 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 3 Jan 2025 10:44:06 +0200 Subject: [PATCH 2/3] fix: complex scenario 1 --- accounts/src/account_core/mod.rs | 2 +- node_core/src/lib.rs | 568 +++++++++++++++++++++--------- node_core/src/storage/mod.rs | 8 +- node_rpc/src/process.rs | 21 +- node_rpc/src/types/rpc_structs.rs | 12 + storage/src/transaction.rs | 16 +- 6 files changed, 451 insertions(+), 176 deletions(-) diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index b478550..699806c 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use anyhow::Result; use k256::AffinePoint; use log::info; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use storage::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier}; use utxo::{ utxo_core::{UTXOPayload, UTXO}, diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 4a91bdc..1289b6f 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -1,14 +1,11 @@ -use std::{ - collections::HashMap, - sync::{ - atomic::{AtomicU64, Ordering}, - Arc, - }, +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, }; use k256::elliptic_curve::group::GroupEncoding; -use ::storage::transaction::{Transaction, TransactionPayload, TxKind}; +use ::storage::{nullifier::UTXONullifier, transaction::{Transaction, TransactionPayload, TxKind}}; use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; use config::NodeConfig; @@ -23,7 +20,7 @@ use sequencer_client::{json::SendTxResponse, SequencerClient}; use serde::{Deserialize, Serialize}; use storage::NodeChainStore; use tokio::{sync::RwLock, task::JoinHandle}; -use utxo::utxo_core::UTXO; +use utxo::utxo_core::{Asset, UTXO}; use zkvm::{ prove_mint_utxo, prove_send_utxo, prove_send_utxo_deshielded, prove_send_utxo_shielded, }; @@ -52,11 +49,17 @@ pub struct SendMoneyDeshieldedTx { pub receiver_data: Vec<(u128, AccountAddress)>, } +#[derive(Debug, Serialize, Deserialize)] +pub struct UTXOPublication { + pub utxos: Vec, +} + #[derive(Debug, Serialize, Deserialize)] pub enum ActionData { MintMoneyPublicTx(MintMoneyPublicTx), SendMoneyShieldedTx(SendMoneyShieldedTx), SendMoneyDeshieldedTx(SendMoneyDeshieldedTx), + UTXOPublication(UTXOPublication), } pub struct NodeCore { @@ -463,10 +466,7 @@ impl NodeCore { let tx = self.deposit_money_public(acc, amount); tx.log(); - Ok(self - .sequencer_client - .send_tx(tx) - .await?) + Ok(self.sequencer_client.send_tx(tx).await?) } pub async fn send_private_send_tx( @@ -522,12 +522,13 @@ impl NodeCore { Ok(self.sequencer_client.send_tx(tx).await?) } - ///Mint utxo, make it public - pub async fn subscenario_1(&mut self) { - let acc_addr = self.create_new_account().await; - + async fn operate_account_mint_private( + &mut self, + acc_addr: AccountAddress, + amount: u128, + ) -> (UTXO, [u8; 32]) { let (resp, new_utxo_hash, comm_gen_hash) = - self.send_private_mint_tx(acc_addr, 100).await.unwrap(); + self.send_private_mint_tx(acc_addr, amount).await.unwrap(); info!("Response for mint private is {resp:?}"); info!("Awaiting new blocks"); @@ -546,9 +547,43 @@ impl NodeCore { }; new_utxo.log(); + info!( + "Account address is {:?} ,new utxo owner address is {:?}", + hex::encode(acc_addr), + hex::encode(new_utxo.owner) + ); + info!( + "Account {:?} got new utxo with amount {amount:?}", + hex::encode(acc_addr) + ); + + (new_utxo, comm_gen_hash) + } + + async fn operate_account_send_deshielded_one_receiver( + &mut self, + acc_addr_sender: AccountAddress, + acc_addr_rec: AccountAddress, + utxo: UTXO, + comm_gen_hash: [u8; 32], + ) { + let amount = utxo.amount; + + let old_balance = { + let acc_map_read_guard = self.storage.read().await; + + let acc = acc_map_read_guard.acc_map.get(&acc_addr_rec).unwrap(); + + acc.balance + }; + + info!( + "Balance of receiver {:?} now is {old_balance:?}", + hex::encode(acc_addr_rec) + ); let resp = self - .send_deshielded_send_tx(new_utxo, comm_gen_hash, vec![(100, acc_addr)]) + .send_deshielded_send_tx(utxo, comm_gen_hash, vec![(amount, acc_addr_rec)]) .await .unwrap(); info!("Response for send deshielded is {resp:?}"); @@ -556,6 +591,42 @@ impl NodeCore { info!("Awaiting new blocks"); tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + let new_balance = { + let acc_map_read_guard = self.storage.read().await; + + let acc = acc_map_read_guard.acc_map.get(&acc_addr_sender).unwrap(); + + acc.balance + }; + + info!( + "Balance of receiver {:?} now is {:?}, delta is {:?}", + hex::encode(acc_addr_rec), + new_balance, + new_balance - old_balance + ); + } + + async fn operate_account_deposit_public(&mut self, acc_addr: AccountAddress, amount: u128) { + let old_balance = { + let acc_map_read_guard = self.storage.read().await; + + let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); + + acc.balance + }; + + info!( + "Balance of {:?} now is {old_balance:?}", + hex::encode(acc_addr) + ); + + let resp = self.send_public_deposit(acc_addr, amount).await.unwrap(); + info!("Response for public deposit is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + let new_balance = { let acc_map_read_guard = self.storage.read().await; @@ -564,30 +635,21 @@ impl NodeCore { acc.balance }; - info!("New account public balance is {new_balance:?}"); + info!( + "Balance of {:?} now is {new_balance:?}, delta is {:?}", + hex::encode(acc_addr), + new_balance - old_balance + ); } - ///Deposit balance, make it private - pub async fn subscenario_2(&mut self) { - let acc_addr = self.create_new_account().await; - - let resp = self.send_public_deposit(acc_addr, 100).await.unwrap(); - info!("Response for public deposit is {resp:?}"); - - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; - - { - let acc_map_read_guard = self.storage.read().await; - - let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); - acc.log(); - - info!("New acconut public balance is {:?}", acc.balance); - }; - + async fn operate_account_send_shielded_one_receiver( + &mut self, + acc_addr_sender: AccountAddress, + acc_addr_rec: AccountAddress, + amount: u128, + ) { let (resp, new_utxo_hashes) = self - .send_shielded_send_tx(acc_addr, 100, vec![(100, acc_addr)]) + .send_shielded_send_tx(acc_addr_sender, amount as u64, vec![(amount, acc_addr_rec)]) .await .unwrap(); info!("Response for send shielded is {resp:?}"); @@ -597,61 +659,6 @@ impl NodeCore { info!("Awaiting new blocks"); tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; - let new_utxo = { - let mut write_guard = self.storage.write().await; - - let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); - acc.log(); - - acc.utxo_tree - .get_item(new_utxo_hash) - .unwrap() - .unwrap() - .clone() - }; - new_utxo.log(); - - info!("User received new utxo {new_utxo:?}"); - } - - ///Mint utxo, privately send it to another user - pub async fn subscenario_3(&mut self) { - let acc_addr = self.create_new_account().await; - - let (resp, new_utxo_hash, comm_gen_hash) = - self.send_private_mint_tx(acc_addr, 100).await.unwrap(); - info!("Response for mint private is {resp:?}"); - - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; - - let new_utxo = { - let mut write_guard = self.storage.write().await; - - let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); - acc.log(); - - acc.utxo_tree - .get_item(new_utxo_hash) - .unwrap() - .unwrap() - .clone() - }; - new_utxo.log(); - - let acc_addr_rec = self.create_new_account().await; - - let (resp, new_utxo_hashes) = self - .send_private_send_tx(new_utxo, comm_gen_hash, vec![(100, acc_addr_rec)]) - .await - .unwrap(); - info!("Response for send deshielded is {resp:?}"); - - let new_utxo_hash = new_utxo_hashes[0].1; - - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; - let new_utxo = { let mut write_guard = self.storage.write().await; @@ -665,8 +672,269 @@ impl NodeCore { .clone() }; new_utxo.log(); + info!( + "Account address is {:?} ,new utxo owner address is {:?}", + hex::encode(acc_addr_rec), + hex::encode(new_utxo.owner) + ); + info!( + "Account {:?} got new utxo with amount {amount:?}", + hex::encode(acc_addr_rec) + ); + } - info!("User {acc_addr_rec:?} received new utxo {new_utxo:?}"); + async fn operate_account_send_private_one_receiver( + &mut self, + acc_addr_rec: AccountAddress, + utxo: UTXO, + comm_gen_hash: [u8; 32], + ) { + let amount = utxo.amount; + + let (resp, new_utxo_hashes) = self + .send_private_send_tx(utxo, comm_gen_hash, vec![(amount, acc_addr_rec)]) + .await + .unwrap(); + info!("Response for send private is {resp:?}"); + + let new_utxo_hash = new_utxo_hashes[0].1; + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxo = { + let mut write_guard = self.storage.write().await; + + let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap(); + acc.log(); + + acc.utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone() + }; + new_utxo.log(); + info!( + "Account address is {:?} ,new utxo owner address is {:?}", + hex::encode(acc_addr_rec), + hex::encode(new_utxo.owner) + ); + info!( + "Account {:?} got new utxo with amount {:?}", + hex::encode(acc_addr_rec), + new_utxo.amount + ); + } + + pub async fn split_utxo( + &self, + utxo: UTXO, + commitment_in: [u8; 32], + receivers: Vec<(u128, AccountAddress)>, + visibility_list: [bool; 3], + ) -> (Transaction, Vec<(AccountAddress, [u8; 32])>) { + let acc_map_read_guard = self.storage.read().await; + + let accout = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); + + let nullifier = generate_nullifiers( + &utxo, + &accout + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key + .to_bytes() + .to_vec(), + ); + + let (resulting_utxos, receipt) = prove_send_utxo(utxo, receivers); + let utxo_hashes = resulting_utxos + .iter() + .map(|(utxo, addr)| (addr.clone(), utxo.hash)) + .collect(); + + let utxos: Vec = resulting_utxos + .iter() + .map(|(utxo, _)| utxo.clone()) + .collect(); + + let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + ephm_key_holder.log(); + + let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); + + let encoded_data: Vec<(Vec, Vec)> = utxos + .iter() + .map(|utxo_enc| { + let accout_enc = acc_map_read_guard.acc_map.get(&utxo_enc.owner).unwrap(); + + let (ciphertext, nonce) = Account::encrypt_data( + &ephm_key_holder, + accout_enc.key_holder.viewing_public_key, + &serde_json::to_vec(&utxo_enc).unwrap(), + ); + + (ciphertext, nonce.to_vec()) + }) + .collect(); + + let commitments = generate_commitments(&utxos); + + let publication = ActionData::UTXOPublication(UTXOPublication { + utxos: utxos + .iter() + .enumerate() + .filter_map(|(id, item)| { + if visibility_list[id] { + Some(item.clone()) + } else { + None + } + }) + .collect(), + }); + + ( + TransactionPayload { + tx_kind: TxKind::Shielded, + execution_input: vec![], + execution_output: serde_json::to_vec(&publication).unwrap(), + utxo_commitments_spent_hashes: vec![commitment_in], + utxo_commitments_created_hashes: commitments + .clone() + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![nullifier.try_into().unwrap()], + execution_proof_private: hex::encode(serde_json::to_vec(&receipt).unwrap()), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + } + .into(), + utxo_hashes, + ) + } + + pub async fn send_split_tx( + &self, + utxo: UTXO, + comm_hash: [u8; 32], + receivers: Vec<(u128, AccountAddress)>, + visibility_list: [bool; 3], + ) -> Result<(SendTxResponse, Vec<([u8; 32], [u8; 32])>, Vec<[u8; 32]>)> { + let point_before_prove = std::time::Instant::now(); + let (tx, utxo_hashes) = self + .split_utxo(utxo, comm_hash, receivers, visibility_list) + .await; + tx.log(); + let point_after_prove = std::time::Instant::now(); + + let timedelta = (point_after_prove - point_before_prove).as_millis(); + info!("Send private utxo proof spent {timedelta:?} milliseconds"); + + let commitments = tx.utxo_commitments_created_hashes.clone(); + + Ok(( + self.sequencer_client.send_tx(tx).await?, + utxo_hashes, + commitments, + )) + } + + async fn operate_account_send_split_utxo( + &mut self, + addrs_receivers: [AccountAddress; 3], + utxo: UTXO, + comm_gen_hash: [u8; 32], + visibility_list: [bool; 3], + ) -> (Vec, Vec<[u8; 32]>) { + let (resp, new_utxo_hashes, commitments_hashes) = self + .send_split_tx( + utxo.clone(), + comm_gen_hash, + addrs_receivers + .clone() + .map(|addr| (utxo.amount / 3, addr)) + .to_vec(), + visibility_list, + ) + .await + .unwrap(); + info!("Response for send shielded is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxos: Vec = { + let mut write_guard = self.storage.write().await; + + new_utxo_hashes + .into_iter() + .map(|(acc_addr_rec, new_utxo_hash)| { + let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap(); + + let new_utxo = acc + .utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone(); + new_utxo.log(); + + info!( + "Account address is {:?} ,new utxo owner address is {:?}", + hex::encode(acc_addr_rec), + hex::encode(new_utxo.owner) + ); + info!( + "Account {:?} got new utxo with amount {:?}", + hex::encode(acc_addr_rec), + new_utxo.amount + ); + + new_utxo + }) + .collect() + }; + + (new_utxos, commitments_hashes) + } + + ///Mint utxo, make it public + pub async fn subscenario_1(&mut self) { + let acc_addr = self.create_new_account().await; + + let (new_utxo, comm_gen_hash) = self.operate_account_mint_private(acc_addr, 100).await; + + self.operate_account_send_deshielded_one_receiver( + acc_addr, + acc_addr, + new_utxo, + comm_gen_hash, + ) + .await; + } + + ///Deposit balance, make it private + pub async fn subscenario_2(&mut self) { + let acc_addr = self.create_new_account().await; + + self.operate_account_deposit_public(acc_addr, 100).await; + + self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr, 100) + .await; + } + + ///Mint utxo, privately send it to another user + pub async fn subscenario_3(&mut self) { + let acc_addr = self.create_new_account().await; + let acc_addr_rec = self.create_new_account().await; + + let (new_utxo, comm_gen_hash) = self.operate_account_mint_private(acc_addr, 100).await; + + self.operate_account_send_private_one_receiver(acc_addr_rec, new_utxo, comm_gen_hash) + .await; } ///Deposit balance, shielded send it to another user @@ -674,46 +942,10 @@ impl NodeCore { let acc_addr = self.create_new_account().await; let acc_addr_rec = self.create_new_account().await; - let resp = self.send_public_deposit(acc_addr, 100).await.unwrap(); - info!("Response for public deposit is {resp:?}"); + self.operate_account_deposit_public(acc_addr, 100).await; - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; - - { - let acc_map_read_guard = self.storage.read().await; - let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); - acc.log(); - - info!("New acconut public balance is {:?}", acc.balance); - } - - let (resp, new_utxo_hashes) = self - .send_shielded_send_tx(acc_addr, 100, vec![(100, acc_addr_rec)]) - .await - .unwrap(); - info!("Response for send shielded is {resp:?}"); - - let new_utxo_hash = new_utxo_hashes[0].1; - - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; - - let new_utxo = { - let mut write_guard = self.storage.write().await; - - let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap(); - acc.log(); - - acc.utxo_tree - .get_item(new_utxo_hash) - .unwrap() - .unwrap() - .clone() - }; - new_utxo.log(); - - info!("User {acc_addr_rec:?} received new utxo {new_utxo:?}"); + self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr_rec, 100) + .await; } ///Mint utxo, deshielded send it to another user @@ -721,42 +953,50 @@ impl NodeCore { let acc_addr = self.create_new_account().await; let acc_addr_rec = self.create_new_account().await; - let (resp, new_utxo_hash, comm_gen_hash) = - self.send_private_mint_tx(acc_addr, 100).await.unwrap(); - info!("Response for mint private is {resp:?}"); + let (new_utxo, comm_gen_hash) = self.operate_account_mint_private(acc_addr, 100).await; - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + self.operate_account_send_deshielded_one_receiver( + acc_addr, + acc_addr_rec, + new_utxo, + comm_gen_hash, + ) + .await; + } - let new_utxo = { - let mut write_guard = self.storage.write().await; + ///First complex scenario. + /// Creating accounts A, B, C, D. + /// Minting UTXO for A, splitting it between B, C, D. + /// Variable `visibility_list` decides, which of actions will be visible on blockchain. + /// Variable `publication index` decides, who of B, C or D moves its UTXO into public state. + pub async fn scenario_1(&mut self, visibility_list: [bool; 3], publication_index: usize) { + let acc_addr_sender = self.create_new_account().await; - let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); - acc.log(); + let acc_addr_rec_1 = self.create_new_account().await; + let acc_addr_rec_2 = self.create_new_account().await; + let acc_addr_rec_3 = self.create_new_account().await; - acc.utxo_tree - .get_item(new_utxo_hash) - .unwrap() - .unwrap() - .clone() - }; - new_utxo.log(); + let addrs_receivers = [acc_addr_rec_1, acc_addr_rec_2, acc_addr_rec_3]; - let resp = self - .send_deshielded_send_tx(new_utxo, comm_gen_hash, vec![(100, acc_addr_rec)]) - .await - .unwrap(); - info!("Response for send deshielded is {resp:?}"); + let (new_utxo, comm_gen_hash) = self + .operate_account_mint_private(acc_addr_sender, 100) + .await; - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + let (new_utxos, comm_gen_hashes) = self + .operate_account_send_split_utxo( + addrs_receivers, + new_utxo, + comm_gen_hash, + visibility_list, + ) + .await; - { - let read_guard = self.storage.read().await; - let acc_rec = read_guard.acc_map.get(&acc_addr_rec).unwrap(); - acc_rec.log(); - - info!("New account public balance is {:?}", acc_rec.balance); - } + self.operate_account_send_deshielded_one_receiver( + addrs_receivers[publication_index], + addrs_receivers[publication_index], + new_utxos[publication_index].clone(), + comm_gen_hashes[publication_index], + ) + .await; } } diff --git a/node_core/src/storage/mod.rs b/node_core/src/storage/mod.rs index 869a72e..31dc538 100644 --- a/node_core/src/storage/mod.rs +++ b/node_core/src/storage/mod.rs @@ -1,20 +1,15 @@ use std::{collections::HashMap, path::Path}; use accounts::account_core::{Account, AccountAddress}; -use accounts_store::NodeAccountsStore; use anyhow::Result; use block_store::NodeBlockStore; use elliptic_curve::group::GroupEncoding; use k256::AffinePoint; use storage::{ block::Block, - merkle_tree_public::{ - merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree}, - TreeHashType, - }, + merkle_tree_public::merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree}, nullifier::UTXONullifier, nullifier_sparse_merkle_tree::NullifierSparseMerkleTree, - transaction::Transaction, utxo_commitment::UTXOCommitment, }; use utxo::utxo_core::UTXO; @@ -85,6 +80,7 @@ impl NodeChainStore { acc_mut.balance.saturating_sub(action.amount as u64); } } + _ => {} } } } diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index 1ee0b0c..accb7d4 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -14,9 +14,7 @@ use crate::{ types::{ err_rpc::cast_seq_client_error_into_rpc_error, rpc_structs::{ - ExecuteSubscenarioRequest, ExecuteSubscenarioResponse, GetBlockDataRequest, - GetBlockDataResponse, GetLastBlockRequest, GetLastBlockResponse, - RegisterAccountRequest, RegisterAccountResponse, SendTxRequest, + ExecuteScenarioSplitRequest, ExecuteScenarioSplitResponse, ExecuteSubscenarioRequest, ExecuteSubscenarioResponse, GetBlockDataRequest, GetBlockDataResponse, GetLastBlockRequest, GetLastBlockResponse, RegisterAccountRequest, RegisterAccountResponse, SendTxRequest }, }, }; @@ -63,6 +61,22 @@ impl JsonHandler { respond(helperstruct) } + async fn process_request_execute_scenario_split(&self, request: Request) -> Result { + let req = ExecuteScenarioSplitRequest::parse(Some(request.params))?; + + { + let mut store = self.node_chain_store.lock().await; + + store.scenario_1(req.visibility_list, req.publication_index).await; + } + + let helperstruct = ExecuteScenarioSplitResponse { + scenario_result: "success".to_string(), + }; + + respond(helperstruct) + } + async fn process_register_account(&self, request: Request) -> Result { let _req = RegisterAccountRequest::parse(Some(request.params))?; @@ -139,6 +153,7 @@ impl JsonHandler { "send_tx" => self.process_send_tx(request).await, "get_block" => self.process_get_block_data(request).await, "get_last_block" => self.process_get_last_block(request).await, + "execute_scenario_split" => self.process_request_execute_scenario_split(request).await, _ => Err(RpcErr(RpcError::method_not_found(request.method))), } } diff --git a/node_rpc/src/types/rpc_structs.rs b/node_rpc/src/types/rpc_structs.rs index fbb3afe..3b7d259 100644 --- a/node_rpc/src/types/rpc_structs.rs +++ b/node_rpc/src/types/rpc_structs.rs @@ -25,6 +25,12 @@ pub struct ExecuteSubscenarioRequest { pub scenario_id: u64, } +#[derive(Serialize, Deserialize, Debug)] +pub struct ExecuteScenarioSplitRequest { + pub visibility_list: [bool; 3], + pub publication_index: usize, +} + #[derive(Serialize, Deserialize, Debug)] pub struct GetGenesisIdRequest {} @@ -36,6 +42,7 @@ parse_request!(SendTxRequest); parse_request!(GetBlockDataRequest); parse_request!(GetGenesisIdRequest); parse_request!(ExecuteSubscenarioRequest); +parse_request!(ExecuteScenarioSplitRequest); parse_request!(GetLastBlockRequest); #[derive(Serialize, Deserialize, Debug)] @@ -63,6 +70,11 @@ pub struct ExecuteSubscenarioResponse { pub scenario_result: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct ExecuteScenarioSplitResponse { + pub scenario_result: String, +} + #[derive(Serialize, Deserialize, Debug)] pub struct GetGenesisIdResponse { pub genesis_id: u64, diff --git a/storage/src/transaction.rs b/storage/src/transaction.rs index ed8da27..bc99b07 100644 --- a/storage/src/transaction.rs +++ b/storage/src/transaction.rs @@ -97,11 +97,23 @@ impl Transaction { info!("Transaction tx_kind is {:?}", self.tx_kind); info!( "Transaction execution_input is {:?}", - hex::encode(self.execution_input.clone()) + { + if let Ok(vall) = serde_json::from_slice::(&self.execution_input) { + vall + } else { + serde_json::Value::Null + } + } ); info!( "Transaction execution_output is {:?}", - hex::encode(self.execution_output.clone()) + { + if let Ok(vall) = serde_json::from_slice::(&self.execution_output) { + vall + } else { + serde_json::Value::Null + } + } ); info!( "Transaction utxo_commitments_spent_hashes is {:?}", From 90d1a9bb9be9a54dcff54187fc4531524a333ffa Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 3 Jan 2025 12:43:05 +0200 Subject: [PATCH 3/3] fix: second complex scenario added --- .../key_management/ephemeral_key_holder.rs | 5 +- node_core/src/lib.rs | 355 +++++++++++++++++- node_rpc/src/process.rs | 40 +- node_rpc/src/types/rpc_structs.rs | 12 + storage/src/transaction.rs | 123 +++++- zkvm/src/lib.rs | 72 ++++ .../src/bin/mint_utxo_multiple_assets.rs | 36 ++ .../src/bin/send_utxo_multiple_assets.rs | 40 ++ 8 files changed, 659 insertions(+), 24 deletions(-) create mode 100644 zkvm/test_methods/guest/src/bin/mint_utxo_multiple_assets.rs create mode 100644 zkvm/test_methods/guest/src/bin/send_utxo_multiple_assets.rs diff --git a/accounts/src/key_management/ephemeral_key_holder.rs b/accounts/src/key_management/ephemeral_key_holder.rs index c464e5c..0d8d64c 100644 --- a/accounts/src/key_management/ephemeral_key_holder.rs +++ b/accounts/src/key_management/ephemeral_key_holder.rs @@ -54,6 +54,9 @@ impl EphemeralKeyHolder { } pub fn log(&self) { - info!("Ephemeral private key is {:?}", hex::encode(self.ephemeral_secret_key.to_bytes())); + info!( + "Ephemeral private key is {:?}", + hex::encode(self.ephemeral_secret_key.to_bytes()) + ); } } diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 1289b6f..46693bc 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -5,7 +5,10 @@ use std::sync::{ use k256::elliptic_curve::group::GroupEncoding; -use ::storage::{nullifier::UTXONullifier, transaction::{Transaction, TransactionPayload, TxKind}}; +use ::storage::{ + nullifier::UTXONullifier, + transaction::{Transaction, TransactionPayload, TxKind}, +}; use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; use config::NodeConfig; @@ -22,7 +25,8 @@ use storage::NodeChainStore; use tokio::{sync::RwLock, task::JoinHandle}; use utxo::utxo_core::{Asset, UTXO}; use zkvm::{ - prove_mint_utxo, prove_send_utxo, prove_send_utxo_deshielded, prove_send_utxo_shielded, + prove_mint_utxo, prove_mint_utxo_multiple_assets, prove_send_utxo, prove_send_utxo_deshielded, + prove_send_utxo_multiple_assets_one_receiver, prove_send_utxo_shielded, }; pub const BLOCK_GEN_DELAY_SECS: u64 = 20; @@ -202,6 +206,58 @@ impl NodeCore { ) } + pub async fn mint_utxo_multiple_assets_private( + &self, + acc: AccountAddress, + amount: u128, + number_of_assets: usize, + ) -> (Transaction, Vec<[u8; 32]>) { + let (utxos, receipt) = prove_mint_utxo_multiple_assets(amount, number_of_assets, acc); + let result_hashes = utxos.iter().map(|utxo| utxo.hash).collect(); + + let acc_map_read_guard = self.storage.read().await; + + let accout = acc_map_read_guard.acc_map.get(&acc).unwrap(); + + let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + ephm_key_holder.log(); + + let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); + + let encoded_data = utxos + .iter() + .map(|utxo| { + Account::encrypt_data( + &ephm_key_holder, + accout.key_holder.viewing_public_key, + &serde_json::to_vec(&utxo).unwrap(), + ) + }) + .map(|(ciphertext, nonce)| (ciphertext, nonce.to_vec())) + .collect(); + + let comm = generate_commitments(&utxos); + + ( + TransactionPayload { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: vec![], + utxo_commitments_created_hashes: comm + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![], + execution_proof_private: hex::encode(serde_json::to_vec(&receipt).unwrap()), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + } + .into(), + result_hashes, + ) + } + pub fn deposit_money_public(&self, acc: AccountAddress, amount: u128) -> Transaction { TransactionPayload { tx_kind: TxKind::Public, @@ -293,6 +349,106 @@ impl NodeCore { ) } + pub async fn transfer_utxo_multiple_assets_private( + &self, + utxos: Vec, + commitments_in: Vec<[u8; 32]>, + number_to_send: usize, + receiver: AccountAddress, + ) -> (Transaction, Vec<[u8; 32]>, Vec<[u8; 32]>) { + let acc_map_read_guard = self.storage.read().await; + + let accout = acc_map_read_guard.acc_map.get(&utxos[0].owner).unwrap(); + + let nsk = accout + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key + .to_bytes() + .to_vec(); + + let nullifiers = utxos + .iter() + .map(|utxo| generate_nullifiers(utxo, &nsk)) + .map(|vecc| vecc.try_into().unwrap()) + .collect(); + + let (resulting_utxos_receiver, resulting_utxos_not_spent, receipt) = + prove_send_utxo_multiple_assets_one_receiver(utxos, number_to_send, receiver); + + let utxo_hashes_receiver = resulting_utxos_receiver + .iter() + .map(|utxo| utxo.hash) + .collect(); + + let utxo_hashes_not_spent = resulting_utxos_not_spent + .iter() + .map(|utxo| utxo.hash) + .collect(); + + let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + ephm_key_holder.log(); + + let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); + + let mut encoded_data: Vec<(Vec, Vec)> = resulting_utxos_receiver + .iter() + .map(|utxo_enc| { + let accout_enc = acc_map_read_guard.acc_map.get(&utxo_enc.owner).unwrap(); + + let (ciphertext, nonce) = Account::encrypt_data( + &ephm_key_holder, + accout_enc.key_holder.viewing_public_key, + &serde_json::to_vec(&utxo_enc).unwrap(), + ); + + (ciphertext, nonce.to_vec()) + }) + .collect(); + + let encoded_data_1: Vec<(Vec, Vec)> = resulting_utxos_not_spent + .iter() + .map(|utxo_enc| { + let accout_enc = acc_map_read_guard.acc_map.get(&utxo_enc.owner).unwrap(); + + let (ciphertext, nonce) = Account::encrypt_data( + &ephm_key_holder, + accout_enc.key_holder.viewing_public_key, + &serde_json::to_vec(&utxo_enc).unwrap(), + ); + + (ciphertext, nonce.to_vec()) + }) + .collect(); + + encoded_data.extend(encoded_data_1); + + let mut commitments = generate_commitments(&resulting_utxos_receiver); + let commitments_1 = generate_commitments(&resulting_utxos_not_spent); + + commitments.extend(commitments_1); + + ( + TransactionPayload { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: commitments_in, + utxo_commitments_created_hashes: commitments + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: nullifiers, + execution_proof_private: hex::encode(serde_json::to_vec(&receipt).unwrap()), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + } + .into(), + utxo_hashes_receiver, + utxo_hashes_not_spent, + ) + } + pub async fn transfer_balance_shielded( &self, acc: AccountAddress, @@ -458,6 +614,31 @@ impl NodeCore { )) } + pub async fn send_private_mint_multiple_assets_tx( + &self, + acc: AccountAddress, + amount: u128, + number_of_assets: usize, + ) -> Result<(SendTxResponse, Vec<[u8; 32]>, Vec<[u8; 32]>)> { + let point_before_prove = std::time::Instant::now(); + let (tx, utxo_hashes) = self + .mint_utxo_multiple_assets_private(acc, amount, number_of_assets) + .await; + tx.log(); + let point_after_prove = std::time::Instant::now(); + + let commitment_generated_hashes = tx.utxo_commitments_created_hashes.clone(); + + let timedelta = (point_after_prove - point_before_prove).as_millis(); + info!("Mint utxo proof spent {timedelta:?} milliseconds"); + + Ok(( + self.sequencer_client.send_tx(tx).await?, + utxo_hashes, + commitment_generated_hashes, + )) + } + pub async fn send_public_deposit( &self, acc: AccountAddress, @@ -486,6 +667,30 @@ impl NodeCore { Ok((self.sequencer_client.send_tx(tx).await?, utxo_hashes)) } + pub async fn send_private_multiple_assets_send_tx( + &self, + utxos: Vec, + comm_hashes: Vec<[u8; 32]>, + number_to_send: usize, + receiver: AccountAddress, + ) -> Result<(SendTxResponse, Vec<[u8; 32]>, Vec<[u8; 32]>)> { + let point_before_prove = std::time::Instant::now(); + let (tx, utxo_hashes_received, utxo_hashes_not_spent) = self + .transfer_utxo_multiple_assets_private(utxos, comm_hashes, number_to_send, receiver) + .await; + tx.log(); + let point_after_prove = std::time::Instant::now(); + + let timedelta = (point_after_prove - point_before_prove).as_millis(); + info!("Send private utxo proof spent {timedelta:?} milliseconds"); + + Ok(( + self.sequencer_client.send_tx(tx).await?, + utxo_hashes_received, + utxo_hashes_not_spent, + )) + } + pub async fn send_shielded_send_tx( &self, acc: AccountAddress, @@ -560,6 +765,56 @@ impl NodeCore { (new_utxo, comm_gen_hash) } + async fn operate_account_mint_multiple_assets_private( + &mut self, + acc_addr: AccountAddress, + amount: u128, + number_of_assets: usize, + ) -> (Vec, Vec<[u8; 32]>) { + let (resp, new_utxo_hashes, comm_gen_hashes) = self + .send_private_mint_multiple_assets_tx(acc_addr, amount, number_of_assets) + .await + .unwrap(); + info!("Response for mint multiple assets private is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxos = { + let mut write_guard = self.storage.write().await; + + new_utxo_hashes + .into_iter() + .map(|new_utxo_hash| { + let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); + + let new_utxo = acc + .utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone(); + + new_utxo.log(); + info!( + "Account address is {:?} ,new utxo owner address is {:?}", + hex::encode(acc_addr), + hex::encode(new_utxo.owner) + ); + info!( + "Account {:?} got new utxo with amount {amount:?} and asset {:?}", + hex::encode(acc_addr), + new_utxo.asset + ); + + new_utxo + }) + .collect() + }; + + (new_utxos, comm_gen_hashes) + } + async fn operate_account_send_deshielded_one_receiver( &mut self, acc_addr_sender: AccountAddress, @@ -727,6 +982,83 @@ impl NodeCore { ); } + async fn operate_account_send_private_multiple_assets_one_receiver( + &mut self, + acc_addr: AccountAddress, + acc_addr_rec: AccountAddress, + utxos: Vec, + comm_gen_hashes: Vec<[u8; 32]>, + number_to_send: usize, + ) { + let (resp, new_utxo_hashes_rec, new_utxo_hashes_not_sp) = self + .send_private_multiple_assets_send_tx( + utxos, + comm_gen_hashes, + number_to_send, + acc_addr_rec, + ) + .await + .unwrap(); + info!("Response for send private multiple assets is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + { + let mut write_guard = self.storage.write().await; + + for new_utxo_hash in new_utxo_hashes_rec { + let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap(); + acc.log(); + + let new_utxo = acc + .utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone(); + + new_utxo.log(); + info!( + "Account address is {:?} ,new utxo owner address is {:?}", + hex::encode(acc_addr_rec), + hex::encode(new_utxo.owner) + ); + info!( + "Account {:?} got new utxo with amount {:?} and asset {:?}", + hex::encode(acc_addr_rec), + new_utxo.amount, + new_utxo.asset, + ); + } + + for new_utxo_hash in new_utxo_hashes_not_sp { + let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); + acc.log(); + + let new_utxo = acc + .utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone(); + + new_utxo.log(); + info!( + "Account address is {:?} ,new utxo owner address is {:?}", + hex::encode(acc_addr), + hex::encode(new_utxo.owner) + ); + info!( + "Account {:?} got new utxo with amount {:?} and asset {:?}", + hex::encode(acc_addr), + new_utxo.amount, + new_utxo.asset, + ); + } + } + } + pub async fn split_utxo( &self, utxo: UTXO, @@ -999,4 +1331,23 @@ impl NodeCore { ) .await; } + + ///Mint number of different assets with same amount for account + pub async fn scenario_2(&mut self, number_of_assets: usize, number_to_send: usize) { + let acc_addr_sender = self.create_new_account().await; + let acc_addr_receiver = self.create_new_account().await; + + let (utxos, comm_gen_hashes) = self + .operate_account_mint_multiple_assets_private(acc_addr_sender, 100, number_of_assets) + .await; + + self.operate_account_send_private_multiple_assets_one_receiver( + acc_addr_sender, + acc_addr_receiver, + utxos, + comm_gen_hashes, + number_to_send, + ) + .await; + } } diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index accb7d4..963aea5 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -14,7 +14,11 @@ use crate::{ types::{ err_rpc::cast_seq_client_error_into_rpc_error, rpc_structs::{ - ExecuteScenarioSplitRequest, ExecuteScenarioSplitResponse, ExecuteSubscenarioRequest, ExecuteSubscenarioResponse, GetBlockDataRequest, GetBlockDataResponse, GetLastBlockRequest, GetLastBlockResponse, RegisterAccountRequest, RegisterAccountResponse, SendTxRequest + ExecuteScenarioMultipleSendRequest, ExecuteScenarioMultipleSendResponse, + ExecuteScenarioSplitRequest, ExecuteScenarioSplitResponse, ExecuteSubscenarioRequest, + ExecuteSubscenarioResponse, GetBlockDataRequest, GetBlockDataResponse, + GetLastBlockRequest, GetLastBlockResponse, RegisterAccountRequest, + RegisterAccountResponse, SendTxRequest, }, }, }; @@ -61,13 +65,18 @@ impl JsonHandler { respond(helperstruct) } - async fn process_request_execute_scenario_split(&self, request: Request) -> Result { + async fn process_request_execute_scenario_split( + &self, + request: Request, + ) -> Result { let req = ExecuteScenarioSplitRequest::parse(Some(request.params))?; { let mut store = self.node_chain_store.lock().await; - store.scenario_1(req.visibility_list, req.publication_index).await; + store + .scenario_1(req.visibility_list, req.publication_index) + .await; } let helperstruct = ExecuteScenarioSplitResponse { @@ -77,6 +86,27 @@ impl JsonHandler { respond(helperstruct) } + async fn process_request_execute_scenario_multiple_send( + &self, + request: Request, + ) -> Result { + let req = ExecuteScenarioMultipleSendRequest::parse(Some(request.params))?; + + { + let mut store = self.node_chain_store.lock().await; + + store + .scenario_2(req.number_of_assets, req.number_to_send) + .await; + } + + let helperstruct = ExecuteScenarioMultipleSendResponse { + scenario_result: "success".to_string(), + }; + + respond(helperstruct) + } + async fn process_register_account(&self, request: Request) -> Result { let _req = RegisterAccountRequest::parse(Some(request.params))?; @@ -154,6 +184,10 @@ impl JsonHandler { "get_block" => self.process_get_block_data(request).await, "get_last_block" => self.process_get_last_block(request).await, "execute_scenario_split" => self.process_request_execute_scenario_split(request).await, + "execute_scenario_multiple_send" => { + self.process_request_execute_scenario_multiple_send(request) + .await + } _ => Err(RpcErr(RpcError::method_not_found(request.method))), } } diff --git a/node_rpc/src/types/rpc_structs.rs b/node_rpc/src/types/rpc_structs.rs index 3b7d259..a542e4c 100644 --- a/node_rpc/src/types/rpc_structs.rs +++ b/node_rpc/src/types/rpc_structs.rs @@ -31,6 +31,12 @@ pub struct ExecuteScenarioSplitRequest { pub publication_index: usize, } +#[derive(Serialize, Deserialize, Debug)] +pub struct ExecuteScenarioMultipleSendRequest { + pub number_of_assets: usize, + pub number_to_send: usize, +} + #[derive(Serialize, Deserialize, Debug)] pub struct GetGenesisIdRequest {} @@ -43,6 +49,7 @@ parse_request!(GetBlockDataRequest); parse_request!(GetGenesisIdRequest); parse_request!(ExecuteSubscenarioRequest); parse_request!(ExecuteScenarioSplitRequest); +parse_request!(ExecuteScenarioMultipleSendRequest); parse_request!(GetLastBlockRequest); #[derive(Serialize, Deserialize, Debug)] @@ -75,6 +82,11 @@ pub struct ExecuteScenarioSplitResponse { pub scenario_result: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct ExecuteScenarioMultipleSendResponse { + pub scenario_result: String, +} + #[derive(Serialize, Deserialize, Debug)] pub struct GetGenesisIdResponse { pub genesis_id: u64, diff --git a/storage/src/transaction.rs b/storage/src/transaction.rs index bc99b07..15672ae 100644 --- a/storage/src/transaction.rs +++ b/storage/src/transaction.rs @@ -91,30 +91,117 @@ impl From for Transaction { } } +#[derive(Debug, Serialize, Deserialize)] +pub struct MintMoneyPublicTx { + pub acc: [u8; 32], + pub amount: u128, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SendMoneyShieldedTx { + pub acc_sender: [u8; 32], + pub amount: u128, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SendMoneyDeshieldedTx { + pub receiver_data: Vec<(u128, [u8; 32])>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct OwnedUTXO { + pub hash: [u8; 32], + pub owner: [u8; 32], + pub amount: u128, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct OwnedUTXOForPublication { + pub hash: String, + pub owner: String, + pub amount: u128, +} + +impl From for OwnedUTXOForPublication { + fn from(value: OwnedUTXO) -> Self { + Self { + hash: hex::encode(value.hash), + owner: hex::encode(value.owner), + amount: value.amount, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UTXOPublication { + pub utxos: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum ActionData { + MintMoneyPublicTx(MintMoneyPublicTx), + SendMoneyShieldedTx(SendMoneyShieldedTx), + SendMoneyDeshieldedTx(SendMoneyDeshieldedTx), + UTXOPublication(UTXOPublication), +} + +impl ActionData { + pub fn into_hexed_print(self) -> String { + match self { + ActionData::MintMoneyPublicTx(action) => { + format!( + "Account {:?} minted {:?} balance", + hex::encode(action.acc), + action.amount + ) + } + ActionData::SendMoneyDeshieldedTx(action) => { + format!( + "Receivers receipt {:?}", + action + .receiver_data + .into_iter() + .map(|(amount, rec)| (amount, hex::encode(rec))) + .collect::>() + ) + } + ActionData::SendMoneyShieldedTx(action) => { + format!( + "Shielded send from {:?} for {:?} balance", + hex::encode(action.acc_sender), + action.amount + ) + } + ActionData::UTXOPublication(action) => { + let pub_own_utxo: Vec = action + .utxos + .into_iter() + .map(|owned_utxo| owned_utxo.into()) + .collect(); + format!("Published utxos {:?}", pub_own_utxo) + } + } + } +} + impl Transaction { pub fn log(&self) { info!("Transaction hash is {:?}", hex::encode(self.hash)); info!("Transaction tx_kind is {:?}", self.tx_kind); - info!( - "Transaction execution_input is {:?}", - { - if let Ok(vall) = serde_json::from_slice::(&self.execution_input) { - vall - } else { - serde_json::Value::Null - } + info!("Transaction execution_input is {:?}", { + if let Ok(action) = serde_json::from_slice::(&self.execution_input) { + action.into_hexed_print() + } else { + "".to_string() } - ); - info!( - "Transaction execution_output is {:?}", - { - if let Ok(vall) = serde_json::from_slice::(&self.execution_output) { - vall - } else { - serde_json::Value::Null - } + }); + info!("Transaction execution_output is {:?}", { + if let Ok(action) = serde_json::from_slice::(&self.execution_output) { + action.into_hexed_print() + } else { + "".to_string() } - ); + }); info!( "Transaction utxo_commitments_spent_hashes is {:?}", self.utxo_commitments_spent_hashes diff --git a/zkvm/src/lib.rs b/zkvm/src/lib.rs index 2eaf694..7c26f73 100644 --- a/zkvm/src/lib.rs +++ b/zkvm/src/lib.rs @@ -52,6 +52,47 @@ pub fn prove_send_utxo( ) } +pub fn prove_send_utxo_multiple_assets_one_receiver( + spent_utxos: Vec, + number_to_send: usize, + receiver: AccountAddress, +) -> (Vec, Vec, Receipt) { + let mut builder = ExecutorEnv::builder(); + let utxo_payload: Vec = spent_utxos + .into_iter() + .map(|spent_utxo| spent_utxo.into_payload()) + .collect(); + + builder.write(&utxo_payload).unwrap(); + builder.write(&number_to_send).unwrap(); + builder.write(&receiver).unwrap(); + + let env = builder.build().unwrap(); + + let prover = default_prover(); + + let receipt = prover + .prove(env, test_methods::SEND_UTXO_MULTIPLE_ASSETS_ELF) + .unwrap() + .receipt; + + let digest: (Vec, Vec) = receipt.journal.decode().unwrap(); + + ( + digest + .0 + .into_iter() + .map(|payload| UTXO::create_utxo_from_payload(payload)) + .collect(), + digest + .1 + .into_iter() + .map(|payload| UTXO::create_utxo_from_payload(payload)) + .collect(), + receipt, + ) +} + pub fn prove_send_utxo_shielded( owner: AccountAddress, amount: u128, @@ -120,6 +161,37 @@ pub fn prove_send_utxo_deshielded( ) } +pub fn prove_mint_utxo_multiple_assets( + amount_to_mint: u128, + number_of_assets: usize, + owner: AccountAddress, +) -> (Vec, Receipt) { + let mut builder = ExecutorEnv::builder(); + + builder.write(&amount_to_mint).unwrap(); + builder.write(&number_of_assets).unwrap(); + builder.write(&owner).unwrap(); + + let env = builder.build().unwrap(); + + let prover = default_prover(); + + let receipt = prover + .prove(env, test_methods::MINT_UTXO_MULTIPLE_ASSETS_ELF) + .unwrap() + .receipt; + + let digest: Vec = receipt.journal.decode().unwrap(); + + ( + digest + .into_iter() + .map(UTXO::create_utxo_from_payload) + .collect(), + receipt, + ) +} + pub fn execute_mint_utxo(amount_to_mint: u128, owner: AccountAddress) -> UTXO { let mut builder = ExecutorEnv::builder(); diff --git a/zkvm/test_methods/guest/src/bin/mint_utxo_multiple_assets.rs b/zkvm/test_methods/guest/src/bin/mint_utxo_multiple_assets.rs new file mode 100644 index 0000000..8ba50c4 --- /dev/null +++ b/zkvm/test_methods/guest/src/bin/mint_utxo_multiple_assets.rs @@ -0,0 +1,36 @@ +use risc0_zkvm::{ + guest::env, +}; +use serde::{Deserialize, Serialize}; + +type AccountAddr = [u8; 32]; + +#[derive(Serialize, Deserialize)] +pub struct UTXOPayload { + pub owner: AccountAddr, + pub asset: Vec, + // TODO: change to u256 + pub amount: u128, + pub privacy_flag: bool, +} + +fn main() { + let amount_to_mint: u128 = env::read(); + let number_of_assets: usize = env::read(); + let owner: AccountAddr = env::read(); + + let mut asseted_utxos = vec![]; + + for i in 0..number_of_assets { + let payload = UTXOPayload { + owner, + asset: vec![i as u8], + amount: amount_to_mint, + privacy_flag: true, + }; + + asseted_utxos.push(payload); + } + + env::commit(&(asseted_utxos)); +} diff --git a/zkvm/test_methods/guest/src/bin/send_utxo_multiple_assets.rs b/zkvm/test_methods/guest/src/bin/send_utxo_multiple_assets.rs new file mode 100644 index 0000000..2568da3 --- /dev/null +++ b/zkvm/test_methods/guest/src/bin/send_utxo_multiple_assets.rs @@ -0,0 +1,40 @@ +use risc0_zkvm::{ + guest::env, +}; +use serde::{Deserialize, Serialize}; + +type AccountAddr = [u8; 32]; + +#[derive(Clone, Serialize, Deserialize)] +pub struct UTXOPayload { + pub owner: AccountAddr, + pub asset: Vec, + // TODO: change to u256 + pub amount: u128, + pub privacy_flag: bool, +} + +fn main() { + let utxo_spent: Vec = env::read(); + let number_to_send = env::read(); + let receiver: AccountAddr = env::read(); + + let mut utxo_received = vec![]; + let mut utxo_not_spent = vec![]; + + for i in 0..utxo_spent.len() { + let mut utxo_payload = utxo_spent[i].clone(); + + if i < number_to_send { + utxo_payload.owner = receiver; + + utxo_received.push(utxo_payload); + } else { + utxo_payload.asset.push(0); + + utxo_not_spent.push(utxo_payload); + } + } + + env::commit(&(utxo_received, utxo_not_spent)); +}