solana/src/accountant.rs

129 lines
3.8 KiB
Rust
Raw Normal View History

2018-02-23 13:08:19 -08:00
//! 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();
2018-02-27 10:08:28 -08:00
sleep(Duration::from_millis(30));
2018-02-23 13:08:19 -08:00
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
);
}
}