use std::collections::HashMap;
use std::sync::Arc;
use ed25519_compact::PublicKey;
use thiserror::Error;
use crate::ledger::LedgerError;
use crate::ledger_disk::LedgerDisk;
use crate::transaction::Transaction;
pub struct BalanceCache {
ledger: Arc<LedgerDisk>,
min_balance: u64,
cache: HashMap<PublicKey, u64>,
}
impl BalanceCache {
pub fn new(ledger: Arc<LedgerDisk>, min_balance: u64) -> Self {
BalanceCache {
ledger,
min_balance,
cache: HashMap::new(),
}
}
pub fn reset(&mut self) {
self.cache = HashMap::new();
}
pub fn apply(&mut self, tx: &Transaction) -> Result<bool, BalanceCacheError> {
if !tx.is_coinbase() {
let fpk = tx.from.expect("transaction should have a sender");
let mut sender_balance = match self.cache.get(&fpk).copied() {
Some(v) => v,
None => self.ledger.get_public_key_balance(&fpk)?,
};
let total_spent = tx.amount + tx.fee.unwrap_or(0);
if total_spent > sender_balance {
return Ok(false);
}
sender_balance -= total_spent;
if sender_balance > 0 && sender_balance < self.min_balance {
return Ok(false);
}
self.cache.insert(fpk, sender_balance);
}
let tpk = tx.to;
let mut recipient_balance = match self.cache.get(&tpk).copied() {
Some(v) => v,
None => self.ledger.get_public_key_balance(&tx.to)?,
};
recipient_balance += tx.amount;
self.cache.insert(tpk, recipient_balance);
Ok(true)
}
pub fn undo(&mut self, tx: &Transaction) -> Result<(), BalanceCacheError> {
if !tx.is_coinbase() {
let fpk = tx.from.expect("transaction should have a sender");
let mut sender_balance = match self.cache.get(&fpk).copied() {
Some(v) => v,
None => self.ledger.get_public_key_balance(&fpk)?,
};
let total_spent = tx.amount + tx.fee.unwrap_or(0);
sender_balance += total_spent;
self.cache.insert(fpk, sender_balance);
}
let tpk = tx.to;
let recipient_balance = match self.cache.get(&tpk).copied() {
Some(v) => v,
None => self.ledger.get_public_key_balance(&tpk)?,
};
if recipient_balance < tx.amount {
panic!("Recipient balance went negative")
}
self.cache.insert(tpk, recipient_balance - tx.amount);
Ok(())
}
pub fn balances(&self) -> &HashMap<PublicKey, u64> {
&self.cache
}
}
#[derive(Error, Debug)]
pub enum BalanceCacheError {
#[error("ledger")]
Ledger(#[from] LedgerError),
}