2025-09-25 11:53:42 +03:00
|
|
|
use borsh::{BorshDeserialize, BorshSerialize};
|
2025-09-08 09:09:32 +03:00
|
|
|
use k256::ecdsa::{Signature, SigningKey, VerifyingKey};
|
2024-12-30 07:35:05 +01:00
|
|
|
use log::info;
|
2025-07-21 18:46:50 -03:00
|
|
|
use serde::{Deserialize, Serialize};
|
2025-07-15 15:48:59 -03:00
|
|
|
|
2025-09-04 14:38:41 +03:00
|
|
|
use sha2::{Digest, digest::FixedOutput};
|
2024-10-10 14:09:31 +03:00
|
|
|
|
2024-12-22 16:14:52 +02:00
|
|
|
use elliptic_curve::{
|
|
|
|
|
consts::{B0, B1},
|
|
|
|
|
generic_array::GenericArray,
|
|
|
|
|
};
|
|
|
|
|
use sha2::digest::typenum::{UInt, UTerm};
|
|
|
|
|
|
2025-09-08 10:11:04 +03:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub enum NSSATransaction {
|
|
|
|
|
Public(nssa::PublicTransaction),
|
|
|
|
|
PrivacyPreserving(nssa::PrivacyPreservingTransaction),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<nssa::PublicTransaction> for NSSATransaction {
|
|
|
|
|
fn from(value: nssa::PublicTransaction) -> Self {
|
|
|
|
|
Self::Public(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<nssa::PrivacyPreservingTransaction> for NSSATransaction {
|
|
|
|
|
fn from(value: nssa::PrivacyPreservingTransaction) -> Self {
|
|
|
|
|
Self::PrivacyPreserving(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-03 10:29:51 +03:00
|
|
|
use crate::TreeHashType;
|
2025-07-14 09:37:00 -03:00
|
|
|
|
2024-12-22 16:14:52 +02:00
|
|
|
pub type CipherText = Vec<u8>;
|
|
|
|
|
pub type Nonce = GenericArray<u8, UInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>, B0>>;
|
2025-03-28 17:12:28 +02:00
|
|
|
pub type Tag = u8;
|
2024-12-22 16:14:52 +02:00
|
|
|
|
2025-09-25 11:53:42 +03:00
|
|
|
#[derive(
|
|
|
|
|
Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize,
|
|
|
|
|
)]
|
2024-11-28 22:05:14 +02:00
|
|
|
pub enum TxKind {
|
|
|
|
|
Public,
|
2025-08-28 12:00:04 +03:00
|
|
|
PrivacyPreserving,
|
2024-11-28 22:05:14 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-25 11:53:42 +03:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
2024-10-10 14:09:31 +03:00
|
|
|
///General transaction object
|
2025-09-08 10:11:04 +03:00
|
|
|
pub struct EncodedTransaction {
|
2024-11-28 22:05:14 +02:00
|
|
|
pub tx_kind: TxKind,
|
2024-12-22 16:14:52 +02:00
|
|
|
///Encoded blobs of data
|
2025-08-28 12:00:04 +03:00
|
|
|
pub encoded_transaction_data: Vec<u8>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 10:11:04 +03:00
|
|
|
impl From<NSSATransaction> for EncodedTransaction {
|
|
|
|
|
fn from(value: NSSATransaction) -> Self {
|
2025-08-28 12:00:04 +03:00
|
|
|
match value {
|
2025-09-08 10:11:04 +03:00
|
|
|
NSSATransaction::Public(tx) => Self {
|
2025-09-03 10:29:51 +03:00
|
|
|
tx_kind: TxKind::Public,
|
|
|
|
|
encoded_transaction_data: tx.to_bytes(),
|
|
|
|
|
},
|
2025-09-08 10:11:04 +03:00
|
|
|
NSSATransaction::PrivacyPreserving(tx) => Self {
|
2025-09-03 10:29:51 +03:00
|
|
|
tx_kind: TxKind::PrivacyPreserving,
|
|
|
|
|
encoded_transaction_data: tx.to_bytes(),
|
2025-08-28 12:00:04 +03:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 10:11:04 +03:00
|
|
|
impl TryFrom<&EncodedTransaction> for NSSATransaction {
|
2025-08-28 12:00:04 +03:00
|
|
|
type Error = nssa::error::NssaError;
|
|
|
|
|
|
2025-09-08 10:11:04 +03:00
|
|
|
fn try_from(value: &EncodedTransaction) -> Result<Self, Self::Error> {
|
2025-08-28 12:00:04 +03:00
|
|
|
match value.tx_kind {
|
2025-09-03 10:29:51 +03:00
|
|
|
TxKind::Public => nssa::PublicTransaction::from_bytes(&value.encoded_transaction_data)
|
|
|
|
|
.map(|tx| tx.into()),
|
2025-08-28 12:00:04 +03:00
|
|
|
TxKind::PrivacyPreserving => {
|
2025-09-03 10:29:51 +03:00
|
|
|
nssa::PrivacyPreservingTransaction::from_bytes(&value.encoded_transaction_data)
|
|
|
|
|
.map(|tx| tx.into())
|
|
|
|
|
}
|
2025-08-28 12:00:04 +03:00
|
|
|
}
|
|
|
|
|
}
|
2024-12-22 16:14:52 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-03 12:43:05 +02:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
|
pub struct MintMoneyPublicTx {
|
|
|
|
|
pub acc: [u8; 32],
|
|
|
|
|
pub amount: u128,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
|
pub struct SendMoneyShieldedTx {
|
|
|
|
|
pub acc_sender: [u8; 32],
|
|
|
|
|
pub amount: u128,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
|
pub struct SendMoneyDeshieldedTx {
|
|
|
|
|
pub receiver_data: Vec<(u128, [u8; 32])>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
|
pub struct OwnedUTXO {
|
|
|
|
|
pub hash: [u8; 32],
|
|
|
|
|
pub owner: [u8; 32],
|
|
|
|
|
pub amount: u128,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
|
pub struct OwnedUTXOForPublication {
|
|
|
|
|
pub hash: String,
|
|
|
|
|
pub owner: String,
|
|
|
|
|
pub amount: u128,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<OwnedUTXO> for OwnedUTXOForPublication {
|
|
|
|
|
fn from(value: OwnedUTXO) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
hash: hex::encode(value.hash),
|
|
|
|
|
owner: hex::encode(value.owner),
|
|
|
|
|
amount: value.amount,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
|
pub struct UTXOPublication {
|
|
|
|
|
pub utxos: Vec<OwnedUTXO>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
|
pub enum ActionData {
|
|
|
|
|
MintMoneyPublicTx(MintMoneyPublicTx),
|
|
|
|
|
SendMoneyShieldedTx(SendMoneyShieldedTx),
|
|
|
|
|
SendMoneyDeshieldedTx(SendMoneyDeshieldedTx),
|
|
|
|
|
UTXOPublication(UTXOPublication),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ActionData {
|
|
|
|
|
pub fn into_hexed_print(self) -> String {
|
|
|
|
|
match self {
|
|
|
|
|
ActionData::MintMoneyPublicTx(action) => {
|
|
|
|
|
format!(
|
|
|
|
|
"Account {:?} minted {:?} balance",
|
|
|
|
|
hex::encode(action.acc),
|
|
|
|
|
action.amount
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ActionData::SendMoneyDeshieldedTx(action) => {
|
|
|
|
|
format!(
|
|
|
|
|
"Receivers receipt {:?}",
|
|
|
|
|
action
|
|
|
|
|
.receiver_data
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|(amount, rec)| (amount, hex::encode(rec)))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ActionData::SendMoneyShieldedTx(action) => {
|
|
|
|
|
format!(
|
|
|
|
|
"Shielded send from {:?} for {:?} balance",
|
|
|
|
|
hex::encode(action.acc_sender),
|
|
|
|
|
action.amount
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ActionData::UTXOPublication(action) => {
|
|
|
|
|
let pub_own_utxo: Vec<OwnedUTXOForPublication> = action
|
|
|
|
|
.utxos
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|owned_utxo| owned_utxo.into())
|
|
|
|
|
.collect();
|
2025-07-21 18:46:50 -03:00
|
|
|
format!("Published utxos {pub_own_utxo:?}")
|
2025-01-03 12:43:05 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 10:11:04 +03:00
|
|
|
impl EncodedTransaction {
|
2025-07-10 12:52:48 -03:00
|
|
|
/// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`.
|
2025-07-10 12:15:37 -03:00
|
|
|
pub fn hash(&self) -> TreeHashType {
|
2025-09-25 11:53:42 +03:00
|
|
|
let bytes_to_hash = borsh::to_vec(&self).unwrap();
|
2025-07-21 21:07:16 -03:00
|
|
|
let mut hasher = sha2::Sha256::new();
|
|
|
|
|
hasher.update(&bytes_to_hash);
|
|
|
|
|
TreeHashType::from(hasher.finalize_fixed())
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 07:35:05 +01:00
|
|
|
pub fn log(&self) {
|
2025-07-10 11:35:46 -03:00
|
|
|
info!("Transaction hash is {:?}", hex::encode(self.hash()));
|
2024-12-30 07:35:05 +01:00
|
|
|
info!("Transaction tx_kind is {:?}", self.tx_kind);
|
|
|
|
|
}
|
2024-12-30 09:10:04 +02:00
|
|
|
}
|
2025-07-10 12:09:01 -03:00
|
|
|
|
2025-07-15 15:48:59 -03:00
|
|
|
pub type TransactionSignature = Signature;
|
2025-07-16 10:04:23 -03:00
|
|
|
pub type SignaturePublicKey = VerifyingKey;
|
|
|
|
|
pub type SignaturePrivateKey = SigningKey;
|
2025-07-14 09:37:00 -03:00
|
|
|
|
2025-07-10 12:09:01 -03:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2025-09-04 14:38:41 +03:00
|
|
|
use sha2::{Digest, digest::FixedOutput};
|
2025-07-10 12:09:01 -03:00
|
|
|
|
2025-07-10 12:16:05 -03:00
|
|
|
use crate::{
|
2025-09-03 10:29:51 +03:00
|
|
|
TreeHashType,
|
2025-09-08 10:11:04 +03:00
|
|
|
transaction::{EncodedTransaction, TxKind},
|
2025-07-10 12:16:05 -03:00
|
|
|
};
|
2025-07-10 12:09:01 -03:00
|
|
|
|
2025-09-08 10:11:04 +03:00
|
|
|
fn test_transaction_body() -> EncodedTransaction {
|
|
|
|
|
EncodedTransaction {
|
2025-07-10 12:09:01 -03:00
|
|
|
tx_kind: TxKind::Public,
|
2025-09-03 10:29:51 +03:00
|
|
|
encoded_transaction_data: vec![1, 2, 3, 4],
|
2025-07-16 11:36:20 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_transaction_hash_is_sha256_of_json_bytes() {
|
|
|
|
|
let body = test_transaction_body();
|
2025-07-10 12:09:01 -03:00
|
|
|
let expected_hash = {
|
2025-09-25 11:53:42 +03:00
|
|
|
let data = borsh::to_vec(&body).unwrap();
|
2025-07-10 12:09:01 -03:00
|
|
|
let mut hasher = sha2::Sha256::new();
|
|
|
|
|
hasher.update(&data);
|
|
|
|
|
TreeHashType::from(hasher.finalize_fixed())
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-16 11:36:20 -03:00
|
|
|
let hash = body.hash();
|
2025-07-10 12:09:01 -03:00
|
|
|
|
|
|
|
|
assert_eq!(expected_hash, hash);
|
|
|
|
|
}
|
2025-07-16 11:36:20 -03:00
|
|
|
|
|
|
|
|
#[test]
|
2025-09-03 10:29:51 +03:00
|
|
|
fn test_to_bytes_from_bytes() {
|
2025-07-16 11:36:20 -03:00
|
|
|
let body = test_transaction_body();
|
|
|
|
|
|
2025-09-25 11:53:42 +03:00
|
|
|
let body_bytes = borsh::to_vec(&body).unwrap();
|
|
|
|
|
let body_new = borsh::from_slice::<EncodedTransaction>(&body_bytes).unwrap();
|
2025-07-16 11:54:11 -03:00
|
|
|
|
2025-09-03 10:29:51 +03:00
|
|
|
assert_eq!(body, body_new);
|
2025-07-29 12:50:30 -03:00
|
|
|
}
|
2025-07-10 12:16:05 -03:00
|
|
|
}
|