bee_ledger/types/
balance_diff.rs1use crate::types::Error;
5
6use bee_message::{
7 address::Address,
8 constants::IOTA_SUPPLY,
9 output::{Output, DUST_THRESHOLD},
10};
11
12use std::collections::{
13 hash_map::{IntoIter, Iter, IterMut},
14 HashMap,
15};
16
17#[derive(Clone, Debug, Default)]
19pub struct BalanceDiff {
20 amount: i64,
21 dust_allowance: i64,
22 dust_outputs: i64,
23}
24
25impl BalanceDiff {
26 pub fn new(amount: i64, dust_allowance: i64, dust_outputs: i64) -> Result<Self, Error> {
28 if amount.abs() as u64 > IOTA_SUPPLY {
29 Err(Error::InvalidBalanceDiff(amount))
30 } else if dust_allowance.abs() as u64 > IOTA_SUPPLY {
31 Err(Error::InvalidBalanceDiff(dust_allowance))
32 } else if dust_outputs.abs() as u64 > IOTA_SUPPLY {
33 Err(Error::InvalidBalanceDiff(dust_outputs))
34 } else {
35 Ok(Self {
36 amount,
37 dust_allowance,
38 dust_outputs,
39 })
40 }
41 }
42
43 pub fn amount(&self) -> i64 {
45 self.amount
46 }
47
48 pub fn dust_allowance(&self) -> i64 {
50 self.dust_allowance
51 }
52
53 pub fn dust_outputs(&self) -> i64 {
55 self.dust_outputs
56 }
57
58 pub fn is_dust_mutating(&self) -> bool {
60 self.dust_allowance < 0 || self.dust_outputs > 0
61 }
62
63 pub fn negate(&mut self) {
65 self.amount = -self.amount;
66 self.dust_allowance = -self.dust_allowance;
67 self.dust_outputs = -self.dust_outputs;
68 }
69}
70
71#[derive(Clone, Debug, Default)]
73pub struct BalanceDiffs(HashMap<Address, BalanceDiff>);
74
75impl BalanceDiffs {
76 pub fn new() -> Self {
78 Self::default()
79 }
80
81 pub fn merge(&mut self, other: Self) -> Result<(), Error> {
83 for (address, diff) in other.0 {
84 let e = self.0.entry(address).or_default();
85 e.amount = e
86 .amount
87 .checked_add(diff.amount)
88 .ok_or(Error::BalanceDiffOverflow(e.amount as i128 + diff.amount as i128))?;
89 e.dust_allowance = e
90 .dust_allowance
91 .checked_add(diff.dust_allowance)
92 .ok_or(Error::BalanceDiffOverflow(
93 e.dust_allowance as i128 + diff.dust_allowance as i128,
94 ))?;
95 e.dust_outputs = e
96 .dust_outputs
97 .checked_add(diff.dust_outputs)
98 .ok_or(Error::BalanceDiffOverflow(
99 e.dust_outputs as i128 + diff.dust_outputs as i128,
100 ))?;
101 }
102
103 Ok(())
104 }
105
106 pub fn get(&self, address: &Address) -> Option<&BalanceDiff> {
108 self.0.get(address)
109 }
110
111 pub fn negate(&mut self) {
113 for (_, diff) in self.iter_mut() {
114 diff.negate();
115 }
116 }
117
118 pub fn negated(&self) -> Self {
120 let mut new = self.clone();
121 new.negate();
122 new
123 }
124
125 pub fn output_add(&mut self, output: &Output) -> Result<(), Error> {
127 match output {
128 Output::SignatureLockedSingle(output) => {
129 self.amount_add(*output.address(), output.amount())?;
130 if output.amount() < DUST_THRESHOLD {
131 self.dust_outputs_inc(*output.address())?;
132 }
133 }
134 Output::SignatureLockedDustAllowance(output) => {
135 self.amount_add(*output.address(), output.amount())?;
136 self.dust_allowance_add(*output.address(), output.amount())?;
137 }
138 Output::Treasury(_) => return Err(Error::UnsupportedOutputKind(output.kind())),
139 }
140
141 Ok(())
142 }
143
144 pub fn output_sub(&mut self, output: &Output) -> Result<(), Error> {
146 match output {
147 Output::SignatureLockedSingle(output) => {
148 self.amount_sub(*output.address(), output.amount())?;
149 if output.amount() < DUST_THRESHOLD {
150 self.dust_outputs_dec(*output.address())?;
151 }
152 }
153 Output::SignatureLockedDustAllowance(output) => {
154 self.amount_sub(*output.address(), output.amount())?;
155 self.dust_allowance_sub(*output.address(), output.amount())?;
156 }
157 Output::Treasury(_) => return Err(Error::UnsupportedOutputKind(output.kind())),
158 }
159
160 Ok(())
161 }
162
163 pub fn amount_add(&mut self, address: Address, amount: u64) -> Result<(), Error> {
165 let entry = self.0.entry(address).or_default();
166 entry.amount = entry
167 .amount
168 .checked_add(amount as i64)
169 .ok_or(Error::BalanceDiffOverflow(entry.amount as i128 + amount as i128))?;
170 Ok(())
171 }
172
173 pub fn amount_sub(&mut self, address: Address, amount: u64) -> Result<(), Error> {
175 let entry = self.0.entry(address).or_default();
176 entry.amount = entry
177 .amount
178 .checked_sub(amount as i64)
179 .ok_or(Error::BalanceDiffOverflow(entry.amount as i128 + amount as i128))?;
180 Ok(())
181 }
182
183 pub fn dust_allowance_add(&mut self, address: Address, amount: u64) -> Result<(), Error> {
185 let entry = self.0.entry(address).or_default();
186 entry.dust_allowance = entry
187 .dust_allowance
188 .checked_add(amount as i64)
189 .ok_or(Error::BalanceDiffOverflow(
190 entry.dust_allowance as i128 + amount as i128,
191 ))?;
192 Ok(())
193 }
194
195 pub fn dust_allowance_sub(&mut self, address: Address, amount: u64) -> Result<(), Error> {
197 let entry = self.0.entry(address).or_default();
198 entry.dust_allowance = entry
199 .dust_allowance
200 .checked_sub(amount as i64)
201 .ok_or(Error::BalanceDiffOverflow(
202 entry.dust_allowance as i128 + amount as i128,
203 ))?;
204 Ok(())
205 }
206
207 pub fn dust_outputs_inc(&mut self, address: Address) -> Result<(), Error> {
209 let entry = self.0.entry(address).or_default();
210 entry.dust_outputs = entry
211 .dust_outputs
212 .checked_add(1)
213 .ok_or(Error::BalanceDiffOverflow(entry.dust_outputs as i128 + 1))?;
214 Ok(())
215 }
216
217 pub fn dust_outputs_dec(&mut self, address: Address) -> Result<(), Error> {
219 let entry = self.0.entry(address).or_default();
220 entry.dust_outputs = entry
221 .dust_outputs
222 .checked_sub(1)
223 .ok_or(Error::BalanceDiffOverflow(entry.dust_outputs as i128 + 1))?;
224 Ok(())
225 }
226
227 pub fn iter(&self) -> Iter<'_, Address, BalanceDiff> {
229 self.0.iter()
230 }
231
232 pub fn iter_mut(&mut self) -> IterMut<'_, Address, BalanceDiff> {
234 self.0.iter_mut()
235 }
236}
237
238impl IntoIterator for BalanceDiffs {
239 type Item = (Address, BalanceDiff);
240 type IntoIter = IntoIter<Address, BalanceDiff>;
241
242 fn into_iter(self) -> Self::IntoIter {
243 self.0.into_iter()
244 }
245}