iota_model/
transaction.rs1use std::convert::TryInto;
2use std::str::FromStr;
3
4use serde::{Deserialize, Serialize};
5
6use crate::Result;
7use iota_conversion::Trinary;
8use iota_crypto::{Curl, Sponge};
9
10pub fn right_pad_string(x: &mut String, len: usize, pad: char) {
16 while x.len() < len {
17 x.push(pad);
18 }
19}
20
21pub fn right_pad_vec<T>(x: &mut Vec<T>, len: usize, pad: T)
27where
28 T: Copy,
29{
30 while x.len() < len {
31 x.push(pad);
32 }
33}
34
35#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
37pub struct Transaction {
38 pub hash: String,
40 pub signature_fragments: String,
42 pub address: String,
44 pub value: i64,
46 pub obsolete_tag: String,
48 pub timestamp: i64,
50 pub current_index: usize,
52 pub last_index: usize,
54 pub bundle: String,
56 pub trunk_transaction: String,
58 pub branch_transaction: String,
60 pub nonce: String,
62 pub persistence: bool,
64 pub tag: String,
66 pub attachment_timestamp: i64,
68 pub attachment_timestamp_lower_bound: i64,
70 pub attachment_timestamp_upper_bound: i64,
72}
73
74#[derive(Copy, Clone, Debug, Fail)]
82pub enum TransactionParseError {
83 #[fail(display = "tryte string is empty")]
85 TryteStringEmpty,
86 #[fail(display = "Should be sixteen 9's at index 2279")]
89 NineSectionMissing,
90}
91
92impl FromStr for Transaction {
93 type Err = failure::Error;
94
95 fn from_str(trytes: &str) -> Result<Self> {
96 ensure!(!trytes.is_empty(), TransactionParseError::TryteStringEmpty);
97 for c in trytes.chars().skip(2279).take(16) {
98 ensure!(c == '9', TransactionParseError::NineSectionMissing);
99 }
100 let transaction_trits = trytes.trits();
101
102 let mut hash = [0; 243];
103 let mut curl = Curl::default();
104 curl.reset();
105 curl.absorb(&transaction_trits)?;
106 curl.squeeze(&mut hash)?;
107
108 let mut transaction = Transaction::default();
109 transaction.hash = hash.trytes()?;
110 transaction.signature_fragments = trytes[0..2187].into();
111 transaction.address = trytes[2187..2268].into();
112 transaction.value = iota_conversion::long_value(&transaction_trits[6804..6837]);
113 transaction.obsolete_tag = trytes[2295..2322].into();
114 transaction.timestamp = iota_conversion::long_value(&transaction_trits[6966..6993]);
115 transaction.current_index =
116 iota_conversion::long_value(&transaction_trits[6993..7020]) as usize;
117 transaction.last_index =
118 iota_conversion::long_value(&transaction_trits[7020..7047]) as usize;
119 transaction.bundle = trytes[2349..2430].into();
120 transaction.trunk_transaction = trytes[2430..2511].into();
121 transaction.branch_transaction = trytes[2511..2592].into();
122
123 transaction.tag = trytes[2592..2619].into();
124 transaction.attachment_timestamp =
125 iota_conversion::long_value(&transaction_trits[7857..7884]);
126 transaction.attachment_timestamp_lower_bound =
127 iota_conversion::long_value(&transaction_trits[7884..7911]);
128 transaction.attachment_timestamp_upper_bound =
129 iota_conversion::long_value(&transaction_trits[7911..7938]);
130 transaction.nonce = trytes[2646..2673].into();
131 Ok(transaction)
132 }
133}
134
135impl TryInto<String> for Transaction {
136 type Error = failure::Error;
137
138 fn try_into(self) -> Result<String> {
139 to_string(&self)
140 }
141}
142
143impl TryInto<String> for &Transaction {
144 type Error = failure::Error;
145
146 fn try_into(self) -> Result<String> {
147 to_string(self)
148 }
149}
150
151fn to_string(tx: &Transaction) -> Result<String> {
152 let mut value_trits = tx.value.trits();
153 right_pad_vec(&mut value_trits, 81, 0);
154
155 let mut timestamp_trits = tx.timestamp.trits();
156 right_pad_vec(&mut timestamp_trits, 27, 0);
157
158 let mut current_index_trits = (tx.current_index as i64).trits();
159 right_pad_vec(&mut current_index_trits, 27, 0);
160
161 let mut last_index_trits = (tx.last_index as i64).trits();
162 right_pad_vec(&mut last_index_trits, 27, 0);
163
164 let mut attachment_timestamp_trits = tx.attachment_timestamp.trits();
165 right_pad_vec(&mut attachment_timestamp_trits, 27, 0);
166
167 let mut attachment_timestamp_lower_bound_trits = tx.attachment_timestamp_lower_bound.trits();
168 right_pad_vec(&mut attachment_timestamp_lower_bound_trits, 27, 0);
169
170 let mut attachment_timestamp_upper_bound_trits = tx.attachment_timestamp_upper_bound.trits();
171 right_pad_vec(&mut attachment_timestamp_upper_bound_trits, 27, 0);
172
173 Ok(tx.signature_fragments.clone()
174 + &tx.address
175 + &value_trits.trytes()?
176 + &tx.obsolete_tag
177 + ×tamp_trits.trytes()?
178 + ¤t_index_trits.trytes()?
179 + &last_index_trits.trytes()?
180 + &tx.bundle
181 + &tx.trunk_transaction
182 + &tx.branch_transaction
183 + &tx.tag
184 + &attachment_timestamp_trits.trytes()?
185 + &attachment_timestamp_lower_bound_trits.trytes()?
186 + &attachment_timestamp_upper_bound_trits.trytes()?
187 + &tx.nonce)
188}