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)); +}