lssa/utxo/src/utxo_core.rs

177 lines
4.9 KiB
Rust
Raw Normal View History

2024-10-22 12:46:44 +03:00
use anyhow::Result;
2025-04-16 16:17:53 +03:00
use common::{merkle_tree_public::TreeHashType, nullifier::UTXONullifier, AccountId};
use log::info;
2024-10-22 12:46:44 +03:00
use serde::{Deserialize, Serialize};
use sha2::{digest::FixedOutput, Digest};
///Raw asset data
pub type Asset = Vec<u8>;
2024-12-02 00:55:11 +01:00
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
2024-10-22 12:46:44 +03:00
///Container for raw utxo payload
pub struct UTXO {
pub hash: TreeHashType,
pub owner: AccountId,
pub nullifier: Option<UTXONullifier>,
pub asset: Asset,
2024-12-02 00:55:11 +01:00
// TODO: change to u256
pub amount: u128,
pub privacy_flag: bool,
2024-10-22 12:46:44 +03:00
}
2024-12-20 11:02:12 +02:00
#[derive(Debug, Clone, Serialize, Deserialize)]
2024-10-22 12:46:44 +03:00
pub struct UTXOPayload {
pub owner: AccountId,
pub asset: Asset,
2024-12-02 00:55:11 +01:00
// TODO: change to u256
pub amount: u128,
pub privacy_flag: bool,
2024-10-22 12:46:44 +03:00
}
impl UTXO {
2025-02-07 15:17:03 -05:00
pub fn create_utxo_from_payload(payload_with_asset: UTXOPayload) -> anyhow::Result<Self> {
let raw_payload = serde_json::to_vec(&payload_with_asset)?;
2024-10-22 12:46:44 +03:00
let mut hasher = sha2::Sha256::new();
hasher.update(&raw_payload);
let hash = <TreeHashType>::from(hasher.finalize_fixed());
2025-02-07 15:17:03 -05:00
Ok(Self {
2024-10-22 12:46:44 +03:00
hash,
owner: payload_with_asset.owner,
nullifier: None,
asset: payload_with_asset.asset,
2024-12-02 00:55:11 +01:00
amount: payload_with_asset.amount,
privacy_flag: payload_with_asset.privacy_flag,
2025-02-07 15:17:03 -05:00
})
2024-10-22 12:46:44 +03:00
}
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> {
Ok(serde_json::from_slice(&self.asset)?)
}
2024-12-30 07:43:27 +02:00
pub fn into_payload(&self) -> UTXOPayload {
2024-12-30 09:10:04 +02:00
UTXOPayload {
owner: self.owner,
asset: self.asset.clone(),
amount: self.amount,
privacy_flag: self.privacy_flag,
2024-12-30 07:43:27 +02:00
}
}
pub fn log(&self) {
info!("UTXO hash is {:?}", hex::encode(self.hash));
2025-01-03 08:13:59 +02:00
info!("UTXO owner is {:?}", hex::encode(self.owner));
2024-12-30 09:10:04 +02:00
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 privacy_flag is {:?}", self.privacy_flag);
}
2024-10-22 12:46:44 +03:00
}
2024-10-25 00:36:10 +02:00
#[cfg(test)]
mod tests {
use super::*;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestAsset {
id: u32,
name: String,
}
fn sample_account() -> AccountId {
AccountId::default()
}
fn sample_nullifier() -> UTXONullifier {
UTXONullifier::default()
}
fn sample_tree_hash() -> TreeHashType {
TreeHashType::default()
}
fn sample_payload() -> UTXOPayload {
UTXOPayload {
owner: sample_account(),
2024-10-25 00:53:06 +02:00
asset: serde_json::to_vec(&TestAsset {
id: 1,
name: "Test".to_string(),
})
.unwrap(),
2024-12-02 00:55:11 +01:00
amount: 10,
privacy_flag: false,
2024-10-25 00:36:10 +02:00
}
}
2024-10-25 00:36:36 +02:00
#[test]
fn test_create_utxo_from_payload() {
let payload = sample_payload();
2025-02-07 15:17:03 -05:00
let utxo = UTXO::create_utxo_from_payload(payload.clone()).unwrap();
2024-10-25 00:36:36 +02:00
// Ensure hash is created and the UTXO fields are correctly assigned
assert_eq!(utxo.owner, payload.owner);
assert_eq!(utxo.asset, payload.asset);
assert!(utxo.nullifier.is_none());
}
2024-10-25 00:36:49 +02:00
#[test]
fn test_consume_utxo() {
let payload = sample_payload();
2025-02-07 15:17:03 -05:00
let mut utxo = UTXO::create_utxo_from_payload(payload).unwrap();
2024-10-25 00:36:49 +02:00
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());
}
2024-10-25 00:36:10 +02:00
2024-10-25 00:37:02 +02:00
#[test]
fn test_interpret_asset() {
let payload = sample_payload();
2025-02-07 15:17:03 -05:00
let utxo = UTXO::create_utxo_from_payload(payload).unwrap();
2024-10-25 00:37:02 +02:00
// Interpret asset as TestAsset
let interpreted: TestAsset = utxo.interpret_asset().unwrap();
2024-10-25 00:53:06 +02:00
assert_eq!(
interpreted,
TestAsset {
id: 1,
name: "Test".to_string()
}
);
2024-10-25 00:37:02 +02:00
}
2024-10-25 00:37:11 +02:00
#[test]
fn test_interpret_invalid_asset() {
let mut payload = sample_payload();
payload.asset = vec![0, 1, 2, 3]; // Invalid data for deserialization
2025-02-07 15:17:03 -05:00
let utxo = UTXO::create_utxo_from_payload(payload).unwrap();
2024-10-25 00:37:02 +02:00
2024-10-25 00:37:11 +02:00
// This should fail because the asset is not valid JSON for TestAsset
let result: Result<TestAsset> = utxo.interpret_asset();
assert!(result.is_err());
}
2024-10-25 00:36:10 +02:00
}