Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add testnode and client-demo #26

Merged
merged 4 commits into from
Mar 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "silk"
description = "A silky smooth implementation of the Loom architecture"
version = "0.2.3"
version = "0.3.0"
documentation = "https://docs.rs/silk"
homepage = "http://loomprotocol.com/"
repository = "https://github.com/loomprotocol/silk"
Expand All @@ -15,6 +15,14 @@ license = "Apache-2.0"
name = "silk-demo"
path = "src/bin/demo.rs"

[[bin]]
name = "silk-client-demo"
path = "src/bin/client-demo.rs"

[[bin]]
name = "silk-testnode"
path = "src/bin/testnode.rs"

[badges]
codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" }

Expand Down
88 changes: 62 additions & 26 deletions src/accountant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! event log to record transactions. Its users can deposit funds and
//! transfer funds to other users.

use log::{verify_entry, Event, PublicKey, Sha256Hash};
use log::{Event, PublicKey, Sha256Hash, Signature};
use historian::Historian;
use ring::signature::Ed25519KeyPair;
use std::sync::mpsc::{RecvError, SendError};
Expand All @@ -24,8 +24,8 @@ impl Accountant {
}
}

pub fn process_event(self: &mut Self, event: Event<u64>) {
match event {
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) {
Expand All @@ -52,39 +52,76 @@ impl Accountant {
}

pub fn sync(self: &mut Self) {
let mut entries = vec![];
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);
entries.push(entry);
}
// TODO: Does this cause the historian's channel to get blocked?
//use log::verify_slice_u64;
//println!("accountant: verifying {} entries...", entries.len());
//assert!(verify_slice_u64(&entries, &self.end_hash));
//println!("accountant: Done verifying {} entries.", entries.len());
if let Some(last_entry) = entries.last() {
self.end_hash = last_entry.end_hash;
}
for e in &entries {
self.process_event(&e.event);
}
}

pub fn deposit_signed(
self: &Self,
key: PublicKey,
data: u64,
sig: Signature,
) -> Result<(), SendError<Event<u64>>> {
let event = Event::Claim { key, data, sig };
self.historian.sender.send(event)
}

pub fn deposit(
self: &Self,
n: u64,
keypair: &Ed25519KeyPair,
) -> Result<(), SendError<Event<u64>>> {
use log::sign_hash;
let event = sign_hash(n, &keypair);
use log::{get_pubkey, sign_serialized};
let key = get_pubkey(keypair);
let sig = sign_serialized(&n, keypair);
self.deposit_signed(key, n, sig)
}

pub fn transfer_signed(
self: &mut Self,
from: PublicKey,
to: PublicKey,
data: u64,
sig: Signature,
) -> Result<(), SendError<Event<u64>>> {
if self.get_balance(&from).unwrap() < data {
// TODO: Replace the SendError result with a custom one.
println!("Error: Insufficient funds");
return Ok(());
}
let event = Event::Transaction {
from,
to,
data,
sig,
};
self.historian.sender.send(event)
}

pub fn transfer(
self: &mut Self,
n: u64,
keypair: &Ed25519KeyPair,
pubkey: PublicKey,
to: PublicKey,
) -> Result<(), SendError<Event<u64>>> {
use log::transfer_hash;
use generic_array::GenericArray;
use log::{get_pubkey, sign_transaction_data};

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(())
let from = get_pubkey(keypair);
let sig = sign_transaction_data(&n, keypair, &to);
self.transfer_signed(from, to, n, sig)
}

pub fn get_balance(self: &mut Self, pubkey: &PublicKey) -> Result<u64, RecvError> {
Expand All @@ -98,9 +135,8 @@ mod tests {
use super::*;
use std::thread::sleep;
use std::time::Duration;
use log::generate_keypair;
use log::{generate_keypair, get_pubkey};
use historian::ExitReason;
use generic_array::GenericArray;

#[test]
fn test_accountant() {
Expand All @@ -112,7 +148,7 @@ mod tests {
acc.deposit(1_000, &bob_keypair).unwrap();

sleep(Duration::from_millis(30));
let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes());
let bob_pubkey = get_pubkey(&bob_keypair);
acc.transfer(500, &alice_keypair, bob_pubkey).unwrap();

sleep(Duration::from_millis(30));
Expand All @@ -135,11 +171,11 @@ mod tests {
acc.deposit(1_000, &bob_keypair).unwrap();

sleep(Duration::from_millis(30));
let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes());
let bob_pubkey = get_pubkey(&bob_keypair);
acc.transfer(10_001, &alice_keypair, bob_pubkey).unwrap();

sleep(Duration::from_millis(30));
let alice_pubkey = GenericArray::clone_from_slice(alice_keypair.public_key_bytes());
let alice_pubkey = get_pubkey(&alice_keypair);
assert_eq!(acc.get_balance(&alice_pubkey).unwrap(), 10_000);
assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_000);

Expand All @@ -151,14 +187,14 @@ mod tests {
}

#[test]
fn test_mulitple_claims() {
fn test_multiple_claims() {
let zero = Sha256Hash::default();
let mut acc = Accountant::new(&zero, Some(2));
let keypair = generate_keypair();
acc.deposit(1, &keypair).unwrap();
acc.deposit(2, &keypair).unwrap();

let pubkey = GenericArray::clone_from_slice(keypair.public_key_bytes());
let pubkey = get_pubkey(&keypair);
sleep(Duration::from_millis(30));
assert_eq!(acc.get_balance(&pubkey).unwrap(), 3);

Expand All @@ -178,7 +214,7 @@ mod tests {
acc.deposit(10_000, &alice_keypair).unwrap();

sleep(Duration::from_millis(30));
let bob_pubkey = GenericArray::clone_from_slice(bob_keypair.public_key_bytes());
let bob_pubkey = get_pubkey(&bob_keypair);
acc.transfer(500, &alice_keypair, bob_pubkey).unwrap();

sleep(Duration::from_millis(30));
Expand Down
75 changes: 75 additions & 0 deletions src/accountant_skel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::io;
use accountant::Accountant;
use log::{PublicKey, Signature};
//use serde::Serialize;

pub struct AccountantSkel {
pub obj: Accountant,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum Request {
Deposit {
key: PublicKey,
val: u64,
sig: Signature,
},
Transfer {
from: PublicKey,
to: PublicKey,
val: u64,
sig: Signature,
},
GetBalance {
key: PublicKey,
},
}

#[derive(Serialize, Deserialize, Debug)]
pub enum Response {
Balance { key: PublicKey, val: u64 },
}

impl AccountantSkel {
pub fn new(obj: Accountant) -> Self {
AccountantSkel { obj }
}

pub fn process_request(self: &mut Self, msg: Request) -> Option<Response> {
match msg {
Request::Deposit { key, val, sig } => {
let _ = self.obj.deposit_signed(key, val, sig);
None
}
Request::Transfer { from, to, val, sig } => {
let _ = self.obj.transfer_signed(from, to, val, sig);
None
}
Request::GetBalance { key } => {
let val = self.obj.get_balance(&key).unwrap();
Some(Response::Balance { key, val })
}
}
}

/// TCP Server that forwards messages to Accountant methods.
pub fn serve(self: &mut Self, addr: &str) -> io::Result<()> {
use std::net::TcpListener;
use std::io::{Read, Write};
use bincode::{deserialize, serialize};
let listener = TcpListener::bind(addr)?;
let mut buf = vec![0u8; 1024];
loop {
//println!("skel: Waiting for incoming connections...");
let (mut stream, _from_addr) = listener.accept()?;
let _sz = stream.read(&mut buf)?;

// TODO: Return a descriptive error message if deserialization fails.
let req = deserialize(&buf).expect("deserialize request");

if let Some(resp) = self.process_request(req) {
stream.write(&serialize(&resp).expect("serialize response"))?;
}
}
}
}
116 changes: 116 additions & 0 deletions src/accountant_stub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! 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 std::net::TcpStream;
use std::io;
use std::io::{Read, Write};
use bincode::{deserialize, serialize};
use log::{PublicKey, Signature};
use ring::signature::Ed25519KeyPair;
use accountant_skel::{Request, Response};

pub struct AccountantStub {
pub addr: String,
}

impl AccountantStub {
pub fn new(addr: &str) -> Self {
AccountantStub {
addr: addr.to_string(),
}
}

pub fn deposit_signed(
self: &mut Self,
key: PublicKey,
val: u64,
sig: Signature,
) -> io::Result<usize> {
let req = Request::Deposit { key, val, sig };
let data = serialize(&req).unwrap();
let mut stream = TcpStream::connect(&self.addr)?;
stream.write(&data)
}

pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> io::Result<usize> {
use log::{get_pubkey, sign_serialized};
let key = get_pubkey(keypair);
let sig = sign_serialized(&n, keypair);
self.deposit_signed(key, n, sig)
}

pub fn transfer_signed(
self: &mut Self,
from: PublicKey,
to: PublicKey,
val: u64,
sig: Signature,
) -> io::Result<usize> {
let req = Request::Transfer { from, to, val, sig };
let data = serialize(&req).unwrap();
let mut stream = TcpStream::connect(&self.addr)?;
stream.write(&data)
}

pub fn transfer(
self: &mut Self,
n: u64,
keypair: &Ed25519KeyPair,
to: PublicKey,
) -> io::Result<usize> {
use log::{get_pubkey, sign_transaction_data};
let from = get_pubkey(keypair);
let sig = sign_transaction_data(&n, keypair, &to);
self.transfer_signed(from, to, n, sig)
}

pub fn get_balance(self: &mut Self, pubkey: &PublicKey) -> io::Result<u64> {
let mut stream = TcpStream::connect(&self.addr)?;
let req = Request::GetBalance { key: *pubkey };
let data = serialize(&req).expect("serialize GetBalance");
stream.write(&data)?;
let mut buf = vec![0u8; 1024];
stream.read(&mut buf)?;
let resp = deserialize(&buf).expect("deserialize balance");
let Response::Balance { key, val } = resp;
assert_eq!(key, *pubkey);
Ok(val)
}
}

#[cfg(test)]
mod tests {
use super::*;
use accountant::Accountant;
use accountant_skel::AccountantSkel;
use std::thread::{sleep, spawn};
use std::time::Duration;
use log::{generate_keypair, get_pubkey, Sha256Hash};

#[test]
fn test_accountant_stub() {
let addr = "127.0.0.1:8000";
spawn(move || {
let zero = Sha256Hash::default();
let acc = Accountant::new(&zero, None);
let mut skel = AccountantSkel::new(acc);
skel.serve(addr).unwrap();
});

sleep(Duration::from_millis(30));

let mut acc = AccountantStub::new(addr);
let alice_keypair = generate_keypair();
let bob_keypair = generate_keypair();
acc.deposit(10_000, &alice_keypair).unwrap();
acc.deposit(1_000, &bob_keypair).unwrap();

sleep(Duration::from_millis(30));
let bob_pubkey = get_pubkey(&bob_keypair);
acc.transfer(500, &alice_keypair, bob_pubkey).unwrap();

sleep(Duration::from_millis(300));
assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500);
}
}
Loading