snap_coin/core/
transaction.rs1use bincode::{Decode, Encode, error::EncodeError};
2use num_bigint::BigUint;
3use rand::Rng;
4use serde::{Deserialize, Serialize};
5
6use crate::crypto::{
7 Hash, Signature,
8 keys::{Private, Public},
9};
10
11pub type TransactionId = Hash;
13
14pub const MAX_TRANSACTION_IO: usize = 500;
15
16#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
18pub struct TransactionInput {
19 pub transaction_id: TransactionId,
20 pub output_index: usize,
21 pub signature: Option<Signature>,
22}
23
24#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
26pub struct TransactionOutput {
27 pub amount: u64,
28 pub receiver: Public,
29}
30
31#[derive(Encode, Decode, Debug, Clone, Serialize, Deserialize)]
36pub struct Transaction {
37 pub inputs: Vec<TransactionInput>,
38 pub outputs: Vec<TransactionOutput>,
39 pub transaction_id: Option<TransactionId>,
40 pub nonce: u64,
41 pub timestamp: u64,
42}
43
44impl Transaction {
45 pub fn new_transaction_now(
48 inputs: Vec<TransactionInput>,
49 outputs: Vec<TransactionOutput>,
50 signing_keys: &mut Vec<Private>,
51 ) -> Result<Self, EncodeError> {
52 let mut transaction = Self {
53 inputs,
54 outputs,
55 transaction_id: None,
56 nonce: 0,
57 timestamp: chrono::Utc::now().timestamp() as u64,
58 };
59 let signing_buf = transaction.get_input_signing_buf()?;
60
61 for (input, key) in transaction.inputs.iter_mut().zip(signing_keys.iter_mut()) {
62 input.signature = Some(Signature::new_signature(key, &signing_buf));
63 }
64
65 Ok(transaction)
66 }
67
68 pub fn compute_pow(
73 &mut self,
74 tx_difficulty: &[u8; 32],
75 difficulty_margin: Option<f64>,
76 ) -> Result<(), EncodeError> {
77 let mut target = BigUint::from_bytes_be(tx_difficulty);
78
79 if let Some(margin) = difficulty_margin {
80 if margin > 0.0 {
81 target /= BigUint::from((margin * 1000.0) as u64);
83 }
84 }
85 let mut rng = rand::rng();
86 loop {
87 self.nonce = rng.random();
88 let hashing_buf = self.get_tx_hashing_buf()?;
89 if BigUint::from_bytes_be(&*Hash::new(&hashing_buf)) <= target {
90 self.transaction_id = Some(Hash::new(&hashing_buf));
91 return Ok(());
92 }
93 }
94 }
95
96 pub fn get_input_signing_buf(&self) -> Result<Vec<u8>, EncodeError> {
98 let mut signature_less_transaction: Transaction = self.clone();
99
100 signature_less_transaction.transaction_id = None;
101 signature_less_transaction.nonce = 0;
102
103 for input in &mut signature_less_transaction.inputs {
104 input.signature = None; }
106
107 Ok(bincode::encode_to_vec(
108 signature_less_transaction,
109 bincode::config::standard(),
110 )?)
111 }
112
113 pub fn get_tx_hashing_buf(&self) -> Result<Vec<u8>, EncodeError> {
115 let mut signature_less_transaction = self.clone();
116
117 signature_less_transaction.transaction_id = None;
118
119 Ok(bincode::encode_to_vec(
120 signature_less_transaction,
121 bincode::config::standard(),
122 )?)
123 }
124}