use borsh::{BorshDeserialize, BorshSerialize}; use log::info; use serde::{Deserialize, Serialize}; use sha2::{Digest, digest::FixedOutput}; pub type TreeHashType = [u8; 32]; #[derive(Debug, Clone, PartialEq, Eq)] pub enum NSSATransaction { Public(nssa::PublicTransaction), PrivacyPreserving(nssa::PrivacyPreservingTransaction), } impl From for NSSATransaction { fn from(value: nssa::PublicTransaction) -> Self { Self::Public(value) } } impl From for NSSATransaction { fn from(value: nssa::PrivacyPreservingTransaction) -> Self { Self::PrivacyPreserving(value) } } #[derive( Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize, )] pub enum TxKind { Public, PrivacyPreserving, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] ///General transaction object pub struct EncodedTransaction { pub tx_kind: TxKind, ///Encoded blobs of data pub encoded_transaction_data: Vec, } impl From for EncodedTransaction { fn from(value: NSSATransaction) -> Self { match value { NSSATransaction::Public(tx) => Self { tx_kind: TxKind::Public, encoded_transaction_data: tx.to_bytes(), }, NSSATransaction::PrivacyPreserving(tx) => Self { tx_kind: TxKind::PrivacyPreserving, encoded_transaction_data: tx.to_bytes(), }, } } } impl TryFrom<&EncodedTransaction> for NSSATransaction { type Error = nssa::error::NssaError; fn try_from(value: &EncodedTransaction) -> Result { match value.tx_kind { TxKind::Public => nssa::PublicTransaction::from_bytes(&value.encoded_transaction_data) .map(|tx| tx.into()), TxKind::PrivacyPreserving => { nssa::PrivacyPreservingTransaction::from_bytes(&value.encoded_transaction_data) .map(|tx| tx.into()) } } } } impl EncodedTransaction { /// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`. pub fn hash(&self) -> TreeHashType { let bytes_to_hash = borsh::to_vec(&self).unwrap(); let mut hasher = sha2::Sha256::new(); hasher.update(&bytes_to_hash); TreeHashType::from(hasher.finalize_fixed()) } pub fn log(&self) { info!("Transaction hash is {:?}", hex::encode(self.hash())); info!("Transaction tx_kind is {:?}", self.tx_kind); } } #[cfg(test)] mod tests { use sha2::{Digest, digest::FixedOutput}; use crate::{ TreeHashType, transaction::{EncodedTransaction, TxKind}, }; fn test_transaction_body() -> EncodedTransaction { EncodedTransaction { tx_kind: TxKind::Public, encoded_transaction_data: vec![1, 2, 3, 4], } } #[test] fn test_transaction_hash_is_sha256_of_json_bytes() { let body = test_transaction_body(); let expected_hash = { let data = borsh::to_vec(&body).unwrap(); let mut hasher = sha2::Sha256::new(); hasher.update(&data); TreeHashType::from(hasher.finalize_fixed()) }; let hash = body.hash(); assert_eq!(expected_hash, hash); } #[test] fn test_to_bytes_from_bytes() { let body = test_transaction_body(); let body_bytes = borsh::to_vec(&body).unwrap(); let body_new = borsh::from_slice::(&body_bytes).unwrap(); assert_eq!(body, body_new); } }