Add accountant
This commit is contained in:
parent
6aa4e52480
commit
5ca0ccbcd2
|
@ -0,0 +1,129 @@
|
|||
//! The `accountant` is a client of the `historian`. It uses the historian's
|
||||
//! event log to record transactions. Its users can deposit funds and
|
||||
//! transfer funds to other users.
|
||||
|
||||
use log::{verify_entry, Event, PublicKey, Sha256Hash};
|
||||
use historian::Historian;
|
||||
use ring::signature::Ed25519KeyPair;
|
||||
use std::sync::mpsc::{RecvError, SendError};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Accountant {
|
||||
pub historian: Historian<u64>,
|
||||
pub balances: HashMap<PublicKey, u64>,
|
||||
pub end_hash: Sha256Hash,
|
||||
}
|
||||
|
||||
impl Accountant {
|
||||
pub fn new(start_hash: &Sha256Hash, ms_per_tick: Option<u64>) -> Self {
|
||||
let hist = Historian::<u64>::new(start_hash, ms_per_tick);
|
||||
Accountant {
|
||||
historian: hist,
|
||||
balances: HashMap::new(),
|
||||
end_hash: *start_hash,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_event(self: &mut Self, event: Event<u64>) {
|
||||
match event {
|
||||
Event::Claim { key, data, .. } => {
|
||||
if self.balances.contains_key(&key) {
|
||||
if let Some(x) = self.balances.get_mut(&key) {
|
||||
*x += data;
|
||||
}
|
||||
} else {
|
||||
self.balances.insert(key, data);
|
||||
}
|
||||
}
|
||||
Event::Transaction { from, to, data, .. } => {
|
||||
if let Some(x) = self.balances.get_mut(&from) {
|
||||
*x -= data;
|
||||
}
|
||||
if self.balances.contains_key(&to) {
|
||||
if let Some(x) = self.balances.get_mut(&to) {
|
||||
*x += data;
|
||||
}
|
||||
} else {
|
||||
self.balances.insert(to, data);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync(self: &mut Self) {
|
||||
while let Ok(entry) = self.historian.receiver.try_recv() {
|
||||
assert!(verify_entry(&entry, &self.end_hash));
|
||||
self.end_hash = entry.end_hash;
|
||||
|
||||
self.process_event(entry.event);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deposit(
|
||||
self: &Self,
|
||||
n: u64,
|
||||
keypair: &Ed25519KeyPair,
|
||||
) -> Result<(), SendError<Event<u64>>> {
|
||||
use log::sign_hash;
|
||||
let event = sign_hash(n, &keypair);
|
||||
self.historian.sender.send(event)
|
||||
}
|
||||
|
||||
pub fn transfer(
|
||||
self: &mut Self,
|
||||
n: u64,
|
||||
keypair: &Ed25519KeyPair,
|
||||
pubkey: PublicKey,
|
||||
) -> Result<(), SendError<Event<u64>>> {
|
||||
use log::transfer_hash;
|
||||
use generic_array::GenericArray;
|
||||
|
||||
let sender_pubkey = GenericArray::clone_from_slice(keypair.public_key_bytes());
|
||||
if self.get_balance(&sender_pubkey).unwrap() >= n {
|
||||
let event = transfer_hash(n, keypair, pubkey);
|
||||
return self.historian.sender.send(event);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_balance(self: &mut Self, pubkey: &PublicKey) -> Result<u64, RecvError> {
|
||||
self.sync();
|
||||
Ok(*self.balances.get(pubkey).unwrap_or(&0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use log::generate_keypair;
|
||||
use historian::ExitReason;
|
||||
use generic_array::GenericArray;
|
||||
|
||||
#[test]
|
||||
fn test_accountant() {
|
||||
let zero = Sha256Hash::default();
|
||||
|
||||
let mut acc = Accountant::new(&zero, Some(2));
|
||||
|
||||
let alice_keypair = generate_keypair();
|
||||
let bob_keypair = generate_keypair();
|
||||
acc.deposit(10_000, &alice_keypair).unwrap();
|
||||
acc.deposit(1_000, &bob_keypair).unwrap();
|
||||
|
||||
let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes());
|
||||
acc.transfer(500, &alice_keypair, bob_pubkey).unwrap();
|
||||
|
||||
sleep(Duration::new(0, 1_000_000));
|
||||
|
||||
assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500);
|
||||
|
||||
drop(acc.historian.sender);
|
||||
assert_eq!(
|
||||
acc.historian.thread_hdl.join().unwrap().1,
|
||||
ExitReason::RecvDisconnected
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#![cfg_attr(feature = "unstable", feature(test))]
|
||||
pub mod log;
|
||||
pub mod historian;
|
||||
pub mod accountant;
|
||||
extern crate bincode;
|
||||
extern crate generic_array;
|
||||
extern crate rayon;
|
||||
|
|
Loading…
Reference in New Issue