From d4d17bb9814a4cfddb414b94ae7e94900407cd43 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravdyvyi Date: Fri, 10 Jan 2025 03:00:32 +0200 Subject: [PATCH] feat: write methods --- Cargo.lock | 1 + node_core/src/lib.rs | 223 ++++++++------ node_rpc/src/process.rs | 463 ++++++++++++++++++++++++++++-- node_rpc/src/types/rpc_structs.rs | 107 +++++++ zkvm/Cargo.toml | 1 + zkvm/src/lib.rs | 191 ++++++++---- 6 files changed, 815 insertions(+), 171 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07a5e93..90a7994 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5414,6 +5414,7 @@ dependencies = [ "serde_json", "storage", "test-methods", + "thiserror", "utxo", ] diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 46693bc..48bbd07 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -26,7 +26,7 @@ use tokio::{sync::RwLock, task::JoinHandle}; use utxo::utxo_core::{Asset, UTXO}; use zkvm::{ 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, + prove_send_utxo_multiple_assets_one_receiver, prove_send_utxo_shielded, ExecutionFailureKind, }; pub const BLOCK_GEN_DELAY_SECS: u64 = 20; @@ -165,8 +165,8 @@ impl NodeCore { &self, acc: AccountAddress, amount: u128, - ) -> (Transaction, [u8; 32]) { - let (utxo, receipt) = prove_mint_utxo(amount, acc); + ) -> Result<(Transaction, [u8; 32]), ExecutionFailureKind> { + let (utxo, receipt) = prove_mint_utxo(amount, acc)?; let result_hash = utxo.hash; let acc_map_read_guard = self.storage.read().await; @@ -186,7 +186,7 @@ impl NodeCore { let comm = generate_commitments(&vec![utxo]); - ( + Ok(( TransactionPayload { tx_kind: TxKind::Private, execution_input: vec![], @@ -203,7 +203,7 @@ impl NodeCore { } .into(), result_hash, - ) + )) } pub async fn mint_utxo_multiple_assets_private( @@ -211,8 +211,8 @@ impl NodeCore { 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); + ) -> Result<(Transaction, Vec<[u8; 32]>), ExecutionFailureKind> { + 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; @@ -238,7 +238,7 @@ impl NodeCore { let comm = generate_commitments(&utxos); - ( + Ok(( TransactionPayload { tx_kind: TxKind::Private, execution_input: vec![], @@ -255,7 +255,7 @@ impl NodeCore { } .into(), result_hashes, - ) + )) } pub fn deposit_money_public(&self, acc: AccountAddress, amount: u128) -> Transaction { @@ -281,7 +281,7 @@ impl NodeCore { utxo: UTXO, commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> (Transaction, Vec<(AccountAddress, [u8; 32])>) { + ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let accout = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -296,7 +296,7 @@ impl NodeCore { .to_vec(), ); - let (resulting_utxos, receipt) = prove_send_utxo(utxo, receivers); + let (resulting_utxos, receipt) = prove_send_utxo(utxo, receivers)?; let utxo_hashes = resulting_utxos .iter() .map(|(utxo, addr)| (addr.clone(), utxo.hash)) @@ -329,7 +329,7 @@ impl NodeCore { let commitments = generate_commitments(&utxos); - ( + Ok(( TransactionPayload { tx_kind: TxKind::Private, execution_input: vec![], @@ -346,7 +346,7 @@ impl NodeCore { } .into(), utxo_hashes, - ) + )) } pub async fn transfer_utxo_multiple_assets_private( @@ -355,7 +355,7 @@ impl NodeCore { commitments_in: Vec<[u8; 32]>, number_to_send: usize, receiver: AccountAddress, - ) -> (Transaction, Vec<[u8; 32]>, Vec<[u8; 32]>) { + ) -> Result<(Transaction, Vec<[u8; 32]>, Vec<[u8; 32]>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let accout = acc_map_read_guard.acc_map.get(&utxos[0].owner).unwrap(); @@ -374,7 +374,7 @@ impl NodeCore { .collect(); let (resulting_utxos_receiver, resulting_utxos_not_spent, receipt) = - prove_send_utxo_multiple_assets_one_receiver(utxos, number_to_send, receiver); + prove_send_utxo_multiple_assets_one_receiver(utxos, number_to_send, receiver)?; let utxo_hashes_receiver = resulting_utxos_receiver .iter() @@ -428,7 +428,7 @@ impl NodeCore { commitments.extend(commitments_1); - ( + Ok(( TransactionPayload { tx_kind: TxKind::Private, execution_input: vec![], @@ -446,7 +446,7 @@ impl NodeCore { .into(), utxo_hashes_receiver, utxo_hashes_not_spent, - ) + )) } pub async fn transfer_balance_shielded( @@ -454,7 +454,7 @@ impl NodeCore { acc: AccountAddress, balance: u64, receivers: Vec<(u128, AccountAddress)>, - ) -> (Transaction, Vec<(AccountAddress, [u8; 32])>) { + ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let accout = acc_map_read_guard.acc_map.get(&acc).unwrap(); @@ -486,7 +486,7 @@ impl NodeCore { .to_vec(), ); - let (resulting_utxos, receipt) = prove_send_utxo_shielded(acc, balance as u128, receivers); + let (resulting_utxos, receipt) = prove_send_utxo_shielded(acc, balance as u128, receivers)?; let utxo_hashes = resulting_utxos .iter() .map(|(utxo, addr)| (addr.clone(), utxo.hash)) @@ -519,7 +519,7 @@ impl NodeCore { let commitments = generate_commitments(&utxos); - ( + Ok(( TransactionPayload { tx_kind: TxKind::Shielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyShieldedTx( @@ -542,7 +542,7 @@ impl NodeCore { } .into(), utxo_hashes, - ) + )) } pub async fn transfer_utxo_deshielded( @@ -550,7 +550,7 @@ impl NodeCore { utxo: UTXO, comm_gen_hash: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> Transaction { + ) -> Result { let acc_map_read_guard = self.storage.read().await; let commitment_in = acc_map_read_guard @@ -571,9 +571,9 @@ impl NodeCore { .to_vec(), ); - let (resulting_balances, receipt) = prove_send_utxo_deshielded(utxo, receivers); + let (resulting_balances, receipt) = prove_send_utxo_deshielded(utxo, receivers)?; - TransactionPayload { + Ok(TransactionPayload { tx_kind: TxKind::Deshielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyDeshieldedTx( SendMoneyDeshieldedTx { @@ -589,7 +589,7 @@ impl NodeCore { encoded_data: vec![], ephemeral_pub_key: vec![], } - .into() + .into()) } pub async fn send_private_mint_tx( @@ -598,7 +598,7 @@ impl NodeCore { amount: u128, ) -> Result<(SendTxResponse, [u8; 32], [u8; 32])> { let point_before_prove = std::time::Instant::now(); - let (tx, utxo_hash) = self.mint_utxo_private(acc, amount).await; + let (tx, utxo_hash) = self.mint_utxo_private(acc, amount).await?; tx.log(); let point_after_prove = std::time::Instant::now(); @@ -623,7 +623,7 @@ impl NodeCore { let point_before_prove = std::time::Instant::now(); let (tx, utxo_hashes) = self .mint_utxo_multiple_assets_private(acc, amount, number_of_assets) - .await; + .await?; tx.log(); let point_after_prove = std::time::Instant::now(); @@ -657,7 +657,9 @@ impl NodeCore { receivers: Vec<(u128, AccountAddress)>, ) -> Result<(SendTxResponse, Vec<([u8; 32], [u8; 32])>)> { let point_before_prove = std::time::Instant::now(); - let (tx, utxo_hashes) = self.transfer_utxo_private(utxo, comm_hash, receivers).await; + let (tx, utxo_hashes) = self + .transfer_utxo_private(utxo, comm_hash, receivers) + .await?; tx.log(); let point_after_prove = std::time::Instant::now(); @@ -677,7 +679,7 @@ impl NodeCore { 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; + .await?; tx.log(); let point_after_prove = std::time::Instant::now(); @@ -698,7 +700,9 @@ impl NodeCore { receivers: Vec<(u128, AccountAddress)>, ) -> Result<(SendTxResponse, Vec<([u8; 32], [u8; 32])>)> { let point_before_prove = std::time::Instant::now(); - let (tx, utxo_hashes) = self.transfer_balance_shielded(acc, amount, receivers).await; + let (tx, utxo_hashes) = self + .transfer_balance_shielded(acc, amount, receivers) + .await?; tx.log(); let point_after_prove = std::time::Instant::now(); @@ -717,7 +721,7 @@ impl NodeCore { let point_before_prove = std::time::Instant::now(); let tx = self .transfer_utxo_deshielded(utxo, comm_gen_hash, receivers) - .await; + .await?; tx.log(); let point_after_prove = std::time::Instant::now(); @@ -727,13 +731,13 @@ impl NodeCore { Ok(self.sequencer_client.send_tx(tx).await?) } - async fn operate_account_mint_private( + pub async fn operate_account_mint_private( &mut self, acc_addr: AccountAddress, amount: u128, - ) -> (UTXO, [u8; 32]) { + ) -> Result<(UTXO, [u8; 32])> { let (resp, new_utxo_hash, comm_gen_hash) = - self.send_private_mint_tx(acc_addr, amount).await.unwrap(); + self.send_private_mint_tx(acc_addr, amount).await?; info!("Response for mint private is {resp:?}"); info!("Awaiting new blocks"); @@ -762,19 +766,18 @@ impl NodeCore { hex::encode(acc_addr) ); - (new_utxo, comm_gen_hash) + Ok((new_utxo, comm_gen_hash)) } - async fn operate_account_mint_multiple_assets_private( + pub async fn operate_account_mint_multiple_assets_private( &mut self, acc_addr: AccountAddress, amount: u128, number_of_assets: usize, - ) -> (Vec, Vec<[u8; 32]>) { + ) -> Result<(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(); + .await?; info!("Response for mint multiple assets private is {resp:?}"); info!("Awaiting new blocks"); @@ -812,16 +815,16 @@ impl NodeCore { .collect() }; - (new_utxos, comm_gen_hashes) + Ok((new_utxos, comm_gen_hashes)) } - async fn operate_account_send_deshielded_one_receiver( + pub 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], - ) { + ) -> Result<()> { let amount = utxo.amount; let old_balance = { @@ -839,8 +842,7 @@ impl NodeCore { let resp = self .send_deshielded_send_tx(utxo, comm_gen_hash, vec![(amount, acc_addr_rec)]) - .await - .unwrap(); + .await?; info!("Response for send deshielded is {resp:?}"); info!("Awaiting new blocks"); @@ -860,9 +862,15 @@ impl NodeCore { new_balance, new_balance - old_balance ); + + Ok(()) } - async fn operate_account_deposit_public(&mut self, acc_addr: AccountAddress, amount: u128) { + pub async fn operate_account_deposit_public( + &mut self, + acc_addr: AccountAddress, + amount: u128, + ) -> Result<()> { let old_balance = { let acc_map_read_guard = self.storage.read().await; @@ -876,7 +884,7 @@ impl NodeCore { hex::encode(acc_addr) ); - let resp = self.send_public_deposit(acc_addr, amount).await.unwrap(); + let resp = self.send_public_deposit(acc_addr, amount).await?; info!("Response for public deposit is {resp:?}"); info!("Awaiting new blocks"); @@ -895,18 +903,19 @@ impl NodeCore { hex::encode(acc_addr), new_balance - old_balance ); + + Ok(()) } - async fn operate_account_send_shielded_one_receiver( + pub async fn operate_account_send_shielded_one_receiver( &mut self, acc_addr_sender: AccountAddress, acc_addr_rec: AccountAddress, amount: u128, - ) { + ) -> Result { let (resp, new_utxo_hashes) = self .send_shielded_send_tx(acc_addr_sender, amount as u64, vec![(amount, acc_addr_rec)]) - .await - .unwrap(); + .await?; info!("Response for send shielded is {resp:?}"); let new_utxo_hash = new_utxo_hashes[0].1; @@ -936,20 +945,21 @@ impl NodeCore { "Account {:?} got new utxo with amount {amount:?}", hex::encode(acc_addr_rec) ); + + Ok(new_utxo) } - async fn operate_account_send_private_one_receiver( + pub async fn operate_account_send_private_one_receiver( &mut self, acc_addr_rec: AccountAddress, utxo: UTXO, comm_gen_hash: [u8; 32], - ) { + ) -> Result { 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(); + .await?; info!("Response for send private is {resp:?}"); let new_utxo_hash = new_utxo_hashes[0].1; @@ -980,16 +990,18 @@ impl NodeCore { hex::encode(acc_addr_rec), new_utxo.amount ); + + Ok(new_utxo) } - async fn operate_account_send_private_multiple_assets_one_receiver( + pub 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, - ) { + ) -> Result<()> { let (resp, new_utxo_hashes_rec, new_utxo_hashes_not_sp) = self .send_private_multiple_assets_send_tx( utxos, @@ -997,8 +1009,7 @@ impl NodeCore { number_to_send, acc_addr_rec, ) - .await - .unwrap(); + .await?; info!("Response for send private multiple assets is {resp:?}"); info!("Awaiting new blocks"); @@ -1057,6 +1068,8 @@ impl NodeCore { ); } } + + Ok(()) } pub async fn split_utxo( @@ -1065,7 +1078,7 @@ impl NodeCore { commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, visibility_list: [bool; 3], - ) -> (Transaction, Vec<(AccountAddress, [u8; 32])>) { + ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let accout = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -1080,7 +1093,7 @@ impl NodeCore { .to_vec(), ); - let (resulting_utxos, receipt) = prove_send_utxo(utxo, receivers); + let (resulting_utxos, receipt) = prove_send_utxo(utxo, receivers)?; let utxo_hashes = resulting_utxos .iter() .map(|(utxo, addr)| (addr.clone(), utxo.hash)) @@ -1127,7 +1140,7 @@ impl NodeCore { .collect(), }); - ( + Ok(( TransactionPayload { tx_kind: TxKind::Shielded, execution_input: vec![], @@ -1145,7 +1158,7 @@ impl NodeCore { } .into(), utxo_hashes, - ) + )) } pub async fn send_split_tx( @@ -1158,7 +1171,7 @@ impl NodeCore { let point_before_prove = std::time::Instant::now(); let (tx, utxo_hashes) = self .split_utxo(utxo, comm_hash, receivers, visibility_list) - .await; + .await?; tx.log(); let point_after_prove = std::time::Instant::now(); @@ -1174,13 +1187,13 @@ impl NodeCore { )) } - async fn operate_account_send_split_utxo( + pub 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]>) { + ) -> Result<(Vec, Vec<[u8; 32]>)> { let (resp, new_utxo_hashes, commitments_hashes) = self .send_split_tx( utxo.clone(), @@ -1191,8 +1204,7 @@ impl NodeCore { .to_vec(), visibility_list, ) - .await - .unwrap(); + .await?; info!("Response for send shielded is {resp:?}"); info!("Awaiting new blocks"); @@ -1230,14 +1242,14 @@ impl NodeCore { .collect() }; - (new_utxos, commitments_hashes) + Ok((new_utxos, commitments_hashes)) } ///Mint utxo, make it public - pub async fn subscenario_1(&mut self) { + pub async fn subscenario_1(&mut self) -> Result<()> { let acc_addr = self.create_new_account().await; - let (new_utxo, comm_gen_hash) = self.operate_account_mint_private(acc_addr, 100).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, @@ -1245,47 +1257,55 @@ impl NodeCore { new_utxo, comm_gen_hash, ) - .await; + .await?; + + Ok(()) } ///Deposit balance, make it private - pub async fn subscenario_2(&mut self) { + pub async fn subscenario_2(&mut self) -> Result<()> { let acc_addr = self.create_new_account().await; - self.operate_account_deposit_public(acc_addr, 100).await; + self.operate_account_deposit_public(acc_addr, 100).await?; self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr, 100) - .await; + .await?; + + Ok(()) } ///Mint utxo, privately send it to another user - pub async fn subscenario_3(&mut self) { + pub async fn subscenario_3(&mut self) -> Result<()> { 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; + 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; + .await?; + + Ok(()) } ///Deposit balance, shielded send it to another user - pub async fn subscenario_4(&mut self) { + pub async fn subscenario_4(&mut self) -> Result<()> { let acc_addr = self.create_new_account().await; let acc_addr_rec = self.create_new_account().await; - self.operate_account_deposit_public(acc_addr, 100).await; + self.operate_account_deposit_public(acc_addr, 100).await?; self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr_rec, 100) - .await; + .await?; + + Ok(()) } ///Mint utxo, deshielded send it to another user - pub async fn subscenario_5(&mut self) { + pub async fn subscenario_5(&mut self) -> Result<()> { 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; + let (new_utxo, comm_gen_hash) = self.operate_account_mint_private(acc_addr, 100).await?; self.operate_account_send_deshielded_one_receiver( acc_addr, @@ -1293,7 +1313,9 @@ impl NodeCore { new_utxo, comm_gen_hash, ) - .await; + .await?; + + Ok(()) } ///First complex scenario. @@ -1301,7 +1323,11 @@ impl NodeCore { /// 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) { + pub async fn scenario_1( + &mut self, + visibility_list: [bool; 3], + publication_index: usize, + ) -> Result<()> { let acc_addr_sender = self.create_new_account().await; let acc_addr_rec_1 = self.create_new_account().await; @@ -1311,8 +1337,8 @@ impl NodeCore { let addrs_receivers = [acc_addr_rec_1, acc_addr_rec_2, acc_addr_rec_3]; let (new_utxo, comm_gen_hash) = self - .operate_account_mint_private(acc_addr_sender, 100) - .await; + .operate_account_mint_private(acc_addr_sender, 99) + .await?; let (new_utxos, comm_gen_hashes) = self .operate_account_send_split_utxo( @@ -1321,7 +1347,7 @@ impl NodeCore { comm_gen_hash, visibility_list, ) - .await; + .await?; self.operate_account_send_deshielded_one_receiver( addrs_receivers[publication_index], @@ -1329,17 +1355,23 @@ impl NodeCore { new_utxos[publication_index].clone(), comm_gen_hashes[publication_index], ) - .await; + .await?; + + Ok(()) } ///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) { + pub async fn scenario_2( + &mut self, + number_of_assets: usize, + number_to_send: usize, + ) -> Result<()> { 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; + .await?; self.operate_account_send_private_multiple_assets_one_receiver( acc_addr_sender, @@ -1348,6 +1380,15 @@ impl NodeCore { comm_gen_hashes, number_to_send, ) - .await; + .await?; + + Ok(()) } } + +pub fn generate_commitments_helper(input_utxos: &[UTXO]) -> Vec<[u8; 32]> { + generate_commitments(input_utxos) + .into_iter() + .map(|comm_raw| comm_raw.try_into().unwrap()) + .collect() +} diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index 1f3dd17..aa4640b 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -1,6 +1,7 @@ use std::sync::atomic::Ordering; use actix_web::Error as HttpError; +use node_core::generate_commitments_helper; use serde_json::Value; use rpc_primitives::{ @@ -21,7 +22,14 @@ use crate::{ GetLastBlockRequest, GetLastBlockResponse, RegisterAccountRequest, RegisterAccountResponse, SendTxRequest, ShowAccountPublicBalanceRequest, ShowAccountPublicBalanceResponse, ShowAccountUTXORequest, ShowAccountUTXOResponse, - ShowTransactionRequest, ShowTransactionResponse, + ShowTransactionRequest, ShowTransactionResponse, UTXOShortEssentialStruct, + WriteDepositPublicBalanceRequest, WriteDepositPublicBalanceResponse, + WriteMintPrivateUTXOMultipleAssetsRequest, WriteMintPrivateUTXOMultipleAssetsResponse, + WriteMintPrivateUTXORequest, WriteMintPrivateUTXOResponse, + WriteSendDeshieldedBalanceRequest, WriteSendDeshieldedUTXOResponse, + WriteSendPrivateUTXORequest, WriteSendPrivateUTXOResponse, + WriteSendShieldedUTXORequest, WriteSendShieldedUTXOResponse, + WriteSendSplitUTXOResponse, WriteSplitUTXORequest, }, }, }; @@ -52,11 +60,11 @@ impl JsonHandler { let mut store = self.node_chain_store.lock().await; match req.scenario_id { - 1 => store.subscenario_1().await, - 2 => store.subscenario_2().await, - 3 => store.subscenario_3().await, - 4 => store.subscenario_4().await, - 5 => store.subscenario_5().await, + 1 => store.subscenario_1().await?, + 2 => store.subscenario_2().await?, + 3 => store.subscenario_3().await?, + 4 => store.subscenario_4().await?, + 5 => store.subscenario_5().await?, _ => return Err(RpcErr(RpcError::invalid_params("Scenario id not found"))), } } @@ -79,7 +87,7 @@ impl JsonHandler { store .scenario_1(req.visibility_list, req.publication_index) - .await; + .await?; } let helperstruct = ExecuteScenarioSplitResponse { @@ -100,7 +108,7 @@ impl JsonHandler { store .scenario_2(req.number_of_assets, req.number_to_send) - .await; + .await?; } let helperstruct = ExecuteScenarioMultipleSendResponse { @@ -126,26 +134,6 @@ impl JsonHandler { respond(helperstruct) } - async fn process_send_tx(&self, request: Request) -> Result { - let req = SendTxRequest::parse(Some(request.params))?; - - { - let guard = self.node_chain_store.lock().await; - - guard - .sequencer_client - .send_tx(req.transaction) - .await - .map_err(cast_seq_client_error_into_rpc_error)?; - } - - let helperstruct = RegisterAccountResponse { - status: "success".to_string(), - }; - - respond(helperstruct) - } - async fn process_get_block_data(&self, request: Request) -> Result { let req = GetBlockDataRequest::parse(Some(request.params))?; @@ -333,12 +321,425 @@ impl JsonHandler { respond(helperstruct) } + pub async fn process_write_deposit_public_balance( + &self, + request: Request, + ) -> Result { + let req = WriteDepositPublicBalanceRequest::parse(Some(request.params))?; + + let acc_addr_hex_dec = hex::decode(req.account_addr.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode account address from hex string".to_string()) + })?; + + let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + { + let mut cover_guard = self.node_chain_store.lock().await; + + cover_guard + .operate_account_deposit_public(acc_addr, req.amount as u128) + .await?; + }; + + let helperstruct = WriteDepositPublicBalanceResponse { + status: "success".to_string(), + }; + + respond(helperstruct) + } + + pub async fn process_write_mint_utxo(&self, request: Request) -> Result { + let req = WriteMintPrivateUTXORequest::parse(Some(request.params))?; + + let acc_addr_hex_dec = hex::decode(req.account_addr.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode account address from hex string".to_string()) + })?; + + let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + let (utxo, commitment_hash) = { + let mut cover_guard = self.node_chain_store.lock().await; + + cover_guard + .operate_account_mint_private(acc_addr, req.amount as u128) + .await? + }; + + let helperstruct = WriteMintPrivateUTXOResponse { + status: "success".to_string(), + utxo: UTXOShortEssentialStruct { + hash: hex::encode(utxo.hash), + commitment_hash: hex::encode(commitment_hash), + asset: utxo.asset, + }, + }; + + respond(helperstruct) + } + + pub async fn process_write_mint_utxo_multiple_assets( + &self, + request: Request, + ) -> Result { + let req = WriteMintPrivateUTXOMultipleAssetsRequest::parse(Some(request.params))?; + + let acc_addr_hex_dec = hex::decode(req.account_addr.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode account address from hex string".to_string()) + })?; + + let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + let (utxos, commitment_hashes) = { + let mut cover_guard = self.node_chain_store.lock().await; + + cover_guard + .operate_account_mint_multiple_assets_private( + acc_addr, + req.amount as u128, + req.num_of_assets, + ) + .await? + }; + + let helperstruct = WriteMintPrivateUTXOMultipleAssetsResponse { + status: "success".to_string(), + utxos: utxos + .into_iter() + .zip(commitment_hashes) + .map(|(utxo, comm_hash)| UTXOShortEssentialStruct { + hash: hex::encode(utxo.hash), + commitment_hash: hex::encode(comm_hash), + asset: utxo.asset, + }) + .collect(), + }; + + respond(helperstruct) + } + + pub async fn process_write_send_private_utxo(&self, request: Request) -> Result { + let req = WriteSendPrivateUTXORequest::parse(Some(request.params))?; + + let acc_addr_hex_dec_sender = + hex::decode(req.account_addr_sender.clone()).map_err(|_| { + RpcError::parse_error( + "Failed to decode account address from hex string".to_string(), + ) + })?; + + let acc_addr_sender: [u8; 32] = acc_addr_hex_dec_sender.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + let acc_addr_hex_dec = hex::decode(req.account_addr_receiver.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode account address from hex string".to_string()) + })?; + + let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + let utxo_hash_hex_dec = hex::decode(req.utxo_hash.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode utxo hash from hex string".to_string()) + })?; + + let utxo_hash: [u8; 32] = utxo_hash_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse utxo hash from bytes".to_string()) + })?; + + let comm_hash_hex_dec = hex::decode(req.utxo_commitment.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode commitment hash from hex string".to_string()) + })?; + + let comm_hash: [u8; 32] = comm_hash_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse commitment hash from bytes".to_string()) + })?; + + let new_utxo_rec = { + let mut cover_guard = self.node_chain_store.lock().await; + + let utxo_to_send = { + let mut under_guard = cover_guard.storage.write().await; + + let acc = under_guard + .acc_map + .get_mut(&acc_addr_sender) + .ok_or(RpcError::new_internal_error(None, "Account not found"))?; + + acc.utxo_tree + .get_item(utxo_hash) + .map_err(|err| { + RpcError::new_internal_error(None, &format!("DB fetch failure {err:?}")) + })? + .ok_or(RpcError::new_internal_error( + None, + "UTXO does not exist in tree", + ))? + .clone() + }; + + cover_guard + .operate_account_send_private_one_receiver(acc_addr, utxo_to_send, comm_hash) + .await? + }; + + let helperstruct = WriteSendPrivateUTXOResponse { + status: "success".to_string(), + utxo_result: UTXOShortEssentialStruct { + hash: hex::encode(new_utxo_rec.hash), + asset: new_utxo_rec.asset.clone(), + commitment_hash: hex::encode(generate_commitments_helper(&vec![new_utxo_rec])[0]), + }, + }; + + respond(helperstruct) + } + + pub async fn process_write_send_shielded_utxo( + &self, + request: Request, + ) -> Result { + let req = WriteSendShieldedUTXORequest::parse(Some(request.params))?; + + let acc_addr_hex_dec_sender = + hex::decode(req.account_addr_sender.clone()).map_err(|_| { + RpcError::parse_error( + "Failed to decode account address sender from hex string".to_string(), + ) + })?; + + let acc_addr_sender: [u8; 32] = acc_addr_hex_dec_sender.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address sender from bytes".to_string()) + })?; + + let acc_addr_hex_dec_rec = + hex::decode(req.account_addr_receiver.clone()).map_err(|_| { + RpcError::parse_error( + "Failed to decode account address receiver from hex string".to_string(), + ) + })?; + + let acc_addr_rec: [u8; 32] = acc_addr_hex_dec_rec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address receiver from bytes".to_string()) + })?; + + let new_utxo_rec = { + let mut cover_guard = self.node_chain_store.lock().await; + + cover_guard + .operate_account_send_shielded_one_receiver( + acc_addr_sender, + acc_addr_rec, + req.amount as u128, + ) + .await? + }; + + let helperstruct = WriteSendShieldedUTXOResponse { + status: "success".to_string(), + utxo_result: UTXOShortEssentialStruct { + hash: hex::encode(new_utxo_rec.hash), + asset: new_utxo_rec.asset.clone(), + commitment_hash: hex::encode(generate_commitments_helper(&vec![new_utxo_rec])[0]), + }, + }; + + respond(helperstruct) + } + + pub async fn process_write_send_deshielded_utxo( + &self, + request: Request, + ) -> Result { + let req = WriteSendDeshieldedBalanceRequest::parse(Some(request.params))?; + + let acc_addr_hex_dec_sender = + hex::decode(req.account_addr_sender.clone()).map_err(|_| { + RpcError::parse_error( + "Failed to decode account address from hex string".to_string(), + ) + })?; + + let acc_addr_sender: [u8; 32] = acc_addr_hex_dec_sender.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + let acc_addr_hex_dec = hex::decode(req.account_addr_receiver.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode account address from hex string".to_string()) + })?; + + let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + let utxo_hash_hex_dec = hex::decode(req.utxo_hash.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode utxo hash from hex string".to_string()) + })?; + + let utxo_hash: [u8; 32] = utxo_hash_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse utxo hash from bytes".to_string()) + })?; + + let comm_hash_hex_dec = hex::decode(req.utxo_commitment.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode commitment hash from hex string".to_string()) + })?; + + let comm_hash: [u8; 32] = comm_hash_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse commitment hash from bytes".to_string()) + })?; + + { + let mut cover_guard = self.node_chain_store.lock().await; + + let utxo_to_send = { + let mut under_guard = cover_guard.storage.write().await; + + let acc = under_guard + .acc_map + .get_mut(&acc_addr_sender) + .ok_or(RpcError::new_internal_error(None, "Account not found"))?; + + acc.utxo_tree + .get_item(utxo_hash) + .map_err(|err| { + RpcError::new_internal_error(None, &format!("DB fetch failure {err:?}")) + })? + .ok_or(RpcError::new_internal_error( + None, + "UTXO does not exist in tree", + ))? + .clone() + }; + + cover_guard + .operate_account_send_deshielded_one_receiver( + acc_addr_sender, + acc_addr, + utxo_to_send, + comm_hash, + ) + .await? + }; + + let helperstruct = WriteSendDeshieldedUTXOResponse { + status: "success".to_string(), + }; + + respond(helperstruct) + } + + pub async fn process_write_send_split_utxo(&self, request: Request) -> Result { + let req = WriteSplitUTXORequest::parse(Some(request.params))?; + + let acc_addr_hex_dec_sender = + hex::decode(req.account_addr_sender.clone()).map_err(|_| { + RpcError::parse_error( + "Failed to decode account address from hex string".to_string(), + ) + })?; + + let acc_addr_sender: [u8; 32] = acc_addr_hex_dec_sender.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse account address from bytes".to_string()) + })?; + + let acc_addresses = { + let mut res_addrs = vec![]; + + for item in req.account_addr_receivers { + let hex_dec_item = hex::decode(item).map_err(|_| { + RpcError::parse_error( + "Failed to decode account address from hex string".to_string(), + ) + })?; + + let dec_item = hex_dec_item.try_into().map_err(|_| { + RpcError::parse_error( + "Failed to decode account address from hex string".to_string(), + ) + })?; + + res_addrs.push(dec_item); + } + + res_addrs.try_into().unwrap() + }; + + let utxo_hash_hex_dec = hex::decode(req.utxo_hash.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode utxo hash from hex string".to_string()) + })?; + + let utxo_hash: [u8; 32] = utxo_hash_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse utxo hash from bytes".to_string()) + })?; + + let comm_hash_hex_dec = hex::decode(req.utxo_commitment.clone()).map_err(|_| { + RpcError::parse_error("Failed to decode commitment hash from hex string".to_string()) + })?; + + let comm_hash: [u8; 32] = comm_hash_hex_dec.try_into().map_err(|_| { + RpcError::parse_error("Failed to parse commitment hash from bytes".to_string()) + })?; + + let (new_utxos, commitment_hashes) = { + let mut cover_guard = self.node_chain_store.lock().await; + + let utxo_to_send = { + let mut under_guard = cover_guard.storage.write().await; + + let acc = under_guard + .acc_map + .get_mut(&acc_addr_sender) + .ok_or(RpcError::new_internal_error(None, "Account not found"))?; + + acc.utxo_tree + .get_item(utxo_hash) + .map_err(|err| { + RpcError::new_internal_error(None, &format!("DB fetch failure {err:?}")) + })? + .ok_or(RpcError::new_internal_error( + None, + "UTXO does not exist in tree", + ))? + .clone() + }; + + cover_guard + .operate_account_send_split_utxo( + acc_addresses, + utxo_to_send, + comm_hash, + req.visibility_list, + ) + .await? + }; + + let helperstruct = WriteSendSplitUTXOResponse { + status: "success".to_string(), + utxo_results: new_utxos + .into_iter() + .zip(commitment_hashes) + .map(|(utxo, comm_hash)| UTXOShortEssentialStruct { + hash: hex::encode(utxo.hash), + commitment_hash: hex::encode(comm_hash), + asset: utxo.asset, + }) + .collect(), + }; + + respond(helperstruct) + } + pub async fn process_request_internal(&self, request: Request) -> Result { match request.method.as_ref() { //Todo : Add handling of more JSON RPC methods - "register_account" => self.process_register_account(request).await, + "write_register_account" => self.process_register_account(request).await, "execute_subscenario" => self.process_request_execute_subscenario(request).await, - "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, @@ -350,7 +751,7 @@ impl JsonHandler { self.process_show_account_public_balance(request).await } "show_account_utxo" => self.process_show_account_utxo_request(request).await, - "show_trasnaction" => self.process_show_transaction(request).await, + "show_transaction" => self.process_show_transaction(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 807125d..563db03 100644 --- a/node_rpc/src/types/rpc_structs.rs +++ b/node_rpc/src/types/rpc_structs.rs @@ -60,6 +60,57 @@ pub struct ShowTransactionRequest { pub tx_hash: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteDepositPublicBalanceRequest { + pub account_addr: String, + pub amount: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteMintPrivateUTXORequest { + pub account_addr: String, + pub amount: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteMintPrivateUTXOMultipleAssetsRequest { + pub account_addr: String, + pub num_of_assets: usize, + pub amount: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSendPrivateUTXORequest { + pub account_addr_sender: String, + pub account_addr_receiver: String, + pub utxo_hash: String, + pub utxo_commitment: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSendShieldedUTXORequest { + pub account_addr_sender: String, + pub account_addr_receiver: String, + pub amount: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSendDeshieldedBalanceRequest { + pub account_addr_sender: String, + pub account_addr_receiver: String, + pub utxo_hash: String, + pub utxo_commitment: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSplitUTXORequest { + pub account_addr_sender: String, + pub account_addr_receivers: [String; 3], + pub visibility_list: [bool; 3], + pub utxo_hash: String, + pub utxo_commitment: String, +} + parse_request!(RegisterAccountRequest); parse_request!(SendTxRequest); parse_request!(GetBlockDataRequest); @@ -68,10 +119,19 @@ parse_request!(ExecuteSubscenarioRequest); parse_request!(ExecuteScenarioSplitRequest); parse_request!(ExecuteScenarioMultipleSendRequest); parse_request!(GetLastBlockRequest); + parse_request!(ShowAccountPublicBalanceRequest); parse_request!(ShowAccountUTXORequest); parse_request!(ShowTransactionRequest); +parse_request!(WriteDepositPublicBalanceRequest); +parse_request!(WriteMintPrivateUTXORequest); +parse_request!(WriteMintPrivateUTXOMultipleAssetsRequest); +parse_request!(WriteSendPrivateUTXORequest); +parse_request!(WriteSendShieldedUTXORequest); +parse_request!(WriteSendDeshieldedBalanceRequest); +parse_request!(WriteSplitUTXORequest); + #[derive(Serialize, Deserialize, Debug)] pub struct HelloResponse { pub greeting: String, @@ -142,3 +202,50 @@ pub struct ShowTransactionResponse { pub encoded_data: Vec<(String, String)>, pub ephemeral_pub_key: String, } + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteDepositPublicBalanceResponse { + pub status: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct UTXOShortEssentialStruct { + pub hash: String, + pub commitment_hash: String, + pub asset: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteMintPrivateUTXOResponse { + pub status: String, + pub utxo: UTXOShortEssentialStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteMintPrivateUTXOMultipleAssetsResponse { + pub status: String, + pub utxos: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSendPrivateUTXOResponse { + pub status: String, + pub utxo_result: UTXOShortEssentialStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSendShieldedUTXOResponse { + pub status: String, + pub utxo_result: UTXOShortEssentialStruct, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSendDeshieldedUTXOResponse { + pub status: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WriteSendSplitUTXOResponse { + pub status: String, + pub utxo_results: Vec, +} diff --git a/zkvm/Cargo.toml b/zkvm/Cargo.toml index 5a2139b..55d7e15 100644 --- a/zkvm/Cargo.toml +++ b/zkvm/Cargo.toml @@ -9,6 +9,7 @@ serde_json.workspace = true env_logger.workspace = true log.workspace = true serde.workspace = true +thiserror.workspace = true risc0-zkvm = { git = "https://github.com/risc0/risc0.git", branch = "release-1.2" } test-methods = { path = "test_methods" } diff --git a/zkvm/src/lib.rs b/zkvm/src/lib.rs index 7c26f73..aff0ced 100644 --- a/zkvm/src/lib.rs +++ b/zkvm/src/lib.rs @@ -2,83 +2,144 @@ use accounts::account_core::AccountAddress; use risc0_zkvm::{default_executor, default_prover, sha::Digest, ExecutorEnv, Receipt}; use utxo::utxo_core::{UTXOPayload, UTXO}; -pub fn prove_mint_utxo(amount_to_mint: u128, owner: AccountAddress) -> (UTXO, Receipt) { +#[derive(Debug, thiserror::Error)] +pub enum ExecutionFailureKind { + #[error("Failed to write into builder err: {0:?}")] + WriteError(anyhow::Error), + #[error("Failed to build builder err: {0:?}")] + BuilderError(anyhow::Error), + #[error("Failed prove execution err: {0:?}")] + ProveError(anyhow::Error), + #[error("Failed to decode data from VM: {0:?}")] + DecodeError(#[from] risc0_zkvm::serde::Error), + #[error("Inputs amounts does not match outputs")] + AmountMismatchError, +} + +impl ExecutionFailureKind { + pub fn write_error(err: anyhow::Error) -> Self { + Self::WriteError(err) + } + + pub fn builder_error(err: anyhow::Error) -> Self { + Self::BuilderError(err) + } + + pub fn prove_error(err: anyhow::Error) -> Self { + Self::ProveError(err) + } +} + +pub fn prove_mint_utxo( + amount_to_mint: u128, + owner: AccountAddress, +) -> Result<(UTXO, Receipt), ExecutionFailureKind> { let mut builder = ExecutorEnv::builder(); - builder.write(&amount_to_mint).unwrap(); - builder.write(&owner).unwrap(); + builder + .write(&amount_to_mint) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&owner) + .map_err(ExecutionFailureKind::write_error)?; - let env = builder.build().unwrap(); + let env = builder + .build() + .map_err(ExecutionFailureKind::builder_error)?; let prover = default_prover(); let receipt = prover .prove(env, test_methods::MINT_UTXO_ELF) - .unwrap() + .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: UTXOPayload = receipt.journal.decode().unwrap(); + let digest: UTXOPayload = receipt.journal.decode()?; - (UTXO::create_utxo_from_payload(digest), receipt) + Ok((UTXO::create_utxo_from_payload(digest), receipt)) } pub fn prove_send_utxo( spent_utxo: UTXO, owners_parts: Vec<(u128, AccountAddress)>, -) -> (Vec<(UTXO, AccountAddress)>, Receipt) { +) -> Result<(Vec<(UTXO, AccountAddress)>, Receipt), ExecutionFailureKind> { + let cumulative_spent = owners_parts.iter().fold(0, |acc, item| acc + item.0); + + if cumulative_spent != spent_utxo.amount { + return Err(ExecutionFailureKind::AmountMismatchError); + } + let mut builder = ExecutorEnv::builder(); let utxo_payload = spent_utxo.into_payload(); - builder.write(&utxo_payload).unwrap(); - builder.write(&owners_parts).unwrap(); + builder + .write(&utxo_payload) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&owners_parts) + .map_err(ExecutionFailureKind::write_error)?; - let env = builder.build().unwrap(); + let env = builder + .build() + .map_err(ExecutionFailureKind::builder_error)?; let prover = default_prover(); let receipt = prover .prove(env, test_methods::SEND_UTXO_ELF) - .unwrap() + .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode().unwrap(); + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode()?; - ( + Ok(( digest .into_iter() .map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr)) .collect(), receipt, - ) + )) } pub fn prove_send_utxo_multiple_assets_one_receiver( spent_utxos: Vec, number_to_send: usize, receiver: AccountAddress, -) -> (Vec, Vec, Receipt) { +) -> Result<(Vec, Vec, Receipt), ExecutionFailureKind> { + if number_to_send > spent_utxos.len() { + return Err(ExecutionFailureKind::AmountMismatchError); + } + 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(); + builder + .write(&utxo_payload) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&number_to_send) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&receiver) + .map_err(ExecutionFailureKind::write_error)?; - let env = builder.build().unwrap(); + let env = builder + .build() + .map_err(ExecutionFailureKind::builder_error)?; let prover = default_prover(); let receipt = prover .prove(env, test_methods::SEND_UTXO_MULTIPLE_ASSETS_ELF) - .unwrap() + .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: (Vec, Vec) = receipt.journal.decode().unwrap(); + let digest: (Vec, Vec) = receipt.journal.decode()?; - ( + Ok(( digest .0 .into_iter() @@ -90,14 +151,20 @@ pub fn prove_send_utxo_multiple_assets_one_receiver( .map(|payload| UTXO::create_utxo_from_payload(payload)) .collect(), receipt, - ) + )) } pub fn prove_send_utxo_shielded( owner: AccountAddress, amount: u128, owners_parts: Vec<(u128, AccountAddress)>, -) -> (Vec<(UTXO, AccountAddress)>, Receipt) { +) -> Result<(Vec<(UTXO, AccountAddress)>, Receipt), ExecutionFailureKind> { + let cumulative_spent = owners_parts.iter().fold(0, |acc, item| acc + item.0); + + if cumulative_spent != amount { + return Err(ExecutionFailureKind::AmountMismatchError); + } + let temp_utxo_to_spend = UTXO::create_utxo_from_payload(UTXOPayload { owner, asset: vec![], @@ -108,88 +175,114 @@ pub fn prove_send_utxo_shielded( let mut builder = ExecutorEnv::builder(); - builder.write(&utxo_payload).unwrap(); - builder.write(&owners_parts).unwrap(); + builder + .write(&utxo_payload) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&owners_parts) + .map_err(ExecutionFailureKind::write_error)?; - let env = builder.build().unwrap(); + let env = builder + .build() + .map_err(ExecutionFailureKind::builder_error)?; let prover = default_prover(); let receipt = prover .prove(env, test_methods::SEND_UTXO_ELF) - .unwrap() + .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode().unwrap(); + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode()?; - ( + Ok(( digest .into_iter() .map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr)) .collect(), receipt, - ) + )) } pub fn prove_send_utxo_deshielded( spent_utxo: UTXO, owners_parts: Vec<(u128, AccountAddress)>, -) -> (Vec<(u128, AccountAddress)>, Receipt) { +) -> Result<(Vec<(u128, AccountAddress)>, Receipt), ExecutionFailureKind> { + let cumulative_spent = owners_parts.iter().fold(0, |acc, item| acc + item.0); + + if cumulative_spent != spent_utxo.amount { + return Err(ExecutionFailureKind::AmountMismatchError); + } + let mut builder = ExecutorEnv::builder(); let utxo_payload = spent_utxo.into_payload(); - builder.write(&utxo_payload).unwrap(); - builder.write(&owners_parts).unwrap(); + builder + .write(&utxo_payload) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&owners_parts) + .map_err(ExecutionFailureKind::write_error)?; - let env = builder.build().unwrap(); + let env = builder + .build() + .map_err(ExecutionFailureKind::builder_error)?; let prover = default_prover(); let receipt = prover .prove(env, test_methods::SEND_UTXO_ELF) - .unwrap() + .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode().unwrap(); + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode()?; - ( + Ok(( digest .into_iter() .map(|(payload, addr)| (payload.amount, addr)) .collect(), receipt, - ) + )) } pub fn prove_mint_utxo_multiple_assets( amount_to_mint: u128, number_of_assets: usize, owner: AccountAddress, -) -> (Vec, Receipt) { +) -> Result<(Vec, Receipt), ExecutionFailureKind> { let mut builder = ExecutorEnv::builder(); - builder.write(&amount_to_mint).unwrap(); - builder.write(&number_of_assets).unwrap(); - builder.write(&owner).unwrap(); + builder + .write(&amount_to_mint) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&number_of_assets) + .map_err(ExecutionFailureKind::write_error)?; + builder + .write(&owner) + .map_err(ExecutionFailureKind::write_error)?; - let env = builder.build().unwrap(); + let env = builder + .build() + .map_err(ExecutionFailureKind::builder_error)?; let prover = default_prover(); let receipt = prover .prove(env, test_methods::MINT_UTXO_MULTIPLE_ASSETS_ELF) - .unwrap() + .map_err(ExecutionFailureKind::prove_error)? .receipt; - let digest: Vec = receipt.journal.decode().unwrap(); + let digest: Vec = receipt.journal.decode()?; - ( + Ok(( digest .into_iter() .map(UTXO::create_utxo_from_payload) .collect(), receipt, - ) + )) } pub fn execute_mint_utxo(amount_to_mint: u128, owner: AccountAddress) -> UTXO {