use crate::storage::{DataMap, Map, MapId, Storage};
use snarkvm::dpc::prelude::*;
use anyhow::{anyhow, Result};
use std::{
collections::{HashMap, HashSet},
iter::FromIterator,
path::Path,
};
#[derive(Debug)]
pub struct OperatorState<N: Network> {
shares: SharesState<N>,
}
impl<N: Network> OperatorState<N> {
pub fn open_writer<S: Storage, P: AsRef<Path>>(path: P) -> Result<Self> {
let context = N::NETWORK_ID;
let is_read_only = false;
let storage = S::open(path, context, is_read_only)?;
let operator = Self {
shares: SharesState::open(storage)?,
};
info!("Operator successfully initialized");
Ok(operator)
}
pub fn to_shares(&self) -> Vec<((u32, Record<N>), HashMap<Address<N>, u64>)> {
self.shares.to_shares()
}
pub fn to_coinbase_records(&self) -> Vec<(u32, Record<N>)> {
self.shares.to_records()
}
pub fn get_shares_for_block(&self, block_height: u32, coinbase_record: Record<N>) -> Result<HashMap<Address<N>, u64>> {
self.shares.get_shares_for_block(block_height, coinbase_record)
}
pub fn get_shares_for_prover(&self, prover: &Address<N>) -> u64 {
self.shares.get_shares_for_prover(prover)
}
pub fn increment_share(&self, block_height: u32, coinbase_record: Record<N>, prover: &Address<N>) -> Result<()> {
self.shares.increment_share(block_height, coinbase_record, prover)
}
pub fn remove_shares(&self, block_height: u32, coinbase_record: Record<N>) -> Result<()> {
self.shares.remove_shares(block_height, coinbase_record)
}
pub fn get_provers(&self) -> Vec<Address<N>> {
self.shares.get_provers()
}
}
#[derive(Clone, Debug)]
#[allow(clippy::type_complexity)]
struct SharesState<N: Network> {
shares: DataMap<(u32, Record<N>), HashMap<Address<N>, u64>>,
}
impl<N: Network> SharesState<N> {
fn open<S: Storage>(storage: S) -> Result<Self> {
Ok(Self {
shares: storage.open_map(MapId::Shares)?,
})
}
fn to_shares(&self) -> Vec<((u32, Record<N>), HashMap<Address<N>, u64>)> {
self.shares.iter().collect()
}
fn to_records(&self) -> Vec<(u32, Record<N>)> {
self.shares.keys().collect()
}
fn get_shares_for_block(&self, block_height: u32, coinbase_record: Record<N>) -> Result<HashMap<Address<N>, u64>> {
match self.shares.get(&(block_height, coinbase_record))? {
Some(shares) => Ok(shares),
None => return Err(anyhow!("Block {} does not exist in shares storage", block_height)),
}
}
fn get_shares_for_prover(&self, prover: &Address<N>) -> u64 {
self.shares.iter().filter_map(|((_, _), shares)| shares.get(prover).copied()).sum()
}
fn increment_share(&self, block_height: u32, coinbase_record: Record<N>, prover: &Address<N>) -> Result<()> {
let mut shares = match self.shares.get(&(block_height, coinbase_record.clone()))? {
Some(shares) => shares,
None => HashMap::new(),
};
let entry = shares.entry(*prover).or_insert(0);
*entry = entry.saturating_add(1);
self.shares.insert(&(block_height, coinbase_record), &shares)
}
fn remove_shares(&self, block_height: u32, coinbase_record: Record<N>) -> Result<()> {
self.shares.remove(&(block_height, coinbase_record))
}
fn get_provers(&self) -> Vec<Address<N>> {
let set: HashSet<Address<N>> = self
.shares
.iter()
.flat_map(|((_, _), shares)| shares.keys().copied().collect::<Vec<_>>())
.collect();
Vec::from_iter(set)
}
}