290 lines
8.6 KiB
Rust
Raw Normal View History

2025-07-21 18:46:50 -03:00
use k256::ecdsa::{
2025-07-21 19:04:49 -03:00
Signature, SigningKey, VerifyingKey,
2025-09-04 14:38:41 +03:00
signature::{Signer, Verifier},
2025-07-21 19:04:49 -03:00
};
use log::info;
2025-07-21 18:46:50 -03:00
use serde::{Deserialize, Serialize};
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-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>>;
pub type Tag = u8;
2024-12-22 16:14:52 +02:00
2025-07-16 11:36:20 -03:00
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
pub enum TxKind {
Public,
2025-08-28 12:00:04 +03:00
PrivacyPreserving,
}
2025-07-16 11:36:20 -03:00
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2024-10-10 14:09:31 +03:00
///General transaction object
pub struct TransactionBody {
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>,
}
impl From<nssa::NSSATransaction> for TransactionBody {
fn from(value: nssa::NSSATransaction) -> Self {
match value {
2025-09-03 10:29:51 +03:00
nssa::NSSATransaction::Public(tx) => Self {
tx_kind: TxKind::Public,
encoded_transaction_data: tx.to_bytes(),
},
nssa::NSSATransaction::PrivacyPreserving(tx) => Self {
tx_kind: TxKind::PrivacyPreserving,
encoded_transaction_data: tx.to_bytes(),
2025-08-28 12:00:04 +03:00
},
}
}
}
2025-09-03 10:29:51 +03:00
impl TryFrom<&TransactionBody> for nssa::NSSATransaction {
2025-08-28 12:00:04 +03:00
type Error = nssa::error::NssaError;
2025-09-03 10:29:51 +03:00
fn try_from(value: &TransactionBody) -> 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
}
}
}
}
impl TransactionBody {
2025-07-10 12:52:48 -03:00
/// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`.
pub fn hash(&self) -> TreeHashType {
let bytes_to_hash = self.to_bytes();
let mut hasher = sha2::Sha256::new();
hasher.update(&bytes_to_hash);
TreeHashType::from(hasher.finalize_fixed())
}
2025-09-03 10:29:51 +03:00
pub fn to_bytes(&self) -> Vec<u8> {
2025-07-10 13:00:27 -03:00
// TODO: Remove `unwrap` by implementing a `to_bytes` method
// that deterministically encodes all transaction fields to bytes
// and guarantees serialization will succeed.
serde_json::to_vec(&self).unwrap()
}
2025-09-03 10:29:51 +03:00
pub fn from_bytes(bytes: Vec<u8>) -> Self {
2025-09-02 11:06:41 +03:00
serde_json::from_slice(&bytes).unwrap()
}
pub fn log(&self) {
info!("Transaction hash is {:?}", hex::encode(self.hash()));
info!("Transaction tx_kind is {:?}", self.tx_kind);
}
2024-12-30 09:10:04 +02:00
}
2025-07-10 12:09:01 -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-07-16 11:36:20 -03:00
use super::*;
2025-09-04 14:38:41 +03:00
use k256::{FieldBytes, ecdsa::signature::Signer};
use secp256k1_zkp::{Tweak, constants::SECRET_KEY_SIZE};
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
transaction::{TransactionBody, TxKind},
TreeHashType,
2025-07-10 12:16:05 -03:00
};
2025-07-10 12:09:01 -03:00
2025-07-16 11:36:20 -03:00
fn test_transaction_body() -> TransactionBody {
TransactionBody {
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-03 10:29:51 +03:00
let data = body.to_bytes();
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-03 10:29:51 +03:00
let body_bytes = body.to_bytes();
let body_new = TransactionBody::from_bytes(body_bytes);
2025-09-03 10:29:51 +03:00
assert_eq!(body, body_new);
#[test]
fn test_into_authenticated_succeeds_for_valid_signature() {
let transaction = test_transaction();
2025-07-16 11:36:20 -03:00
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
let signature = authenticated_tx.transaction().signature;
2025-07-16 11:36:20 -03:00
let hash = authenticated_tx.hash();
assert_eq!(authenticated_tx.transaction(), &transaction);
assert_eq!(hash, &transaction.body.hash());
2025-09-04 14:38:41 +03:00
assert!(
authenticated_tx
.transaction()
.public_key
.verify(&transaction.body.to_bytes(), &signature)
.is_ok()
);
2025-07-16 11:36:20 -03:00
}
#[test]
fn test_into_authenticated_fails_for_invalid_signature() {
let body = test_transaction_body();
let key_bytes = FieldBytes::from_slice(&[37; 32]);
let private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap();
let transaction = {
let mut this = Transaction::new(body, private_key.clone());
// Modify the signature to make it invalid
// We do this by changing it to the signature of something else
this.signature = private_key.sign(b"deadbeef");
this
};
2025-07-16 11:36:20 -03:00
matches!(
transaction.into_authenticated(),
Err(TransactionSignatureError::InvalidSignature)
);
}
#[test]
fn test_authenticated_transaction_getter() {
let transaction = test_transaction();
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
assert_eq!(authenticated_tx.transaction(), &transaction);
}
#[test]
fn test_authenticated_transaction_hash_getter() {
let transaction = test_transaction();
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
assert_eq!(authenticated_tx.hash(), &transaction.body.hash());
}
#[test]
fn test_authenticated_transaction_into_transaction() {
let transaction = test_transaction();
let authenticated_tx = transaction.clone().into_authenticated().unwrap();
assert_eq!(authenticated_tx.into_transaction(), transaction);
}
2025-07-10 12:16:05 -03:00
}