iota_model/
bundle.rs

1use std::fmt;
2use std::ops::{Deref, DerefMut};
3
4use serde::{Deserialize, Serialize};
5use serde_json;
6
7use crate::Result;
8use iota_constants::HASH_TRINARY_SIZE as HASH_LENGTH;
9use iota_conversion::Trinary;
10use iota_crypto::{Kerl, Sponge};
11
12use super::transaction::Transaction;
13
14const EMPTY_HASH: &str =
15    "999999999999999999999999999999999999999999999999999999999999999999999999999999999";
16
17/// Represents a bundle of transactions
18#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
19pub struct Bundle(Vec<Transaction>);
20
21/// Represent a entry to add to bundle
22#[derive(Clone, Debug, Default, PartialEq)]
23pub struct BundleEntry<'a, 'b> {
24    /// Number of siganure or message transaction needed for its length
25    pub signature_message_length: usize,
26    /// Transaction address
27    pub address: &'a str,
28    /// Transaction value
29    pub value: i64,
30    /// Transaction Tag
31    pub tag: &'b str,
32    /// Unix epoch: Seconds since Jan 1, 1970
33    pub timestamp: i64,
34}
35
36impl Deref for Bundle {
37    type Target = Vec<Transaction>;
38
39    fn deref(&self) -> &Self::Target {
40        &self.0
41    }
42}
43
44impl DerefMut for Bundle {
45    fn deref_mut(&mut self) -> &mut Self::Target {
46        &mut self.0
47    }
48}
49
50impl fmt::Display for Bundle {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        write!(
53            f,
54            "{}",
55            serde_json::to_string_pretty(self).unwrap_or_default()
56        )
57    }
58}
59
60impl Bundle {
61    /// Greates a new bundle using the provided transactions
62    pub fn new(transactions: impl Into<Vec<Transaction>>) -> Bundle {
63        Bundle(transactions.into())
64    }
65
66    /// Adds an entry into the bundle
67    pub fn add_entry(&mut self, entry: BundleEntry<'_, '_>) {
68        for i in 0..entry.signature_message_length {
69            let mut trx = Transaction::default();
70            trx.address = entry.address.into();
71            trx.tag = entry.tag.into();
72            trx.obsolete_tag = entry.tag.into();
73            trx.timestamp = entry.timestamp;
74            match i {
75                0 => trx.value = entry.value,
76                _ => trx.value = 0,
77            }
78            self.0.push(trx);
79        }
80    }
81
82    /// Adds trytes into the bundle
83    pub fn add_trytes(&mut self, signature_fragments: &[String]) {
84        let empty_signature_fragment = "9".repeat(2187);
85        let empty_hash = EMPTY_HASH;
86        let empty_timestamp = 999_999_999;
87
88        for (i, bundle) in self.0.iter_mut().enumerate() {
89            let new_sig = if signature_fragments.is_empty() || signature_fragments[i].is_empty() {
90                &empty_signature_fragment
91            } else {
92                &signature_fragments[i]
93            };
94            bundle.signature_fragments = new_sig.clone();
95            bundle.trunk_transaction = empty_hash.into();
96            bundle.branch_transaction = empty_hash.into();
97            bundle.attachment_timestamp = empty_timestamp;
98            bundle.attachment_timestamp_lower_bound = empty_timestamp;
99            bundle.attachment_timestamp_upper_bound = empty_timestamp;
100            bundle.nonce = "9".repeat(27);
101        }
102    }
103
104    /// Reset bundle indexes
105    pub fn reset_indexes(&mut self) {
106        let mut current_index = 0;
107        let last_index = self.0.len() - 1;
108
109        for tx in &mut self.0 {
110            tx.current_index = current_index;
111            tx.last_index = last_index;
112            current_index += 1;
113        }
114    }
115
116    /// Finalizes the bundle
117    pub fn finalize(&mut self) -> Result<()> {
118        let mut valid_bundle = false;
119        let mut kerl = Kerl::default();
120        while !valid_bundle {
121            kerl.reset();
122            for bundle in &mut self.0 {
123                let value_trits = bundle.value.trits_with_length(81);
124                let timestamp_trits = bundle.timestamp.trits_with_length(27);
125                let current_index_trits = (bundle.current_index as i64).trits_with_length(27);
126                let last_index_trits = (bundle.last_index as i64).trits_with_length(27);
127                let bundle_essence = bundle.address.clone()
128                    + &value_trits.trytes()?
129                    + &bundle.obsolete_tag
130                    + &timestamp_trits.trytes()?
131                    + &current_index_trits.trytes()?
132                    + &last_index_trits.trytes()?;
133                kerl.absorb(&bundle_essence.trits())?;
134            }
135            let mut hash = [0; HASH_LENGTH];
136            kerl.squeeze(&mut hash)?;
137            let hash_trytes = hash.trytes()?;
138            for bundle in &mut self.0 {
139                bundle.bundle = hash_trytes.clone();
140            }
141            let normalized_hash = Bundle::normalized_bundle(&hash_trytes);
142            if normalized_hash.contains(&13) {
143                let increased_tag = crate::trit_adder::add(&self.0[0].obsolete_tag.trits(), &[1]);
144                self.0[0].obsolete_tag = increased_tag.trytes()?;
145            } else {
146                valid_bundle = true;
147            }
148        }
149        Ok(())
150    }
151
152    /// Normalizes a bundle hash
153    pub fn normalized_bundle(bundle_hash: &str) -> [i8; 81] {
154        let mut normalized_bundle = [0; 81];
155        for i in 0..3 {
156            let mut sum: i64 = 0;
157            for j in 0..27 {
158                let mut t = String::new();
159                t.push(bundle_hash.chars().nth(i * 27 + j).unwrap());
160                normalized_bundle[i * 27 + j] = iota_conversion::value(&t.trits());
161                sum += i64::from(normalized_bundle[i * 27 + j]);
162            }
163            if sum >= 0 {
164                while sum > 0 {
165                    for j in 0..27 {
166                        if normalized_bundle[i * 27 + j] > -13 {
167                            normalized_bundle[i * 27 + j] -= 1;
168                            break;
169                        }
170                    }
171                    sum -= 1;
172                }
173            } else {
174                while sum < 0 {
175                    for j in 0..27 {
176                        if normalized_bundle[i * 27 + j] < 13 {
177                            normalized_bundle[i * 27 + j] += 1;
178                            break;
179                        }
180                    }
181                    sum += 1;
182                }
183            }
184        }
185        normalized_bundle
186    }
187}