iota_model/
transaction.rs

1use 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
10/// Right pads a string to a certain length in place
11///
12/// * `x` - the string to be padded
13/// * `len` - the target length of the string
14/// * `pad` - the char to pad with
15pub fn right_pad_string(x: &mut String, len: usize, pad: char) {
16    while x.len() < len {
17        x.push(pad);
18    }
19}
20
21/// Right pads a vector to a certain length in place
22///
23/// * `x` - the vec to be padded
24/// * `len` - the target length of the string`
25/// * `pad` - the element to pad with
26pub 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/// Represents an IOTA transaction
36#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
37pub struct Transaction {
38    /// Transaction Hash
39    pub hash: String,
40    /// A signature or a message, both of which may be fragmented over multiple transactions in the bundle.
41    pub signature_fragments: String,
42    /// Contains either the sender or recipient's address.
43    pub address: String,
44    /// Amount of IOTA tokens to deposit to or withdraw from the address
45    pub value: i64,
46    /// User-defined tag (soon to be removed)
47    pub obsolete_tag: String,
48    /// Unix epoch: Seconds since Jan 1, 1970
49    pub timestamp: i64,
50    /// Index of a transaction in the bundle
51    pub current_index: usize,
52    /// Index of the last transaction in the bundle
53    pub last_index: usize,
54    /// Hash of the bundle
55    pub bundle: String,
56    /// Trunk transaction hash
57    pub trunk_transaction: String,
58    /// Branch transaction hash
59    pub branch_transaction: String,
60    /// Trytes that represent the amount of times a transaction must be hashed to check the proof of work.
61    pub nonce: String,
62    /// Persistence of transaction
63    pub persistence: bool,
64    /// User-defined tag
65    pub tag: String,
66    /// Unix epoch: Milliseconds since Jan 1, 1970 (after POW)
67    pub attachment_timestamp: i64,
68    /// Lower limit of the attachmentTimestamp field (not currently used)
69    pub attachment_timestamp_lower_bound: i64,
70    /// Upper limit of the attachmentTimestamp field (not currently used)
71    pub attachment_timestamp_upper_bound: i64,
72}
73
74// impl fmt::Display for Transaction {
75//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76//         write!(f, "{}", serde_json::to_string_pretty(self))
77//     }
78// }
79
80/// This type provides some errors that can occur
81#[derive(Copy, Clone, Debug, Fail)]
82pub enum TransactionParseError {
83    /// This error occurs if the string being parsed is empty
84    #[fail(display = "tryte string is empty")]
85    TryteStringEmpty,
86    /// This error occurs when the block of sixteen 9s at index 2279
87    /// isn't present in the provided string
88    #[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        + &timestamp_trits.trytes()?
178        + &current_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}