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#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
19pub struct Bundle(Vec<Transaction>);
20
21#[derive(Clone, Debug, Default, PartialEq)]
23pub struct BundleEntry<'a, 'b> {
24 pub signature_message_length: usize,
26 pub address: &'a str,
28 pub value: i64,
30 pub tag: &'b str,
32 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 pub fn new(transactions: impl Into<Vec<Transaction>>) -> Bundle {
63 Bundle(transactions.into())
64 }
65
66 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 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 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 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 + ×tamp_trits.trytes()?
131 + ¤t_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 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}