use std::mem;
use base::Result;
use base::Checkable;
use base::Datable;
use base::Serializable;
use base::{Sizable, ConstantSize};
use base::{Eval, EvalMut};
use base::Numerical;
use base::Meta;
use crypto::Hash;
use io::{Store, Storable};
use model::Coin;
pub const WALLET_CODE: u64 = 7;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash, Serialize, Deserialize)]
pub struct Wallet<D, A, P>
where D: Ord + Datable + ConstantSize,
A: Numerical,
P: Datable
{
pub id: D,
pub meta: Meta,
pub spent_coins_len: u64,
pub spent_coins: Vec<Coin<D, A>>,
pub unspent_coins_len: u64,
pub unspent_coins: Vec<Coin<D, A>>,
pub payload: P,
}
impl<D, A, P> Wallet<D, A, P>
where D: Ord + Datable + ConstantSize,
P: Datable,
A: Numerical,
Self: Serializable
{
pub fn new() -> Self {
let mut wallet = Wallet::default();
wallet.update_size();
wallet
}
pub fn update_size(&mut self) {
let size = self.size();
self.meta.set_size(size);
}
pub fn meta(mut self, meta: &Meta) -> Result<Self> {
meta.check()?;
self.meta = meta.clone();
self.update_size();
Ok(self)
}
pub fn spent_coins(mut self, spent_coins: &Vec<Coin<D, A>>) -> Result<Self> {
spent_coins.check()?;
let mut unique_spent_coins = spent_coins.clone();
unique_spent_coins.dedup_by(|a, b| { a == b });
if unique_spent_coins.len() != spent_coins.len() {
return Err(format!("duplicates found"));
}
for ref coin in spent_coins {
if self.unspent_coins.contains(&coin) {
return Err(format!("spent and unpent not disjunct"))
}
}
self.spent_coins_len = spent_coins.len() as u64;
self.spent_coins = spent_coins.clone();
self.update_size();
Ok(self)
}
pub fn add_spent_coin(&mut self, spent_coin: &Coin<D, A>) -> Result<()> {
spent_coin.check()?;
if self.spent_coins.contains(spent_coin) {
return Err(format!("already found"));
}
if self.unspent_coins.contains(spent_coin) {
return Err(format!("already found"));
}
self.spent_coins.push(spent_coin.to_owned());
self.spent_coins_len += 1;
Ok(())
}
pub fn del_spent_coin(&mut self, spent_coin: &Coin<D, A>) -> Result<()> {
spent_coin.check()?;
if !self.spent_coins.contains(spent_coin) {
return Err(format!("not found"));
}
if self.unspent_coins.contains(spent_coin) {
return Err(format!("already found"));
}
let idx = self.spent_coins.binary_search(spent_coin)
.map_err(|e| format!("{:?}", e))?;
self.spent_coins.remove(idx);
self.spent_coins_len -= 1;
Ok(())
}
pub fn unspent_coins(mut self, unspent_coins: &Vec<Coin<D, A>>) -> Result<Self> {
unspent_coins.check()?;
let mut unique_unspent_coins = unspent_coins.clone();
unique_unspent_coins.dedup_by(|a, b| { a == b });
if unique_unspent_coins.len() != unspent_coins.len() {
return Err(format!("duplicates found"));
}
for ref coin in unspent_coins {
if self.spent_coins.contains(&coin) {
return Err(format!("spent and unpent not disjunct"))
}
}
self.unspent_coins_len = unspent_coins.len() as u64;
self.unspent_coins = unspent_coins.clone();
self.update_size();
Ok(self)
}
pub fn add_unspent_coin(&mut self, unspent_coin: &Coin<D, A>) -> Result<()> {
unspent_coin.check()?;
if self.unspent_coins.contains(unspent_coin) {
return Err(format!("already found"));
}
if self.spent_coins.contains(unspent_coin) {
return Err(format!("already found"));
}
self.unspent_coins.push(unspent_coin.to_owned());
self.unspent_coins_len += 1;
Ok(())
}
pub fn del_unspent_coin(&mut self, unspent_coin: &Coin<D, A>) -> Result<()> {
unspent_coin.check()?;
if !self.unspent_coins.contains(unspent_coin) {
return Err(format!("not found"));
}
if self.spent_coins.contains(unspent_coin) {
return Err(format!("already found"));
}
let idx = self.unspent_coins.binary_search(unspent_coin)
.map_err(|e| format!("{:?}", e))?;
self.unspent_coins.remove(idx);
self.unspent_coins_len -= 1;
Ok(())
}
pub fn spend_coin(&mut self, unspent_coin: &Coin<D, A>) -> Result<()> {
self.del_unspent_coin(unspent_coin)?;
self.add_spent_coin(unspent_coin)?;
Ok(())
}
pub fn payload(mut self, payload: &P) -> Result<Self> {
payload.check()?;
self.payload = payload.clone();
self.update_size();
Ok(self)
}
pub fn finalize<H: Hash<D>>(mut self, hasher: &mut H) -> Result<Self> {
let msg = self.to_bytes()?;
self.id = hasher.digest(&msg)?;
self.update_size();
self.check()?;
Ok(self)
}
pub fn digest<H: Hash<D>>(&self, hasher: &mut H) -> Result<D> {
let mut wallet = self.clone();
wallet.id = D::default();
wallet.update_size();
let msg = wallet.to_bytes()?;
hasher.digest(&msg)
}
pub fn verify_digest<H: Hash<D>>(&self, hasher: &mut H) -> Result<bool> {
let digest = self.id.clone();
digest.check()?;
let mut wallet = self.clone();
wallet.id = D::default();
wallet.update_size();
let msg = wallet.to_bytes()?;
hasher.verify(&msg, &digest)
}
pub fn check_digest<H: Hash<D>>(&self, hasher: &mut H) -> Result<()> {
let digest = self.id.clone();
digest.check()?;
let mut wallet = self.clone();
wallet.id = D::default();
wallet.update_size();
let msg = wallet.to_bytes()?;
hasher.check(&msg, &digest)
}
pub fn eval<Ev, EP, ER>(&self, params: &EP, evaluator: &Ev)
-> Result<ER>
where Ev: Eval<Self, EP, ER>,
EP: Datable,
ER: Datable
{
self.check()?;
params.check()?;
evaluator.eval(self, params)
}
pub fn eval_mut<EvM, EP, ER>(&mut self, params: &EP, evaluator: &mut EvM)
-> Result<ER>
where EvM: EvalMut<Self, EP, ER>,
EP: Datable,
ER: Datable
{
self.check()?;
params.check()?;
let result = evaluator.eval_mut(self, params)?;
self.update_size();
self.check()?;
Ok(result)
}
}
impl<D, A, P> Sizable for Wallet<D, A, P>
where D: Ord + Datable + ConstantSize,
A: Numerical,
P: Datable
{
fn size(&self) -> u64 {
self.id.size() +
self.meta.size() +
self.payload.size()
}
}
impl<D, A, P> Checkable for Wallet<D, A, P>
where D: Ord + Datable + ConstantSize,
A: Numerical,
P: Datable
{
fn check(&self) -> Result<()> {
self.id.check()?;
self.id.check_size()?;
self.meta.check()?;
if self.meta.get_size() != self.size() {
return Err(String::from("invalid meta size"));
}
self.spent_coins_len.check()?;
self.spent_coins.check()?;
if self.spent_coins.len() != self.spent_coins_len as usize {
return Err(String::from("invalid spent coins length"));
}
let mut unique_spent_coins = self.spent_coins.clone();
unique_spent_coins.dedup_by(|a, b| { a == b });
if unique_spent_coins.len() != self.spent_coins.len() {
return Err(format!("duplicates found"));
}
self.unspent_coins_len.check()?;
self.unspent_coins.check()?;
if self.unspent_coins.len() != self.unspent_coins_len as usize {
return Err(String::from("invalid unspent coins length"));
}
let mut unique_unspent_coins = self.unspent_coins.clone();
unique_unspent_coins.dedup_by(|a, b| { a == b });
if unique_unspent_coins.len() != self.unspent_coins.len() {
return Err(format!("duplicates found"));
}
for ref coin in self.spent_coins.iter() {
if self.unspent_coins.contains(&coin) {
return Err(format!("spent and unpent not disjunct"))
}
}
for ref coin in self.unspent_coins.iter() {
if self.spent_coins.contains(&coin) {
return Err(format!("spent and unpent not disjunct"))
}
}
self.payload.check()?;
Ok(())
}
}
impl<D, A, P> Serializable for Wallet<D, A, P>
where D: Ord + Datable + ConstantSize + Serializable,
A: Numerical + Serializable,
P: Datable + Serializable
{}
impl<D, A, P> Datable for Wallet<D, A, P>
where D: Ord + Datable + ConstantSize,
A: Numerical,
P: Datable
{}
impl<St, S, D, A, P>
Storable<St, S, D, Wallet<D, A, P>>
for Wallet<D, A, P>
where St: Store<S>,
S: Datable + Serializable,
D: Ord + Datable + ConstantSize + Serializable,
A: Numerical + Serializable,
P: Datable + Serializable
{
fn store_prefix() -> Vec<u8> {
let mut prefix = Vec::new();
let _prefix: [u8; 8] = unsafe { mem::transmute(WALLET_CODE) };
prefix.extend_from_slice(&_prefix[..]);
prefix
}
fn store_key(&self) -> Result<D> {
self.id.check()?;
Ok(self.id.clone())
}
fn store_value(&self) -> Result<Self> {
self.check()?;
Ok(self.clone())
}
}