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}