1#![doc = include_str!("../README.md")]
2
3#[allow(clippy::all)]
4#[allow(dead_code)]
5#[allow(missing_docs)]
6pub mod generated {
7 include!(concat!(env!("OUT_DIR"), "/mod.rs"));
8}
9
10#[cfg(feature = "serde")]
11pub mod json;
12
13use bech32::{Bech32, Hrp};
14use blake2::{
15 Blake2b,
16 digest::{Digest, consts::U32},
17};
18type Blake2b256 = Blake2b<U32>;
19#[cfg(feature = "serde")]
20pub use json::Transaction as JsonTransaction;
21pub use prost::Message;
22
23const ERD_HRP: Hrp = Hrp::parse_unchecked("erd");
25
26impl generated::proto::Transaction {
27 pub const HASH_SIZE: usize = 32;
29
30 #[must_use]
33 pub fn compute_hash(&self) -> [u8; Self::HASH_SIZE] {
34 let encoded = self.encode_to_vec();
35 Self::hash_bytes(&encoded)
36 }
37
38 #[must_use]
41 pub fn get_tx_hash(&self) -> [u8; Self::HASH_SIZE] {
42 self.compute_hash()
43 }
44
45 #[must_use]
47 pub fn hash_bytes(bytes: &[u8]) -> [u8; Self::HASH_SIZE] {
48 let digest = Blake2b256::digest(bytes);
49 digest.into()
50 }
51
52 fn bech32_address(bytes: &[u8]) -> Result<Option<String>, bech32::EncodeError> {
53 if bytes.is_empty() {
54 return Ok(None);
55 }
56
57 let encoded = bech32::encode::<Bech32>(ERD_HRP, bytes)?;
58 Ok(Some(encoded))
59 }
60
61 pub fn sender_address_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
63 Self::bech32_address(self.snd_addr.as_ref())
64 }
65
66 pub fn sender_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
68 self.sender_address_bech32()
69 }
70
71 pub fn receiver_address_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
73 Self::bech32_address(self.rcv_addr.as_ref())
74 }
75
76 pub fn receiver_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
78 self.receiver_address_bech32()
79 }
80
81 pub fn guardian_address_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
83 Self::bech32_address(self.guardian_addr.as_ref())
84 }
85
86 pub fn guardian_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
88 self.guardian_address_bech32()
89 }
90
91 pub fn relayer_address_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
93 Self::bech32_address(self.relayer_addr.as_ref())
94 }
95
96 pub fn relayer_bech32(&self) -> Result<Option<String>, bech32::EncodeError> {
98 self.relayer_address_bech32()
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::ERD_HRP;
105 use super::generated::proto::Transaction;
106 use bech32::Bech32;
107 use hex::FromHex;
108 use prost::Message;
109 use prost::bytes::Bytes;
110
111 #[test]
112 fn hash_bytes_matches_known_digests() {
113 let expected_empty = <[u8; Transaction::HASH_SIZE]>::from_hex(
114 "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8",
115 )
116 .unwrap();
117 assert_eq!(Transaction::hash_bytes(b""), expected_empty);
118
119 let expected_hello = <[u8; Transaction::HASH_SIZE]>::from_hex(
120 "256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610",
121 )
122 .unwrap();
123 assert_eq!(Transaction::hash_bytes(b"hello world"), expected_hello);
124 }
125
126 #[test]
127 fn compute_hash_reencodes_transaction_like_go() {
128 let tx = Transaction {
129 nonce: 1,
130 value: Bytes::new(),
131 rcv_addr: Bytes::new(),
132 rcv_user_name: Bytes::new(),
133 snd_addr: Bytes::new(),
134 snd_user_name: Bytes::new(),
135 gas_price: 0,
136 gas_limit: 0,
137 data: Bytes::new(),
138 chain_id: Bytes::new(),
139 version: 0,
140 signature: Bytes::new(),
141 options: 0,
142 guardian_addr: Bytes::new(),
143 guardian_signature: Bytes::new(),
144 relayer_addr: Bytes::new(),
145 relayer_signature: Bytes::new(),
146 };
147
148 let expected = <[u8; Transaction::HASH_SIZE]>::from_hex(
149 "890b1d2195b2db958c0b3c02d09997776a5f7c0fc2daf30f3bf8469b841c30e9",
150 )
151 .unwrap();
152
153 let encoded = tx.encode_to_vec();
155 assert_eq!(encoded, [0x08, 0x01]);
156
157 assert_eq!(tx.compute_hash(), expected);
158 }
159
160 #[test]
161 fn bech32_address_helpers_encode_addresses() {
162 let sender_vec: Vec<u8> = (0..32).collect();
163 let receiver_vec: Vec<u8> = (32..64).collect();
164 let relayer_vec: Vec<u8> = (64..96).collect();
165
166 let tx = Transaction {
167 snd_addr: Bytes::from(sender_vec.clone()),
168 rcv_addr: Bytes::from(receiver_vec.clone()),
169 relayer_addr: Bytes::from(relayer_vec.clone()),
170 ..Default::default()
171 };
172
173 let expected_sender = bech32::encode::<Bech32>(ERD_HRP, &sender_vec).unwrap();
174 let expected_receiver = bech32::encode::<Bech32>(ERD_HRP, &receiver_vec).unwrap();
175 let expected_relayer = bech32::encode::<Bech32>(ERD_HRP, &relayer_vec).unwrap();
176
177 assert_eq!(tx.sender_address_bech32().unwrap(), Some(expected_sender));
178 assert_eq!(
179 tx.sender_bech32().unwrap(),
180 tx.sender_address_bech32().unwrap()
181 );
182 assert_eq!(
183 tx.receiver_address_bech32().unwrap(),
184 Some(expected_receiver)
185 );
186 assert_eq!(
187 tx.receiver_bech32().unwrap(),
188 tx.receiver_address_bech32().unwrap()
189 );
190 assert_eq!(tx.guardian_address_bech32().unwrap(), None);
191 assert_eq!(
192 tx.guardian_bech32().unwrap(),
193 tx.guardian_address_bech32().unwrap()
194 );
195 assert_eq!(tx.relayer_address_bech32().unwrap(), Some(expected_relayer));
196 assert_eq!(
197 tx.relayer_bech32().unwrap(),
198 tx.relayer_address_bech32().unwrap()
199 );
200 }
201
202 #[test]
203 fn test_hash_bytes_large_input() {
204 let large_input: Vec<u8> = (0..=255).cycle().take(10 * 1024).collect();
206 let hash = Transaction::hash_bytes(&large_input);
207
208 assert_eq!(hash.len(), Transaction::HASH_SIZE);
210
211 let hash2 = Transaction::hash_bytes(&large_input);
213 assert_eq!(hash, hash2);
214
215 assert_ne!(hash, [0u8; 32]);
217 }
218
219 #[test]
220 fn test_hash_bytes_all_zeros() {
221 let zeros = vec![0u8; 1024];
222 let hash = Transaction::hash_bytes(&zeros);
223
224 assert_eq!(hash.len(), Transaction::HASH_SIZE);
226
227 assert_ne!(hash, [0u8; 32]);
229
230 assert_eq!(hash, Transaction::hash_bytes(&zeros));
232 }
233
234 #[test]
235 fn test_hash_bytes_all_ones() {
236 let ones = vec![0xFFu8; 1024];
237 let hash = Transaction::hash_bytes(&ones);
238
239 assert_eq!(hash.len(), Transaction::HASH_SIZE);
241
242 assert_ne!(hash, [0xFFu8; 32]);
244
245 assert_eq!(hash, Transaction::hash_bytes(&ones));
247
248 let zeros = vec![0u8; 1024];
250 assert_ne!(hash, Transaction::hash_bytes(&zeros));
251 }
252
253 #[test]
254 fn test_compute_hash_empty_tx() {
255 let tx = Transaction::default();
256 let hash = tx.compute_hash();
257
258 assert_eq!(hash.len(), Transaction::HASH_SIZE);
260
261 let expected_empty = <[u8; Transaction::HASH_SIZE]>::from_hex(
263 "0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8",
264 )
265 .unwrap();
266 assert_eq!(hash, expected_empty);
267 }
268
269 #[test]
270 fn test_compute_hash_full_tx() {
271 let tx = Transaction {
273 nonce: 42,
274 value: Bytes::from(vec![0x01, 0x00, 0x00, 0x00]), rcv_addr: Bytes::from(vec![1u8; 32]),
276 rcv_user_name: Bytes::from("receiver_name"),
277 snd_addr: Bytes::from(vec![2u8; 32]),
278 snd_user_name: Bytes::from("sender_name"),
279 gas_price: 1_000_000_000,
280 gas_limit: 50_000,
281 data: Bytes::from("transfer@01"),
282 chain_id: Bytes::from("1"),
283 version: 2,
284 signature: Bytes::from(vec![3u8; 64]),
285 options: 1,
286 guardian_addr: Bytes::from(vec![4u8; 32]),
287 guardian_signature: Bytes::from(vec![5u8; 64]),
288 relayer_addr: Bytes::from(vec![6u8; 32]),
289 relayer_signature: Bytes::from(vec![7u8; 64]),
290 };
291
292 let hash = tx.compute_hash();
293
294 assert_eq!(hash.len(), Transaction::HASH_SIZE);
296
297 assert_ne!(hash, [0u8; 32]);
299
300 assert_ne!(hash, Transaction::default().compute_hash());
302 }
303
304 #[test]
305 fn test_compute_hash_deterministic() {
306 let tx = Transaction {
307 nonce: 100,
308 value: Bytes::from(vec![0x0A]),
309 rcv_addr: Bytes::from(vec![0xAB; 32]),
310 snd_addr: Bytes::from(vec![0xCD; 32]),
311 gas_price: 500_000_000,
312 gas_limit: 100_000,
313 data: Bytes::from("test_data"),
314 chain_id: Bytes::from("T"),
315 version: 1,
316 ..Default::default()
317 };
318
319 let hash1 = tx.compute_hash();
321 let hash2 = tx.compute_hash();
322 let hash3 = tx.compute_hash();
323
324 assert_eq!(hash1, hash2);
326 assert_eq!(hash2, hash3);
327
328 let tx_cloned = tx.clone();
330 assert_eq!(tx.compute_hash(), tx_cloned.compute_hash());
331 assert_eq!(tx.get_tx_hash(), tx.compute_hash());
332 }
333
334 #[test]
335 fn test_sender_address_empty() {
336 let tx = Transaction {
337 snd_addr: Bytes::new(),
338 ..Default::default()
339 };
340
341 let result = tx.sender_address_bech32().unwrap();
342 assert_eq!(result, None);
343 }
344
345 #[test]
346 fn test_receiver_address_empty() {
347 let tx = Transaction {
348 rcv_addr: Bytes::new(),
349 ..Default::default()
350 };
351
352 let result = tx.receiver_address_bech32().unwrap();
353 assert_eq!(result, None);
354 }
355
356 #[test]
357 fn test_guardian_address_populated() {
358 let guardian_bytes: Vec<u8> = (100..132).collect(); let tx = Transaction {
360 guardian_addr: Bytes::from(guardian_bytes.clone()),
361 ..Default::default()
362 };
363
364 let result = tx.guardian_address_bech32().unwrap();
365 assert!(result.is_some());
366
367 let expected = bech32::encode::<Bech32>(ERD_HRP, &guardian_bytes).unwrap();
368 assert_eq!(result.unwrap(), expected);
369 assert_eq!(
370 tx.guardian_bech32().unwrap(),
371 tx.guardian_address_bech32().unwrap()
372 );
373 }
374
375 #[test]
376 fn test_relayer_address_populated() {
377 let relayer_bytes: Vec<u8> = (150..182).collect(); let tx = Transaction {
379 relayer_addr: Bytes::from(relayer_bytes.clone()),
380 ..Default::default()
381 };
382
383 let result = tx.relayer_address_bech32().unwrap();
384 assert!(result.is_some());
385
386 let expected = bech32::encode::<Bech32>(ERD_HRP, &relayer_bytes).unwrap();
387 assert_eq!(result.unwrap(), expected);
388 assert_eq!(
389 tx.relayer_bech32().unwrap(),
390 tx.relayer_address_bech32().unwrap()
391 );
392 }
393
394 #[test]
395 fn test_all_addresses_format() {
396 let sender_bytes: Vec<u8> = (0..32).collect();
397 let receiver_bytes: Vec<u8> = (32..64).collect();
398 let guardian_bytes: Vec<u8> = (64..96).collect();
399 let relayer_bytes: Vec<u8> = (96..128).collect();
400
401 let tx = Transaction {
402 snd_addr: Bytes::from(sender_bytes),
403 rcv_addr: Bytes::from(receiver_bytes),
404 guardian_addr: Bytes::from(guardian_bytes),
405 relayer_addr: Bytes::from(relayer_bytes),
406 ..Default::default()
407 };
408
409 let sender = tx.sender_bech32().unwrap().unwrap();
411 let receiver = tx.receiver_bech32().unwrap().unwrap();
412 let guardian = tx.guardian_bech32().unwrap().unwrap();
413 let relayer = tx.relayer_bech32().unwrap().unwrap();
414
415 assert!(
416 sender.starts_with("erd1"),
417 "Sender should start with erd1, got: {sender}"
418 );
419 assert!(
420 receiver.starts_with("erd1"),
421 "Receiver should start with erd1, got: {receiver}"
422 );
423 assert!(
424 guardian.starts_with("erd1"),
425 "Guardian should start with erd1, got: {guardian}"
426 );
427 assert!(
428 relayer.starts_with("erd1"),
429 "Relayer should start with erd1, got: {relayer}"
430 );
431
432 assert_eq!(sender.len(), 62);
435 assert_eq!(receiver.len(), 62);
436 assert_eq!(guardian.len(), 62);
437 assert_eq!(relayer.len(), 62);
438 }
439}