use std::collections::{HashMap, HashSet};
use serde::{Serialize, Deserialize};
#[cfg(feature = "blockchain")]
use tokio::io::{Result as TokioResult};
use crate::utils::*;
use crate::schema::Schema;
use crate::coin::coin_order;
use crate::block::{Block, BlockInfo};
use crate::transaction::{Transaction, Type};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoinInfo {
pub owner: U256,
pub order: u64,
pub counter: u64,
}
pub type CoinInfoMap = HashMap<U256, CoinInfo>;
pub type OrderCoinsMap = HashMap<u64, HashSet<U256>>;
pub type OwnerCoinsMap = HashMap<U256, OrderCoinsMap>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct State {
coin_info_map: CoinInfoMap,
owner_coins_map: OwnerCoinsMap,
last_block_info: BlockInfo,
}
impl State {
pub fn new() -> Self {
Self {
coin_info_map: CoinInfoMap::new(),
owner_coins_map: OwnerCoinsMap::new(),
last_block_info: BlockInfo::genesis(),
}
}
#[cfg(feature = "blockchain")]
pub async fn load(path: &str) -> TokioResult<Self> {
let bytes = tokio::fs::read(path).await?;
let content = String::from_utf8(bytes).unwrap();
let instance = serde_json::from_str(&content)?;
Ok(instance)
}
#[cfg(feature = "blockchain")]
pub async fn dump(&self, path: &str) -> TokioResult<()> {
let content = serde_json::to_string(self).unwrap();
tokio::fs::write(path, content.as_bytes()).await
}
pub fn get_owner(&self, coin: &U256) -> Option<&U256> {
self.coin_info_map.get(coin).map(|cs| &cs.owner)
}
pub fn get_coin_info(&self, coin: &U256) -> Option<&CoinInfo> {
self.coin_info_map.get(coin)
}
pub fn get_coin_counter(&self, coin: &U256) -> u64 {
self.coin_info_map.get(coin).map(|cs| cs.counter).unwrap_or(0)
}
pub fn get_coins(&self, owner: &U256) -> Option<&OrderCoinsMap> {
self.owner_coins_map.get(owner)
}
pub fn calc_coins_hash(&self, owner: &U256, order: u64) -> Option<U256> {
let coins_map = self.owner_coins_map.get(owner)?;
if let Some(coins) = coins_map.get(&order) {
Some(coins.iter().fold(U256::from(0), |acc, num| &acc ^ &num))
} else {
Some(U256::from(0))
}
}
pub fn get_last_block_info(&self) -> &BlockInfo {
&self.last_block_info
}
pub fn roll_up(&mut self, bix: u64, block: &Block,
transactions: &[Transaction], schema: &Schema) {
assert_eq!(bix, self.last_block_info.bix + 1);
assert_eq!(block.offset, self.last_block_info.offset);
assert_eq!(block.hash_prev, self.last_block_info.hash);
let senders = Transaction::calc_senders(&transactions, self, &schema);
for (transaction, sender) in transactions.iter().zip(senders.iter()) {
let receiver = if transaction.get_type() == Type::Transfer {
&transaction.addr
} else {
&block.validator
};
if let Some(coin_info) = self.coin_info_map
.get_mut(&transaction.coin) {
coin_info.owner = receiver.clone();
coin_info.counter += 1;
self.owner_coin_remove(&sender, &transaction.coin);
self.owner_coin_add(&receiver, &transaction.coin);
} else {
let order = coin_order(&transaction.coin, &sender);
let coin_info = CoinInfo {
owner: receiver.clone(), order, counter: 1,
};
self.coin_info_map.insert(transaction.coin.clone(),
coin_info);
self.owner_coin_add(&receiver, &transaction.coin);
}
}
self.last_block_info.bix = bix;
self.last_block_info.offset += transactions.len() as u64;
self.last_block_info.hash = block.hash.clone();
}
pub fn roll_down(&mut self, bix: u64, block: &Block,
transactions: &[Transaction], schema: &Schema) {
assert_eq!(bix, self.last_block_info.bix);
assert_eq!(block.offset + transactions.len() as u64,
self.last_block_info.offset);
assert_eq!(block.hash, self.last_block_info.hash);
self.last_block_info.bix -= 1;
self.last_block_info.offset = block.offset;
self.last_block_info.hash = block.hash_prev.clone();
for transaction in transactions.iter() {
self.coin_info_map.get_mut(&transaction.coin).unwrap().counter -= 1;
}
let senders = Transaction::calc_senders(&transactions, self, &schema);
for (transaction, sender) in transactions.iter().zip(senders.iter()) {
let receiver = if transaction.get_type() == Type::Transfer {
&transaction.addr
} else {
&block.validator
};
let coin_info = self.coin_info_map.get_mut(&transaction.coin)
.unwrap();
if coin_info.counter == 0 {
self.owner_coin_remove(&receiver, &transaction.coin);
self.coin_info_map.remove(&transaction.coin);
} else {
coin_info.owner = sender.clone();
self.owner_coin_remove(&receiver, &transaction.coin);
self.owner_coin_add(&sender, &transaction.coin);
}
}
}
fn owner_coin_add(&mut self, owner: &U256, coin: &U256) {
let order = self.coin_info_map[coin].order;
if !self.owner_coins_map.contains_key(owner) {
self.owner_coins_map.insert(owner.clone(), HashMap::new());
}
if !self.owner_coins_map[owner].contains_key(&order) {
self.owner_coins_map.get_mut(owner).unwrap()
.insert(order, HashSet::new());
}
self.owner_coins_map.get_mut(owner).unwrap()
.get_mut(&order).unwrap().insert(coin.clone());
}
fn owner_coin_remove(&mut self, owner: &U256, coin: &U256) {
let order = self.coin_info_map[coin].order;
let coins_map = self.owner_coins_map.get_mut(owner).unwrap();
coins_map.get_mut(&order).unwrap().remove(coin);
if self.owner_coins_map[owner][&order].is_empty() {
self.owner_coins_map.get_mut(owner).unwrap().remove(&order);
}
if self.owner_coins_map[owner].is_empty() {
self.owner_coins_map.remove(owner);
}
}
}