diff --git a/Cargo.lock b/Cargo.lock index 21e1b75..9fcb1bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2030,8 +2030,11 @@ dependencies = [ "anyhow", "env_logger", "log", + "monotree", "serde", "serde_json", + "sha2 0.10.8", + "storage", ] [[package]] diff --git a/storage/src/lib.rs b/storage/src/lib.rs index efb34cd..684dcc8 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -3,6 +3,7 @@ use std::{path::Path, sync::Arc}; use block::Block; use error::DbError; use log::warn; +use merkle_tree_public::TreeHashType; use rocksdb::{ BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, }; @@ -15,6 +16,9 @@ pub mod nullifier_sparse_merkle_tree; pub mod transaction; pub mod utxo_commitment; +///Account id on blockchain +pub type AccountId = TreeHashType; + ///Maximal size of stored blocks in base /// ///Used to control db size diff --git a/utxo/Cargo.toml b/utxo/Cargo.toml index 7bc3dc0..3a2f1b9 100644 --- a/utxo/Cargo.toml +++ b/utxo/Cargo.toml @@ -8,4 +8,9 @@ anyhow.workspace = true serde_json.workspace = true env_logger.workspace = true log.workspace = true -serde.workspace = true \ No newline at end of file +serde.workspace = true +monotree.workspace = true +sha2.workspace = true + +[dependencies.storage] +path = "../storage" \ No newline at end of file diff --git a/utxo/src/lib.rs b/utxo/src/lib.rs index 08193b8..1b7e30c 100644 --- a/utxo/src/lib.rs +++ b/utxo/src/lib.rs @@ -1 +1,2 @@ -//ToDo: Add utxo module +pub mod utxo_core; +pub mod utxo_tree; diff --git a/utxo/src/utxo_core.rs b/utxo/src/utxo_core.rs new file mode 100644 index 0000000..d076598 --- /dev/null +++ b/utxo/src/utxo_core.rs @@ -0,0 +1,55 @@ +use anyhow::Result; +use serde::{Deserialize, Serialize}; +use sha2::{digest::FixedOutput, Digest}; +use storage::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier, AccountId}; + +///Raw asset data +pub type Asset = Vec; + +#[derive(Debug)] +///Container for raw utxo payload +pub struct UTXO { + pub hash: TreeHashType, + pub owner: AccountId, + pub nullifier: Option, + pub asset: Asset, +} + +#[derive(Debug, Clone, Serialize)] +pub struct UTXOPayload { + pub owner: AccountId, + pub asset: Asset, +} + +impl UTXO { + pub fn create_utxo_from_payload(payload_with_asset: UTXOPayload) -> Self { + let raw_payload = serde_json::to_vec(&payload_with_asset).unwrap(); + + let mut hasher = sha2::Sha256::new(); + + hasher.update(&raw_payload); + + let hash = ::from(hasher.finalize_fixed()); + + Self { + hash, + owner: payload_with_asset.owner, + nullifier: None, + asset: payload_with_asset.asset, + } + } + + 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 { + Ok(serde_json::from_slice(&self.asset)?) + } +} diff --git a/utxo/src/utxo_tree.rs b/utxo/src/utxo_tree.rs new file mode 100644 index 0000000..147c458 --- /dev/null +++ b/utxo/src/utxo_tree.rs @@ -0,0 +1,74 @@ +use std::collections::HashMap; + +use monotree::database::MemoryDB; +use monotree::hasher::Blake3; +use monotree::{Hasher, Monotree, Proof}; +use storage::merkle_tree_public::TreeHashType; + +use crate::utxo_core::UTXO; + +pub struct UTXOSparseMerkleTree { + pub curr_root: Option, + pub tree: Monotree, + pub hasher: Blake3, + pub store: HashMap, +} + +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) -> Result<(), monotree::Errors> { + let root = self.curr_root.as_ref(); + + let hashes: Vec = 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, 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, monotree::Errors> { + self.tree + .get_merkle_proof(self.curr_root.as_ref(), &nullifier_hash) + } +} + +impl Default for UTXOSparseMerkleTree { + fn default() -> Self { + Self::new() + } +}