use crate::types::Error;
use bee_message::{
address::Address,
constants::IOTA_SUPPLY,
output::{Output, DUST_THRESHOLD},
};
use std::collections::{
hash_map::{IntoIter, Iter, IterMut},
HashMap,
};
#[derive(Clone, Debug, Default)]
pub struct BalanceDiff {
amount: i64,
dust_allowance: i64,
dust_outputs: i64,
}
impl BalanceDiff {
pub fn new(amount: i64, dust_allowance: i64, dust_outputs: i64) -> Result<Self, Error> {
if amount.abs() as u64 > IOTA_SUPPLY {
Err(Error::InvalidBalanceDiff(amount))
} else if dust_allowance.abs() as u64 > IOTA_SUPPLY {
Err(Error::InvalidBalanceDiff(dust_allowance))
} else if dust_outputs.abs() as u64 > IOTA_SUPPLY {
Err(Error::InvalidBalanceDiff(dust_outputs))
} else {
Ok(Self {
amount,
dust_allowance,
dust_outputs,
})
}
}
pub fn amount(&self) -> i64 {
self.amount
}
pub fn dust_allowance(&self) -> i64 {
self.dust_allowance
}
pub fn dust_outputs(&self) -> i64 {
self.dust_outputs
}
pub fn is_dust_mutating(&self) -> bool {
self.dust_allowance < 0 || self.dust_outputs > 0
}
pub fn negate(&mut self) {
self.amount = -self.amount;
self.dust_allowance = -self.dust_allowance;
self.dust_outputs = -self.dust_outputs;
}
}
#[derive(Clone, Debug, Default)]
pub struct BalanceDiffs(HashMap<Address, BalanceDiff>);
impl BalanceDiffs {
pub fn new() -> Self {
Self::default()
}
pub fn merge(&mut self, other: Self) -> Result<(), Error> {
for (address, diff) in other.0 {
let e = self.0.entry(address).or_default();
e.amount = e
.amount
.checked_add(diff.amount)
.ok_or(Error::BalanceDiffOverflow(e.amount as i128 + diff.amount as i128))?;
e.dust_allowance = e
.dust_allowance
.checked_add(diff.dust_allowance)
.ok_or(Error::BalanceDiffOverflow(
e.dust_allowance as i128 + diff.dust_allowance as i128,
))?;
e.dust_outputs = e
.dust_outputs
.checked_add(diff.dust_outputs)
.ok_or(Error::BalanceDiffOverflow(
e.dust_outputs as i128 + diff.dust_outputs as i128,
))?;
}
Ok(())
}
pub fn get(&self, address: &Address) -> Option<&BalanceDiff> {
self.0.get(address)
}
pub fn negate(&mut self) {
for (_, diff) in self.iter_mut() {
diff.negate();
}
}
pub fn negated(&self) -> Self {
let mut new = self.clone();
new.negate();
new
}
pub fn output_add(&mut self, output: &Output) -> Result<(), Error> {
match output {
Output::SignatureLockedSingle(output) => {
self.amount_add(*output.address(), output.amount())?;
if output.amount() < DUST_THRESHOLD {
self.dust_outputs_inc(*output.address())?;
}
}
Output::SignatureLockedDustAllowance(output) => {
self.amount_add(*output.address(), output.amount())?;
self.dust_allowance_add(*output.address(), output.amount())?;
}
Output::Treasury(_) => return Err(Error::UnsupportedOutputKind(output.kind())),
}
Ok(())
}
pub fn output_sub(&mut self, output: &Output) -> Result<(), Error> {
match output {
Output::SignatureLockedSingle(output) => {
self.amount_sub(*output.address(), output.amount())?;
if output.amount() < DUST_THRESHOLD {
self.dust_outputs_dec(*output.address())?;
}
}
Output::SignatureLockedDustAllowance(output) => {
self.amount_sub(*output.address(), output.amount())?;
self.dust_allowance_sub(*output.address(), output.amount())?;
}
Output::Treasury(_) => return Err(Error::UnsupportedOutputKind(output.kind())),
}
Ok(())
}
pub fn amount_add(&mut self, address: Address, amount: u64) -> Result<(), Error> {
let entry = self.0.entry(address).or_default();
entry.amount = entry
.amount
.checked_add(amount as i64)
.ok_or(Error::BalanceDiffOverflow(entry.amount as i128 + amount as i128))?;
Ok(())
}
pub fn amount_sub(&mut self, address: Address, amount: u64) -> Result<(), Error> {
let entry = self.0.entry(address).or_default();
entry.amount = entry
.amount
.checked_sub(amount as i64)
.ok_or(Error::BalanceDiffOverflow(entry.amount as i128 + amount as i128))?;
Ok(())
}
pub fn dust_allowance_add(&mut self, address: Address, amount: u64) -> Result<(), Error> {
let entry = self.0.entry(address).or_default();
entry.dust_allowance = entry
.dust_allowance
.checked_add(amount as i64)
.ok_or(Error::BalanceDiffOverflow(
entry.dust_allowance as i128 + amount as i128,
))?;
Ok(())
}
pub fn dust_allowance_sub(&mut self, address: Address, amount: u64) -> Result<(), Error> {
let entry = self.0.entry(address).or_default();
entry.dust_allowance = entry
.dust_allowance
.checked_sub(amount as i64)
.ok_or(Error::BalanceDiffOverflow(
entry.dust_allowance as i128 + amount as i128,
))?;
Ok(())
}
pub fn dust_outputs_inc(&mut self, address: Address) -> Result<(), Error> {
let entry = self.0.entry(address).or_default();
entry.dust_outputs = entry
.dust_outputs
.checked_add(1)
.ok_or(Error::BalanceDiffOverflow(entry.dust_outputs as i128 + 1))?;
Ok(())
}
pub fn dust_outputs_dec(&mut self, address: Address) -> Result<(), Error> {
let entry = self.0.entry(address).or_default();
entry.dust_outputs = entry
.dust_outputs
.checked_sub(1)
.ok_or(Error::BalanceDiffOverflow(entry.dust_outputs as i128 + 1))?;
Ok(())
}
pub fn iter(&self) -> Iter<'_, Address, BalanceDiff> {
self.0.iter()
}
pub fn iter_mut(&mut self) -> IterMut<'_, Address, BalanceDiff> {
self.0.iter_mut()
}
}
impl IntoIterator for BalanceDiffs {
type Item = (Address, BalanceDiff);
type IntoIter = IntoIter<Address, BalanceDiff>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}