tofuri_transaction/
lib.rs

1use serde::Deserialize;
2use serde::Serialize;
3use serde_big_array::BigArray;
4use sha2::Digest;
5use sha2::Sha256;
6use std::error::Error;
7use tofuri_core::*;
8use tofuri_key::Key;
9pub trait Transaction {
10    fn get_output_address(&self) -> &AddressBytes;
11    fn get_timestamp(&self) -> u32;
12    fn get_amount_bytes(&self) -> AmountBytes;
13    fn get_fee_bytes(&self) -> AmountBytes;
14    fn hash(&self) -> Hash;
15    fn hash_input(&self) -> [u8; 32];
16}
17impl Transaction for TransactionA {
18    fn get_output_address(&self) -> &AddressBytes {
19        &self.output_address
20    }
21    fn get_timestamp(&self) -> u32 {
22        self.timestamp
23    }
24    fn get_amount_bytes(&self) -> AmountBytes {
25        tofuri_int::to_be_bytes(self.amount)
26    }
27    fn get_fee_bytes(&self) -> AmountBytes {
28        tofuri_int::to_be_bytes(self.fee)
29    }
30    fn hash(&self) -> Hash {
31        hash(self)
32    }
33    fn hash_input(&self) -> [u8; 32] {
34        hash_input(self)
35    }
36}
37impl Transaction for TransactionB {
38    fn get_output_address(&self) -> &AddressBytes {
39        &self.output_address
40    }
41    fn get_timestamp(&self) -> u32 {
42        self.timestamp
43    }
44    fn get_amount_bytes(&self) -> AmountBytes {
45        self.amount
46    }
47    fn get_fee_bytes(&self) -> AmountBytes {
48        self.fee
49    }
50    fn hash(&self) -> Hash {
51        hash(self)
52    }
53    fn hash_input(&self) -> [u8; 32] {
54        hash_input(self)
55    }
56}
57#[derive(Serialize, Deserialize, Clone, Debug)]
58pub struct TransactionA {
59    pub input_address: AddressBytes,
60    pub output_address: AddressBytes,
61    pub amount: u128,
62    pub fee: u128,
63    pub timestamp: u32,
64    pub hash: Hash,
65    #[serde(with = "BigArray")]
66    pub signature: SignatureBytes,
67}
68#[derive(Serialize, Deserialize, Debug, Clone)]
69pub struct TransactionB {
70    pub output_address: AddressBytes,
71    pub amount: AmountBytes,
72    pub fee: AmountBytes,
73    pub timestamp: u32,
74    #[serde(with = "BigArray")]
75    pub signature: SignatureBytes,
76}
77impl TransactionA {
78    pub fn b(&self) -> TransactionB {
79        TransactionB {
80            output_address: self.output_address,
81            amount: tofuri_int::to_be_bytes(self.amount),
82            fee: tofuri_int::to_be_bytes(self.fee),
83            timestamp: self.timestamp,
84            signature: self.signature,
85        }
86    }
87    pub fn hash(&self) -> Hash {
88        hash(self)
89    }
90    pub fn sign(public_key_output: AddressBytes, amount: u128, fee: u128, timestamp: u32, key: &Key) -> Result<TransactionA, Box<dyn Error>> {
91        let mut transaction_a = TransactionA {
92            input_address: [0; 20],
93            output_address: public_key_output,
94            amount: tofuri_int::floor(amount),
95            fee: tofuri_int::floor(fee),
96            timestamp,
97            hash: [0; 32],
98            signature: [0; 64],
99        };
100        transaction_a.hash = transaction_a.hash();
101        transaction_a.signature = key.sign(&transaction_a.hash)?;
102        transaction_a.input_address = key.address_bytes();
103        Ok(transaction_a)
104    }
105}
106impl TransactionB {
107    pub fn a(&self, input_address: Option<AddressBytes>) -> Result<TransactionA, Box<dyn Error>> {
108        let input_address = input_address.unwrap_or(self.input_address()?);
109        Ok(TransactionA {
110            output_address: self.output_address,
111            amount: tofuri_int::from_be_slice(&self.amount),
112            fee: tofuri_int::from_be_slice(&self.fee),
113            timestamp: self.timestamp,
114            signature: self.signature,
115            input_address,
116            hash: self.hash(),
117        })
118    }
119    pub fn hash(&self) -> Hash {
120        hash(self)
121    }
122    fn input_address(&self) -> Result<AddressBytes, Box<dyn Error>> {
123        Ok(Key::address(&self.input_public_key()?))
124    }
125    fn input_public_key(&self) -> Result<PublicKeyBytes, Box<dyn Error>> {
126        Key::recover(&self.hash(), &self.signature)
127    }
128}
129fn hash<T: Transaction>(transaction: &T) -> Hash {
130    let mut hasher = Sha256::new();
131    hasher.update(transaction.hash_input());
132    hasher.finalize().into()
133}
134fn hash_input<T: Transaction>(transaction: &T) -> [u8; 32] {
135    let mut bytes = [0; 32];
136    bytes[0..20].copy_from_slice(transaction.get_output_address());
137    bytes[20..24].copy_from_slice(&transaction.get_timestamp().to_be_bytes());
138    bytes[24..28].copy_from_slice(&transaction.get_amount_bytes());
139    bytes[28..32].copy_from_slice(&transaction.get_fee_bytes());
140    bytes
141}
142impl Default for TransactionA {
143    fn default() -> Self {
144        TransactionA {
145            output_address: [0; 20],
146            amount: 0,
147            fee: 0,
148            timestamp: 0,
149            signature: [0; 64],
150            input_address: [0; 20],
151            hash: [0; 32],
152        }
153    }
154}
155impl Default for TransactionB {
156    fn default() -> Self {
157        TransactionB {
158            output_address: [0; 20],
159            amount: [0; AMOUNT_BYTES],
160            fee: [0; AMOUNT_BYTES],
161            timestamp: 0,
162            signature: [0; 64],
163        }
164    }
165}
166#[cfg(test)]
167mod tests {
168    use super::*;
169    #[test]
170    fn test_hash() {
171        assert_eq!(
172            TransactionB::default().hash(),
173            [
174                102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32, 8, 151, 20, 133, 110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41,
175                37
176            ]
177        );
178    }
179}