mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-05 23:03:06 +00:00
remove usage of Merkle tree for local storage of utxos. Remove assets.
This commit is contained in:
parent
25ef949a97
commit
dcd8fbcd05
@ -1,14 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use common::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier, transaction::Tag};
|
use common::{merkle_tree_public::TreeHashType, transaction::Tag};
|
||||||
use k256::AffinePoint;
|
use k256::AffinePoint;
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use utxo::{
|
use utxo::utxo_core::UTXO;
|
||||||
utxo_core::{UTXOPayload, UTXO},
|
|
||||||
utxo_tree::UTXOSparseMerkleTree,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::key_management::{
|
use crate::key_management::{
|
||||||
constants_types::{CipherText, Nonce},
|
constants_types::{CipherText, Nonce},
|
||||||
@ -23,7 +20,7 @@ pub struct Account {
|
|||||||
pub key_holder: AddressKeyHolder,
|
pub key_holder: AddressKeyHolder,
|
||||||
pub address: AccountAddress,
|
pub address: AccountAddress,
|
||||||
pub balance: u64,
|
pub balance: u64,
|
||||||
pub utxo_tree: UTXOSparseMerkleTree,
|
pub utxos: HashMap<TreeHashType, UTXO>,
|
||||||
}
|
}
|
||||||
|
|
||||||
///A strucure, which represents all the visible(public) information
|
///A strucure, which represents all the visible(public) information
|
||||||
@ -42,26 +39,30 @@ impl Account {
|
|||||||
let key_holder = AddressKeyHolder::new_os_random();
|
let key_holder = AddressKeyHolder::new_os_random();
|
||||||
let address = key_holder.address;
|
let address = key_holder.address;
|
||||||
let balance = 0;
|
let balance = 0;
|
||||||
let utxo_tree = UTXOSparseMerkleTree::new();
|
let utxos = HashMap::new();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
key_holder,
|
key_holder,
|
||||||
address,
|
address,
|
||||||
balance,
|
balance,
|
||||||
utxo_tree,
|
utxos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_utxo_by_hash(&self, utxo_hash: TreeHashType) -> Option<&UTXO> {
|
||||||
|
self.utxos.get(&utxo_hash)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_with_balance(balance: u64) -> Self {
|
pub fn new_with_balance(balance: u64) -> Self {
|
||||||
let key_holder = AddressKeyHolder::new_os_random();
|
let key_holder = AddressKeyHolder::new_os_random();
|
||||||
let address = key_holder.address;
|
let address = key_holder.address;
|
||||||
let utxo_tree = UTXOSparseMerkleTree::new();
|
let utxo_tree = HashMap::new();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
key_holder,
|
key_holder,
|
||||||
address,
|
address,
|
||||||
balance,
|
balance,
|
||||||
utxo_tree,
|
utxos: utxo_tree,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,47 +88,20 @@ impl Account {
|
|||||||
.decrypt_data(ephemeral_public_key_sender, ciphertext, nonce)
|
.decrypt_data(ephemeral_public_key_sender, ciphertext, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_spent_utxo(
|
|
||||||
&mut self,
|
|
||||||
utxo_nullifier_map: HashMap<TreeHashType, UTXONullifier>,
|
|
||||||
) -> Result<()> {
|
|
||||||
for (hash, nullifier) in utxo_nullifier_map {
|
|
||||||
if let Some(utxo_entry) = self.utxo_tree.store.get_mut(&hash) {
|
|
||||||
utxo_entry.consume_utxo(nullifier)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_new_utxo_outputs(&mut self, utxos: Vec<UTXO>) -> Result<()> {
|
pub fn add_new_utxo_outputs(&mut self, utxos: Vec<UTXO>) -> Result<()> {
|
||||||
Ok(self.utxo_tree.insert_items(utxos)?)
|
for utxo in utxos {
|
||||||
|
if self.utxos.contains_key(&utxo.hash) {
|
||||||
|
return Err(anyhow::anyhow!("UTXO already exists"));
|
||||||
|
}
|
||||||
|
self.utxos.insert(utxo.hash, utxo);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_public_balance(&mut self, new_balance: u64) {
|
pub fn update_public_balance(&mut self, new_balance: u64) {
|
||||||
self.balance = new_balance;
|
self.balance = new_balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_asset<Asset: Serialize>(
|
|
||||||
&mut self,
|
|
||||||
asset: Asset,
|
|
||||||
amount: u128,
|
|
||||||
privacy_flag: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
let payload_with_asset = UTXOPayload {
|
|
||||||
owner: self.address,
|
|
||||||
asset: serde_json::to_vec(&asset)?,
|
|
||||||
amount,
|
|
||||||
privacy_flag,
|
|
||||||
};
|
|
||||||
|
|
||||||
let asset_utxo = UTXO::create_utxo_from_payload(payload_with_asset)?;
|
|
||||||
|
|
||||||
self.utxo_tree.insert_item(asset_utxo)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log(&self) {
|
pub fn log(&self) {
|
||||||
info!("Keys generated");
|
info!("Keys generated");
|
||||||
info!("Account address is {:?}", hex::encode(self.address));
|
info!("Account address is {:?}", hex::encode(self.address));
|
||||||
@ -157,11 +131,9 @@ impl Default for Account {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use utxo::utxo_core::UTXOPayload;
|
||||||
|
|
||||||
fn generate_dummy_utxo_nullifier() -> UTXONullifier {
|
use super::*;
|
||||||
UTXONullifier::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_dummy_utxo(address: TreeHashType, amount: u128) -> anyhow::Result<UTXO> {
|
fn generate_dummy_utxo(address: TreeHashType, amount: u128) -> anyhow::Result<UTXO> {
|
||||||
let payload = UTXOPayload {
|
let payload = UTXOPayload {
|
||||||
@ -181,21 +153,6 @@ mod tests {
|
|||||||
assert!(account.key_holder.address != [0u8; 32]); // Check if the address is not empty
|
assert!(account.key_holder.address != [0u8; 32]); // Check if the address is not empty
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mark_spent_utxo() {
|
|
||||||
let mut account = Account::new();
|
|
||||||
let utxo = generate_dummy_utxo(account.address, 100).unwrap();
|
|
||||||
account.add_new_utxo_outputs(vec![utxo]).unwrap();
|
|
||||||
|
|
||||||
let mut utxo_nullifier_map = HashMap::new();
|
|
||||||
utxo_nullifier_map.insert(account.address, generate_dummy_utxo_nullifier());
|
|
||||||
|
|
||||||
let result = account.mark_spent_utxo(utxo_nullifier_map);
|
|
||||||
|
|
||||||
assert!(result.is_ok());
|
|
||||||
assert!(account.utxo_tree.store.get(&account.address).is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_new_utxo_outputs() {
|
fn test_add_new_utxo_outputs() {
|
||||||
let mut account = Account::new();
|
let mut account = Account::new();
|
||||||
@ -205,7 +162,7 @@ mod tests {
|
|||||||
let result = account.add_new_utxo_outputs(vec![utxo1.clone(), utxo2.clone()]);
|
let result = account.add_new_utxo_outputs(vec![utxo1.clone(), utxo2.clone()]);
|
||||||
|
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
assert_eq!(account.utxo_tree.store.len(), 2);
|
assert_eq!(account.utxos.len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -215,16 +172,4 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(account.balance, 500);
|
assert_eq!(account.balance, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add_asset() {
|
|
||||||
let mut account = Account::new();
|
|
||||||
let asset = "dummy_asset";
|
|
||||||
let amount = 1000u128;
|
|
||||||
|
|
||||||
let result = account.add_asset(asset, amount, false);
|
|
||||||
|
|
||||||
assert!(result.is_ok());
|
|
||||||
assert_eq!(account.utxo_tree.store.len(), 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,7 +125,7 @@ impl NodeChainStore {
|
|||||||
serde_json::from_slice::<UTXO>(&decoded_data_curr_acc);
|
serde_json::from_slice::<UTXO>(&decoded_data_curr_acc);
|
||||||
if let Ok(utxo) = decoded_utxo_try {
|
if let Ok(utxo) = decoded_utxo_try {
|
||||||
if &utxo.owner == acc_id {
|
if &utxo.owner == acc_id {
|
||||||
acc.utxo_tree.insert_item(utxo)?;
|
acc.utxos.insert(utxo.hash, utxo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1058,11 +1058,15 @@ impl NodeCore {
|
|||||||
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
||||||
|
|
||||||
let new_utxo = {
|
let new_utxo = {
|
||||||
let mut write_guard = self.storage.write().await;
|
let read_guard = self.storage.read().await;
|
||||||
|
|
||||||
let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap();
|
let acc = read_guard.acc_map.get(&acc_addr).unwrap();
|
||||||
|
|
||||||
acc.utxo_tree.get_item(new_utxo_hash)?.unwrap().clone()
|
acc.get_utxo_by_hash(new_utxo_hash)
|
||||||
|
.ok_or(ExecutionFailureKind::DBError(anyhow::anyhow!(
|
||||||
|
"Minted UTXO did not land"
|
||||||
|
)))?
|
||||||
|
.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
new_utxo.log();
|
new_utxo.log();
|
||||||
@ -1079,55 +1083,6 @@ impl NodeCore {
|
|||||||
Ok((new_utxo, comm_gen_hash))
|
Ok((new_utxo, comm_gen_hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn operate_account_mint_multiple_assets_private(
|
|
||||||
&mut self,
|
|
||||||
acc_addr: AccountAddress,
|
|
||||||
amount: u128,
|
|
||||||
number_of_assets: usize,
|
|
||||||
) -> Result<(Vec<UTXO>, Vec<[u8; 32]>), ExecutionFailureKind> {
|
|
||||||
let (resp, new_utxo_hashes, comm_gen_hashes) = self
|
|
||||||
.send_private_mint_multiple_assets_tx(acc_addr, amount, number_of_assets)
|
|
||||||
.await?;
|
|
||||||
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()
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((new_utxos, comm_gen_hashes))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn operate_account_send_deshielded_one_receiver(
|
pub async fn operate_account_send_deshielded_one_receiver(
|
||||||
&mut self,
|
&mut self,
|
||||||
acc_addr_rec: AccountAddress,
|
acc_addr_rec: AccountAddress,
|
||||||
@ -1233,12 +1188,16 @@ impl NodeCore {
|
|||||||
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
||||||
|
|
||||||
let new_utxo = {
|
let new_utxo = {
|
||||||
let mut write_guard = self.storage.write().await;
|
let read_guard = self.storage.read().await;
|
||||||
|
|
||||||
let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap();
|
let acc = read_guard.acc_map.get(&acc_addr_rec).unwrap();
|
||||||
acc.log();
|
acc.log();
|
||||||
|
|
||||||
acc.utxo_tree.get_item(new_utxo_hash)?.unwrap().clone()
|
acc.get_utxo_by_hash(new_utxo_hash)
|
||||||
|
.ok_or(ExecutionFailureKind::DBError(anyhow::anyhow!(
|
||||||
|
"Minted UTXO did not land"
|
||||||
|
)))?
|
||||||
|
.clone()
|
||||||
};
|
};
|
||||||
new_utxo.log();
|
new_utxo.log();
|
||||||
info!(
|
info!(
|
||||||
@ -1273,12 +1232,16 @@ impl NodeCore {
|
|||||||
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
||||||
|
|
||||||
let new_utxo = {
|
let new_utxo = {
|
||||||
let mut write_guard = self.storage.write().await;
|
let read_guard = self.storage.read().await;
|
||||||
|
|
||||||
let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap();
|
let acc = read_guard.acc_map.get(&acc_addr_rec).unwrap();
|
||||||
acc.log();
|
acc.log();
|
||||||
|
|
||||||
acc.utxo_tree.get_item(new_utxo_hash)?.unwrap().clone()
|
acc.get_utxo_by_hash(new_utxo_hash)
|
||||||
|
.ok_or(ExecutionFailureKind::DBError(anyhow::anyhow!(
|
||||||
|
"Minted UTXO did not land"
|
||||||
|
)))?
|
||||||
|
.clone()
|
||||||
};
|
};
|
||||||
new_utxo.log();
|
new_utxo.log();
|
||||||
info!(
|
info!(
|
||||||
@ -1317,13 +1280,18 @@ impl NodeCore {
|
|||||||
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut write_guard = self.storage.write().await;
|
let read_guard = self.storage.read().await;
|
||||||
|
|
||||||
for new_utxo_hash in new_utxo_hashes_rec {
|
for new_utxo_hash in new_utxo_hashes_rec {
|
||||||
let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap();
|
let acc = read_guard.acc_map.get(&acc_addr_rec).unwrap();
|
||||||
acc.log();
|
acc.log();
|
||||||
|
|
||||||
let new_utxo = acc.utxo_tree.get_item(new_utxo_hash)?.unwrap().clone();
|
let new_utxo = acc
|
||||||
|
.get_utxo_by_hash(new_utxo_hash)
|
||||||
|
.ok_or(ExecutionFailureKind::DBError(anyhow::anyhow!(
|
||||||
|
"Minted UTXO did not land"
|
||||||
|
)))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
new_utxo.log();
|
new_utxo.log();
|
||||||
info!(
|
info!(
|
||||||
@ -1340,10 +1308,15 @@ impl NodeCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for new_utxo_hash in new_utxo_hashes_not_sp {
|
for new_utxo_hash in new_utxo_hashes_not_sp {
|
||||||
let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap();
|
let acc = read_guard.acc_map.get(&acc_addr).unwrap();
|
||||||
acc.log();
|
acc.log();
|
||||||
|
|
||||||
let new_utxo = acc.utxo_tree.get_item(new_utxo_hash)?.unwrap().clone();
|
let new_utxo = acc
|
||||||
|
.get_utxo_by_hash(new_utxo_hash)
|
||||||
|
.ok_or(ExecutionFailureKind::DBError(anyhow::anyhow!(
|
||||||
|
"Minted UTXO did not land"
|
||||||
|
)))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
new_utxo.log();
|
new_utxo.log();
|
||||||
info!(
|
info!(
|
||||||
@ -1549,19 +1522,14 @@ impl NodeCore {
|
|||||||
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await;
|
||||||
|
|
||||||
let new_utxos: Vec<UTXO> = {
|
let new_utxos: Vec<UTXO> = {
|
||||||
let mut write_guard = self.storage.write().await;
|
let read_guard = self.storage.read().await;
|
||||||
|
|
||||||
new_utxo_hashes
|
new_utxo_hashes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(acc_addr_rec, new_utxo_hash)| {
|
.map(|(acc_addr_rec, new_utxo_hash)| {
|
||||||
let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap();
|
let acc = read_guard.acc_map.get(&acc_addr_rec).unwrap();
|
||||||
|
|
||||||
let new_utxo = acc
|
let new_utxo = acc.get_utxo_by_hash(new_utxo_hash).unwrap().clone();
|
||||||
.utxo_tree
|
|
||||||
.get_item(new_utxo_hash)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
new_utxo.log();
|
new_utxo.log();
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
@ -1686,31 +1654,6 @@ impl NodeCore {
|
|||||||
|
|
||||||
Ok(())
|
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,
|
|
||||||
) -> Result<(), ExecutionFailureKind> {
|
|
||||||
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?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_commitments_helper(input_utxos: &[UTXO]) -> Vec<[u8; 32]> {
|
pub fn generate_commitments_helper(input_utxos: &[UTXO]) -> Vec<[u8; 32]> {
|
||||||
|
|||||||
@ -18,14 +18,12 @@ use common::rpc_primitives::requests::{
|
|||||||
use crate::types::{
|
use crate::types::{
|
||||||
err_rpc::cast_common_execution_error_into_rpc_error,
|
err_rpc::cast_common_execution_error_into_rpc_error,
|
||||||
rpc_structs::{
|
rpc_structs::{
|
||||||
CreateAccountRequest, CreateAccountResponse, ExecuteScenarioMultipleSendRequest,
|
CreateAccountRequest, CreateAccountResponse, ExecuteScenarioSplitRequest,
|
||||||
ExecuteScenarioMultipleSendResponse, ExecuteScenarioSplitRequest,
|
|
||||||
ExecuteScenarioSplitResponse, ExecuteSubscenarioRequest, ExecuteSubscenarioResponse,
|
ExecuteScenarioSplitResponse, ExecuteSubscenarioRequest, ExecuteSubscenarioResponse,
|
||||||
ShowAccountPublicBalanceRequest, ShowAccountPublicBalanceResponse, ShowAccountUTXORequest,
|
ShowAccountPublicBalanceRequest, ShowAccountPublicBalanceResponse, ShowAccountUTXORequest,
|
||||||
ShowAccountUTXOResponse, ShowTransactionRequest, ShowTransactionResponse,
|
ShowAccountUTXOResponse, ShowTransactionRequest, ShowTransactionResponse,
|
||||||
UTXOShortEssentialStruct, WriteDepositPublicBalanceRequest,
|
UTXOShortEssentialStruct, WriteDepositPublicBalanceRequest,
|
||||||
WriteDepositPublicBalanceResponse, WriteMintPrivateUTXOMultipleAssetsRequest,
|
WriteDepositPublicBalanceResponse, WriteMintPrivateUTXORequest,
|
||||||
WriteMintPrivateUTXOMultipleAssetsResponse, WriteMintPrivateUTXORequest,
|
|
||||||
WriteMintPrivateUTXOResponse, WriteSendDeshieldedBalanceRequest,
|
WriteMintPrivateUTXOResponse, WriteSendDeshieldedBalanceRequest,
|
||||||
WriteSendDeshieldedUTXOResponse, WriteSendPrivateUTXORequest, WriteSendPrivateUTXOResponse,
|
WriteSendDeshieldedUTXOResponse, WriteSendPrivateUTXORequest, WriteSendPrivateUTXOResponse,
|
||||||
WriteSendShieldedUTXORequest, WriteSendShieldedUTXOResponse, WriteSendSplitUTXOResponse,
|
WriteSendShieldedUTXORequest, WriteSendShieldedUTXOResponse, WriteSendSplitUTXOResponse,
|
||||||
@ -133,28 +131,6 @@ impl JsonHandler {
|
|||||||
respond(helperstruct)
|
respond(helperstruct)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_request_execute_scenario_multiple_send(
|
|
||||||
&self,
|
|
||||||
request: Request,
|
|
||||||
) -> Result<Value, RpcErr> {
|
|
||||||
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
|
|
||||||
.map_err(cast_common_execution_error_into_rpc_error)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let helperstruct = ExecuteScenarioMultipleSendResponse {
|
|
||||||
scenario_result: SUCCESS.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
respond(helperstruct)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn process_create_account(&self, request: Request) -> Result<Value, RpcErr> {
|
async fn process_create_account(&self, request: Request) -> Result<Value, RpcErr> {
|
||||||
let _req = CreateAccountRequest::parse(Some(request.params))?;
|
let _req = CreateAccountRequest::parse(Some(request.params))?;
|
||||||
|
|
||||||
@ -268,15 +244,8 @@ impl JsonHandler {
|
|||||||
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
||||||
|
|
||||||
let utxo = acc
|
let utxo = acc
|
||||||
.utxo_tree
|
.get_utxo_by_hash(utxo_hash)
|
||||||
.get_item(utxo_hash)
|
.ok_or(RpcError::new_internal_error(None, "UTXO does not exist"))?;
|
||||||
.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 the tree",
|
|
||||||
))?;
|
|
||||||
|
|
||||||
(utxo.asset.clone(), utxo.amount)
|
(utxo.asset.clone(), utxo.amount)
|
||||||
}
|
}
|
||||||
@ -420,49 +389,6 @@ impl JsonHandler {
|
|||||||
respond(helperstruct)
|
respond(helperstruct)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn process_write_mint_utxo_multiple_assets(
|
|
||||||
&self,
|
|
||||||
request: Request,
|
|
||||||
) -> Result<Value, RpcErr> {
|
|
||||||
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
|
|
||||||
.map_err(cast_common_execution_error_into_rpc_error)?
|
|
||||||
};
|
|
||||||
|
|
||||||
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<Value, RpcErr> {
|
pub async fn process_write_send_private_utxo(&self, request: Request) -> Result<Value, RpcErr> {
|
||||||
let req = WriteSendPrivateUTXORequest::parse(Some(request.params))?;
|
let req = WriteSendPrivateUTXORequest::parse(Some(request.params))?;
|
||||||
|
|
||||||
@ -512,11 +438,7 @@ impl JsonHandler {
|
|||||||
.get_mut(&acc_addr_sender)
|
.get_mut(&acc_addr_sender)
|
||||||
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
||||||
|
|
||||||
acc.utxo_tree
|
acc.get_utxo_by_hash(utxo_hash)
|
||||||
.get_item(utxo_hash)
|
|
||||||
.map_err(|err| {
|
|
||||||
RpcError::new_internal_error(None, &format!("DB fetch failure {err:?}"))
|
|
||||||
})?
|
|
||||||
.ok_or(RpcError::new_internal_error(
|
.ok_or(RpcError::new_internal_error(
|
||||||
None,
|
None,
|
||||||
"UTXO does not exist in tree",
|
"UTXO does not exist in tree",
|
||||||
@ -647,11 +569,7 @@ impl JsonHandler {
|
|||||||
.get_mut(&acc_addr_sender)
|
.get_mut(&acc_addr_sender)
|
||||||
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
||||||
|
|
||||||
acc.utxo_tree
|
acc.get_utxo_by_hash(utxo_hash)
|
||||||
.get_item(utxo_hash)
|
|
||||||
.map_err(|err| {
|
|
||||||
RpcError::new_internal_error(None, &format!("DB fetch failure {err:?}"))
|
|
||||||
})?
|
|
||||||
.ok_or(RpcError::new_internal_error(
|
.ok_or(RpcError::new_internal_error(
|
||||||
None,
|
None,
|
||||||
"UTXO does not exist in tree",
|
"UTXO does not exist in tree",
|
||||||
@ -735,11 +653,7 @@ impl JsonHandler {
|
|||||||
.get_mut(&acc_addr_sender)
|
.get_mut(&acc_addr_sender)
|
||||||
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
.ok_or(RpcError::new_internal_error(None, ACCOUNT_NOT_FOUND))?;
|
||||||
|
|
||||||
acc.utxo_tree
|
acc.get_utxo_by_hash(utxo_hash)
|
||||||
.get_item(utxo_hash)
|
|
||||||
.map_err(|err| {
|
|
||||||
RpcError::new_internal_error(None, &format!("DB fetch failure {err:?}"))
|
|
||||||
})?
|
|
||||||
.ok_or(RpcError::new_internal_error(
|
.ok_or(RpcError::new_internal_error(
|
||||||
None,
|
None,
|
||||||
"UTXO does not exist in tree",
|
"UTXO does not exist in tree",
|
||||||
@ -782,10 +696,6 @@ impl JsonHandler {
|
|||||||
GET_BLOCK => self.process_get_block_data(request).await,
|
GET_BLOCK => self.process_get_block_data(request).await,
|
||||||
GET_LAST_BLOCK => self.process_get_last_block(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_SPLIT => self.process_request_execute_scenario_split(request).await,
|
||||||
EXECUTE_SCENARIO_MULTIPLE_SEND => {
|
|
||||||
self.process_request_execute_scenario_multiple_send(request)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
SHOW_ACCOUNT_PUBLIC_BALANCE => self.process_show_account_public_balance(request).await,
|
SHOW_ACCOUNT_PUBLIC_BALANCE => self.process_show_account_public_balance(request).await,
|
||||||
SHOW_ACCOUNT_UTXO => self.process_show_account_utxo_request(request).await,
|
SHOW_ACCOUNT_UTXO => self.process_show_account_utxo_request(request).await,
|
||||||
SHOW_TRANSACTION => self.process_show_transaction(request).await,
|
SHOW_TRANSACTION => self.process_show_transaction(request).await,
|
||||||
@ -793,9 +703,6 @@ impl JsonHandler {
|
|||||||
self.process_write_deposit_public_balance(request).await
|
self.process_write_deposit_public_balance(request).await
|
||||||
}
|
}
|
||||||
WRITE_MINT_UTXO => self.process_write_mint_utxo(request).await,
|
WRITE_MINT_UTXO => self.process_write_mint_utxo(request).await,
|
||||||
WRITE_MINT_UTXO_MULTIPLE_ASSETS => {
|
|
||||||
self.process_write_mint_utxo_multiple_assets(request).await
|
|
||||||
}
|
|
||||||
WRITE_SEND_UTXO_PRIVATE => self.process_write_send_private_utxo(request).await,
|
WRITE_SEND_UTXO_PRIVATE => self.process_write_send_private_utxo(request).await,
|
||||||
WRITE_SEND_UTXO_SHIELDED => self.process_write_send_shielded_utxo(request).await,
|
WRITE_SEND_UTXO_SHIELDED => self.process_write_send_shielded_utxo(request).await,
|
||||||
WRITE_SEND_UTXO_DESHIELDED => self.process_write_send_deshielded_utxo(request).await,
|
WRITE_SEND_UTXO_DESHIELDED => self.process_write_send_deshielded_utxo(request).await,
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
pub mod cryptography;
|
pub mod cryptography;
|
||||||
pub mod proofs_circuits;
|
pub mod proofs_circuits;
|
||||||
pub mod transaction_payloads_tools;
|
pub mod transaction_payloads_tools;
|
||||||
pub mod utxo_manipulator;
|
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
use anyhow::Result;
|
|
||||||
use common::nullifier::UTXONullifier;
|
|
||||||
use utxo::utxo_core::{UTXOPayload, UTXO};
|
|
||||||
|
|
||||||
pub fn utxo_change_owner(
|
|
||||||
utxo: &mut UTXO,
|
|
||||||
nullifier: UTXONullifier,
|
|
||||||
new_owner: [u8; 32],
|
|
||||||
) -> Result<UTXO> {
|
|
||||||
let new_payload = UTXOPayload {
|
|
||||||
owner: new_owner,
|
|
||||||
asset: utxo.asset.clone(),
|
|
||||||
amount: utxo.amount,
|
|
||||||
privacy_flag: utxo.privacy_flag,
|
|
||||||
};
|
|
||||||
|
|
||||||
utxo.consume_utxo(nullifier)?;
|
|
||||||
|
|
||||||
Ok(UTXO::create_utxo_from_payload(new_payload)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn utxo_substact_part_another_owner(
|
|
||||||
utxo: &mut UTXO,
|
|
||||||
nullifier: UTXONullifier,
|
|
||||||
amount: u128,
|
|
||||||
new_owner: [u8; 32],
|
|
||||||
) -> Result<(UTXO, UTXO)> {
|
|
||||||
if amount > utxo.amount {
|
|
||||||
anyhow::bail!("Amount too big");
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff = utxo.amount - amount;
|
|
||||||
|
|
||||||
let new_payload1 = UTXOPayload {
|
|
||||||
owner: utxo.owner,
|
|
||||||
asset: utxo.asset.clone(),
|
|
||||||
amount: diff,
|
|
||||||
privacy_flag: utxo.privacy_flag,
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_payload2 = UTXOPayload {
|
|
||||||
owner: new_owner,
|
|
||||||
asset: utxo.asset.clone(),
|
|
||||||
amount,
|
|
||||||
privacy_flag: utxo.privacy_flag,
|
|
||||||
};
|
|
||||||
|
|
||||||
utxo.consume_utxo(nullifier)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
UTXO::create_utxo_from_payload(new_payload1)?,
|
|
||||||
UTXO::create_utxo_from_payload(new_payload2)?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn utxo_substract_part(
|
|
||||||
utxo: &mut UTXO,
|
|
||||||
nullifier: UTXONullifier,
|
|
||||||
amount: u128,
|
|
||||||
) -> Result<(UTXO, UTXO)> {
|
|
||||||
let new_owner = utxo.owner;
|
|
||||||
|
|
||||||
utxo_substact_part_another_owner(utxo, nullifier, amount, new_owner)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn utxo_split_n_users(
|
|
||||||
utxo: &mut UTXO,
|
|
||||||
nullifier: UTXONullifier,
|
|
||||||
users_amounts: Vec<([u8; 32], u128)>,
|
|
||||||
) -> Result<Vec<UTXO>> {
|
|
||||||
let cumulative_diff = users_amounts
|
|
||||||
.iter()
|
|
||||||
.fold(0, |acc, (_, amount)| acc + *amount);
|
|
||||||
|
|
||||||
if cumulative_diff > utxo.amount {
|
|
||||||
anyhow::bail!("Amount too big");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut utxo_res = vec![];
|
|
||||||
|
|
||||||
for (new_owner, amount) in users_amounts {
|
|
||||||
let new_payload = UTXOPayload {
|
|
||||||
owner: new_owner,
|
|
||||||
asset: utxo.asset.clone(),
|
|
||||||
amount,
|
|
||||||
privacy_flag: utxo.privacy_flag,
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_utxo = UTXO::create_utxo_from_payload(new_payload)?;
|
|
||||||
|
|
||||||
utxo_res.push(new_utxo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if cumulative_diff != utxo.amount {
|
|
||||||
let new_payload = UTXOPayload {
|
|
||||||
owner: utxo.owner,
|
|
||||||
asset: utxo.asset.clone(),
|
|
||||||
amount: utxo.amount - cumulative_diff,
|
|
||||||
privacy_flag: utxo.privacy_flag,
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_utxo = UTXO::create_utxo_from_payload(new_payload)?;
|
|
||||||
|
|
||||||
utxo_res.push(new_utxo);
|
|
||||||
}
|
|
||||||
|
|
||||||
utxo.consume_utxo(nullifier)?;
|
|
||||||
|
|
||||||
Ok(utxo_res)
|
|
||||||
}
|
|
||||||
@ -1,2 +1 @@
|
|||||||
pub mod utxo_core;
|
pub mod utxo_core;
|
||||||
pub mod utxo_tree;
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use common::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier, AccountId};
|
use common::{merkle_tree_public::TreeHashType, AccountId};
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{digest::FixedOutput, Digest};
|
use sha2::{digest::FixedOutput, Digest};
|
||||||
@ -7,12 +7,11 @@ use sha2::{digest::FixedOutput, Digest};
|
|||||||
///Raw asset data
|
///Raw asset data
|
||||||
pub type Asset = Vec<u8>;
|
pub type Asset = Vec<u8>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash)]
|
||||||
///Container for raw utxo payload
|
///Container for raw utxo payload
|
||||||
pub struct UTXO {
|
pub struct UTXO {
|
||||||
pub hash: TreeHashType,
|
pub hash: TreeHashType,
|
||||||
pub owner: AccountId,
|
pub owner: AccountId,
|
||||||
pub nullifier: Option<UTXONullifier>,
|
|
||||||
pub asset: Asset,
|
pub asset: Asset,
|
||||||
// TODO: change to u256
|
// TODO: change to u256
|
||||||
pub amount: u128,
|
pub amount: u128,
|
||||||
@ -41,23 +40,12 @@ impl UTXO {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
hash,
|
hash,
|
||||||
owner: payload_with_asset.owner,
|
owner: payload_with_asset.owner,
|
||||||
nullifier: None,
|
|
||||||
asset: payload_with_asset.asset,
|
asset: payload_with_asset.asset,
|
||||||
amount: payload_with_asset.amount,
|
amount: payload_with_asset.amount,
|
||||||
privacy_flag: payload_with_asset.privacy_flag,
|
privacy_flag: payload_with_asset.privacy_flag,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume_utxo(&mut self, nullifier: UTXONullifier) -> Result<()> {
|
|
||||||
if self.nullifier.is_some() {
|
|
||||||
anyhow::bail!("UTXO already consumed");
|
|
||||||
} else {
|
|
||||||
self.nullifier = Some(nullifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn interpret_asset<'de, ToInterpret: Deserialize<'de>>(&'de self) -> Result<ToInterpret> {
|
pub fn interpret_asset<'de, ToInterpret: Deserialize<'de>>(&'de self) -> Result<ToInterpret> {
|
||||||
Ok(serde_json::from_slice(&self.asset)?)
|
Ok(serde_json::from_slice(&self.asset)?)
|
||||||
}
|
}
|
||||||
@ -74,11 +62,6 @@ impl UTXO {
|
|||||||
pub fn log(&self) {
|
pub fn log(&self) {
|
||||||
info!("UTXO hash is {:?}", hex::encode(self.hash));
|
info!("UTXO hash is {:?}", hex::encode(self.hash));
|
||||||
info!("UTXO owner is {:?}", hex::encode(self.owner));
|
info!("UTXO owner is {:?}", hex::encode(self.owner));
|
||||||
info!(
|
|
||||||
"UTXO nullifier is {:?}",
|
|
||||||
self.nullifier.clone().map(|val| hex::encode(val.utxo_hash))
|
|
||||||
);
|
|
||||||
info!("UTXO asset is {:?}", hex::encode(self.asset.clone()));
|
|
||||||
info!("UTXO amount is {:?}", self.amount);
|
info!("UTXO amount is {:?}", self.amount);
|
||||||
info!("UTXO privacy_flag is {:?}", self.privacy_flag);
|
info!("UTXO privacy_flag is {:?}", self.privacy_flag);
|
||||||
}
|
}
|
||||||
@ -98,14 +81,6 @@ mod tests {
|
|||||||
AccountId::default()
|
AccountId::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_nullifier() -> UTXONullifier {
|
|
||||||
UTXONullifier::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_tree_hash() -> TreeHashType {
|
|
||||||
TreeHashType::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_payload() -> UTXOPayload {
|
fn sample_payload() -> UTXOPayload {
|
||||||
UTXOPayload {
|
UTXOPayload {
|
||||||
owner: sample_account(),
|
owner: sample_account(),
|
||||||
@ -127,23 +102,6 @@ mod tests {
|
|||||||
// Ensure hash is created and the UTXO fields are correctly assigned
|
// Ensure hash is created and the UTXO fields are correctly assigned
|
||||||
assert_eq!(utxo.owner, payload.owner);
|
assert_eq!(utxo.owner, payload.owner);
|
||||||
assert_eq!(utxo.asset, payload.asset);
|
assert_eq!(utxo.asset, payload.asset);
|
||||||
assert!(utxo.nullifier.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_consume_utxo() {
|
|
||||||
let payload = sample_payload();
|
|
||||||
let mut utxo = UTXO::create_utxo_from_payload(payload).unwrap();
|
|
||||||
|
|
||||||
let nullifier = sample_nullifier();
|
|
||||||
|
|
||||||
// First consumption should succeed
|
|
||||||
assert!(utxo.consume_utxo(nullifier.clone()).is_ok());
|
|
||||||
assert_eq!(utxo.nullifier, Some(nullifier));
|
|
||||||
|
|
||||||
// Second consumption should fail
|
|
||||||
let result = utxo.consume_utxo(sample_nullifier());
|
|
||||||
assert!(result.is_err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -1,192 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use common::merkle_tree_public::TreeHashType;
|
|
||||||
use monotree::database::MemoryDB;
|
|
||||||
use monotree::hasher::Blake3;
|
|
||||||
use monotree::{Hasher, Monotree, Proof};
|
|
||||||
|
|
||||||
use crate::utxo_core::UTXO;
|
|
||||||
|
|
||||||
pub struct UTXOSparseMerkleTree {
|
|
||||||
pub curr_root: Option<TreeHashType>,
|
|
||||||
pub tree: Monotree<MemoryDB, Blake3>,
|
|
||||||
pub hasher: Blake3,
|
|
||||||
pub store: HashMap<TreeHashType, UTXO>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UTXOSparseMerkleTree {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
UTXOSparseMerkleTree {
|
|
||||||
curr_root: None,
|
|
||||||
tree: Monotree::default(),
|
|
||||||
hasher: Blake3::new(),
|
|
||||||
store: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_item(&mut self, utxo: UTXO) -> Result<(), monotree::Errors> {
|
|
||||||
let root = self.curr_root.as_ref();
|
|
||||||
|
|
||||||
let new_root = self.tree.insert(root, &utxo.hash, &utxo.hash)?;
|
|
||||||
|
|
||||||
self.store.insert(utxo.hash, utxo);
|
|
||||||
|
|
||||||
self.curr_root = new_root;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_items(&mut self, utxos: Vec<UTXO>) -> Result<(), monotree::Errors> {
|
|
||||||
let root = self.curr_root.as_ref();
|
|
||||||
|
|
||||||
let hashes: Vec<TreeHashType> = utxos.iter().map(|item| item.hash).collect();
|
|
||||||
|
|
||||||
let new_root = self.tree.inserts(root, &hashes, &hashes)?;
|
|
||||||
|
|
||||||
for utxo in utxos {
|
|
||||||
self.store.insert(utxo.hash, utxo);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.curr_root = new_root;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_item(&mut self, hash: TreeHashType) -> Result<Option<&UTXO>, monotree::Errors> {
|
|
||||||
let hash = self.tree.get(self.curr_root.as_ref(), &hash)?;
|
|
||||||
|
|
||||||
Ok(hash.and_then(|hash| self.store.get(&hash)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_membership_proof(
|
|
||||||
&mut self,
|
|
||||||
nullifier_hash: TreeHashType,
|
|
||||||
) -> Result<Option<Proof>, monotree::Errors> {
|
|
||||||
self.tree
|
|
||||||
.get_merkle_proof(self.curr_root.as_ref(), &nullifier_hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for UTXOSparseMerkleTree {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use common::AccountId;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::utxo_core::{UTXOPayload, UTXO};
|
|
||||||
|
|
||||||
fn sample_utxo_payload() -> UTXOPayload {
|
|
||||||
UTXOPayload {
|
|
||||||
owner: AccountId::default(),
|
|
||||||
asset: vec![1, 2, 3],
|
|
||||||
amount: 10,
|
|
||||||
privacy_flag: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sample_utxo() -> anyhow::Result<UTXO> {
|
|
||||||
UTXO::create_utxo_from_payload(sample_utxo_payload())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_utxo_sparse_merkle_tree_new() {
|
|
||||||
let smt = UTXOSparseMerkleTree::new();
|
|
||||||
assert!(smt.curr_root.is_none());
|
|
||||||
assert_eq!(smt.store.len(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_insert_item() {
|
|
||||||
let mut smt = UTXOSparseMerkleTree::new();
|
|
||||||
let utxo = sample_utxo().unwrap();
|
|
||||||
|
|
||||||
let result = smt.insert_item(utxo.clone());
|
|
||||||
|
|
||||||
// Test insertion is successful
|
|
||||||
assert!(result.is_ok());
|
|
||||||
|
|
||||||
// Test UTXO is now stored in the tree
|
|
||||||
assert_eq!(smt.store.get(&utxo.hash).unwrap().hash, utxo.hash);
|
|
||||||
|
|
||||||
// Test curr_root is updated
|
|
||||||
assert!(smt.curr_root.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_insert_items() {
|
|
||||||
let mut smt = UTXOSparseMerkleTree::new();
|
|
||||||
let utxo1 = sample_utxo().unwrap();
|
|
||||||
let utxo2 = sample_utxo().unwrap();
|
|
||||||
|
|
||||||
let result = smt.insert_items(vec![utxo1.clone(), utxo2.clone()]);
|
|
||||||
|
|
||||||
// Test insertion of multiple items is successful
|
|
||||||
assert!(result.is_ok());
|
|
||||||
|
|
||||||
// Test UTXOs are now stored in the tree
|
|
||||||
assert!(smt.store.get(&utxo1.hash).is_some());
|
|
||||||
assert!(smt.store.get(&utxo2.hash).is_some());
|
|
||||||
|
|
||||||
// Test curr_root is updated
|
|
||||||
assert!(smt.curr_root.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_item_exists() {
|
|
||||||
let mut smt = UTXOSparseMerkleTree::new();
|
|
||||||
let utxo = sample_utxo().unwrap();
|
|
||||||
|
|
||||||
smt.insert_item(utxo.clone()).unwrap();
|
|
||||||
|
|
||||||
// Test that the UTXO can be retrieved by hash
|
|
||||||
let retrieved_utxo = smt.get_item(utxo.hash).unwrap();
|
|
||||||
assert!(retrieved_utxo.is_some());
|
|
||||||
assert_eq!(retrieved_utxo.unwrap().hash, utxo.hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_item_not_exists() {
|
|
||||||
let mut smt = UTXOSparseMerkleTree::new();
|
|
||||||
let utxo = sample_utxo().unwrap();
|
|
||||||
|
|
||||||
// Insert one UTXO and try to fetch a different hash
|
|
||||||
smt.insert_item(utxo).unwrap();
|
|
||||||
|
|
||||||
let non_existent_hash = TreeHashType::default();
|
|
||||||
let result = smt.get_item(non_existent_hash).unwrap();
|
|
||||||
|
|
||||||
// Test that retrieval for a non-existent UTXO returns None
|
|
||||||
assert!(result.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_membership_proof() {
|
|
||||||
let mut smt = UTXOSparseMerkleTree::new();
|
|
||||||
let utxo = sample_utxo().unwrap();
|
|
||||||
|
|
||||||
smt.insert_item(utxo.clone()).unwrap();
|
|
||||||
|
|
||||||
// Fetch membership proof for the inserted UTXO
|
|
||||||
let proof = smt.get_membership_proof(utxo.hash).unwrap();
|
|
||||||
|
|
||||||
// Test proof is generated successfully
|
|
||||||
assert!(proof.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get_membership_proof_not_exists() {
|
|
||||||
let mut smt = UTXOSparseMerkleTree::new();
|
|
||||||
|
|
||||||
// Try fetching proof for a non-existent UTXO hash
|
|
||||||
let non_existent_hash = TreeHashType::default();
|
|
||||||
let proof = smt.get_membership_proof(non_existent_hash).unwrap();
|
|
||||||
|
|
||||||
// Test no proof is generated for a non-existent UTXO
|
|
||||||
assert!(proof.is_none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user