Merge pull request #45 from garious/init-from-log

Towards sending the log to clients
This commit is contained in:
Greg Fitzgerald 2018-03-05 16:17:41 -07:00 committed by GitHub
commit cb0ce9986c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 459 additions and 319 deletions

104
README.md
View File

@ -21,74 +21,64 @@ corresponding benchmarks are also added that demonstrate real performance boosts
feature set here will always be a ways behind the loom repo, but that this is an implementation feature set here will always be a ways behind the loom repo, but that this is an implementation
you can take to the bank, literally. you can take to the bank, literally.
Usage Running the demo
=== ===
Add the latest [silk package](https://crates.io/crates/silk) to the `[dependencies]` section First, build the demo executables in release mode (optimized for performance):
of your Cargo.toml.
Create a *Historian* and send it *events* to generate an *event log*, where each log *entry* ```bash
is tagged with the historian's latest *hash*. Then ensure the order of events was not tampered $ cargo build --release
with by verifying each entry's hash can be generated from the hash in the previous entry: $ cd target/release
![historian](https://user-images.githubusercontent.com/55449/36950845-459bdb58-1fb9-11e8-850e-894586f3729b.png)
```rust
extern crate silk;
use silk::historian::Historian;
use silk::log::{verify_slice, Entry, Sha256Hash};
use silk::event::{generate_keypair, get_pubkey, sign_claim_data, Event};
use std::thread::sleep;
use std::time::Duration;
use std::sync::mpsc::SendError;
fn create_log(hist: &Historian<Sha256Hash>) -> Result<(), SendError<Event<Sha256Hash>>> {
sleep(Duration::from_millis(15));
let data = Sha256Hash::default();
let keypair = generate_keypair();
let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_claim_data(&data, &keypair));
hist.sender.send(event0)?;
sleep(Duration::from_millis(10));
Ok(())
}
fn main() {
let seed = Sha256Hash::default();
let hist = Historian::new(&seed, Some(10));
create_log(&hist).expect("send error");
drop(hist.sender);
let entries: Vec<Entry<Sha256Hash>> = hist.receiver.iter().collect();
for entry in &entries {
println!("{:?}", entry);
}
// Proof-of-History: Verify the historian learned about the events
// in the same order they appear in the vector.
assert!(verify_slice(&entries, &seed));
}
``` ```
Running the program should produce a log similar to: The testnode server is initialized with a transaction log from stdin and
generates a log on stdout. To create the input log, we'll need to create
a *genesis* configuration file and then generate a log from it. It's done
in two steps here because the demo-genesis.json file contains a private
key that will be used later in this demo.
```rust ```bash
Entry { num_hashes: 0, id: [0, ...], event: Tick } $ ./silk-genesis-file-demo > demo-genesis.jsoc
Entry { num_hashes: 3, id: [67, ...], event: Transaction { data: [37, ...] } } $ cat demo-genesis.json | ./silk-genesis-block > demo-genesis.log
Entry { num_hashes: 3, id: [123, ...], event: Tick }
``` ```
Proof-of-History Now you can start the server:
---
Take note of the last line: ```bash
$ cat demo-genesis.log | ./silk-testnode > demo-entries0.log
```rust
assert!(verify_slice(&entries, &seed));
``` ```
[It's a proof!](https://en.wikipedia.org/wiki/CurryHoward_correspondence) For each entry returned by the Then, in a seperate shell, let's execute some transactions. Note we pass in
historian, we can verify that `id` is the result of applying a sha256 hash to the previous `id` the JSON configuration file here, not the genesis log.
exactly `num_hashes` times, and then hashing then event data on top of that. Because the event data is
included in the hash, the events cannot be reordered without regenerating all the hashes. ```bash
$ cat demo-genesis.json | ./silk-client-demo
```
Now kill the server with Ctrl-C and take a look at the transaction log. You should
see something similar to:
```json
{"num_hashes":27,"id":[0, ...],"event":"Tick"}
{"num_hashes:"3,"id":[67, ...],"event":{"Transaction":{"data":[37, ...]}}}
{"num_hashes":27,"id":[0, ...],"event":"Tick"}
```
Now restart the server from where we left off. Pass it both the genesis log and
the transaction log.
```bash
$ cat demo-genesis.log demo-entries0.log | ./silk-testnode > demo-entries1.log
```
Lastly, run the client demo again and verify that all funds were spent in the
previous round and so no additional transactions are added.
```bash
$ cat demo-genesis.json | ./silk-client-demo
```
Stop the server again and verify there are only Tick entries and no Transaction entries.
Developing Developing
=== ===

65
doc/historian.md Normal file
View File

@ -0,0 +1,65 @@
The Historian
===
Create a *Historian* and send it *events* to generate an *event log*, where each log *entry*
is tagged with the historian's latest *hash*. Then ensure the order of events was not tampered
with by verifying each entry's hash can be generated from the hash in the previous entry:
![historian](https://user-images.githubusercontent.com/55449/36950845-459bdb58-1fb9-11e8-850e-894586f3729b.png)
```rust
extern crate silk;
use silk::historian::Historian;
use silk::log::{verify_slice, Entry, Sha256Hash};
use silk::event::{generate_keypair, get_pubkey, sign_claim_data, Event};
use std::thread::sleep;
use std::time::Duration;
use std::sync::mpsc::SendError;
fn create_log(hist: &Historian<Sha256Hash>) -> Result<(), SendError<Event<Sha256Hash>>> {
sleep(Duration::from_millis(15));
let data = Sha256Hash::default();
let keypair = generate_keypair();
let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_claim_data(&data, &keypair));
hist.sender.send(event0)?;
sleep(Duration::from_millis(10));
Ok(())
}
fn main() {
let seed = Sha256Hash::default();
let hist = Historian::new(&seed, Some(10));
create_log(&hist).expect("send error");
drop(hist.sender);
let entries: Vec<Entry<Sha256Hash>> = hist.receiver.iter().collect();
for entry in &entries {
println!("{:?}", entry);
}
// Proof-of-History: Verify the historian learned about the events
// in the same order they appear in the vector.
assert!(verify_slice(&entries, &seed));
}
```
Running the program should produce a log similar to:
```rust
Entry { num_hashes: 0, id: [0, ...], event: Tick }
Entry { num_hashes: 3, id: [67, ...], event: Transaction { data: [37, ...] } }
Entry { num_hashes: 3, id: [123, ...], event: Tick }
```
Proof-of-History
---
Take note of the last line:
```rust
assert!(verify_slice(&entries, &seed));
```
[It's a proof!](https://en.wikipedia.org/wiki/CurryHoward_correspondence) For each entry returned by the
historian, we can verify that `id` is the result of applying a sha256 hash to the previous `id`
exactly `num_hashes` times, and then hashing then event data on top of that. Because the event data is
included in the hash, the events cannot be reordered without regenerating all the hashes.

View File

@ -2,16 +2,14 @@
//! event log to record transactions. Its users can deposit funds and //! event log to record transactions. Its users can deposit funds and
//! transfer funds to other users. //! transfer funds to other users.
use log::{hash, Entry, Sha256Hash}; use log::{Entry, Sha256Hash};
use event::{get_pubkey, sign_transaction_data, Event, PublicKey, Signature}; use event::{get_pubkey, sign_transaction_data, verify_event, Event, PublicKey, Signature};
use genesis::Genesis; use genesis::Genesis;
use historian::Historian; use historian::{reserve_signature, Historian};
use ring::signature::Ed25519KeyPair; use ring::signature::Ed25519KeyPair;
use std::sync::mpsc::SendError; use std::sync::mpsc::SendError;
use std::collections::HashMap; use std::collections::HashMap;
use std::result; use std::result;
use std::thread::sleep;
use std::time::Duration;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum AccountingError { pub enum AccountingError {
@ -25,35 +23,51 @@ pub type Result<T> = result::Result<T, AccountingError>;
pub struct Accountant { pub struct Accountant {
pub historian: Historian<u64>, pub historian: Historian<u64>,
pub balances: HashMap<PublicKey, u64>, pub balances: HashMap<PublicKey, u64>,
pub first_id: Sha256Hash,
pub last_id: Sha256Hash, pub last_id: Sha256Hash,
} }
impl Accountant { impl Accountant {
pub fn new(gen: &Genesis, ms_per_tick: Option<u64>) -> Self { pub fn new_from_entries<I>(entries: I, ms_per_tick: Option<u64>) -> Self
let start_hash = hash(&gen.pkcs8); where
I: IntoIterator<Item = Entry<u64>>,
{
let mut entries = entries.into_iter();
// The first item in the log is required to be an entry with zero num_hashes,
// which implies its id can be used as the log's seed.
let entry0 = entries.next().unwrap();
let start_hash = entry0.id;
let hist = Historian::<u64>::new(&start_hash, ms_per_tick); let hist = Historian::<u64>::new(&start_hash, ms_per_tick);
let mut acc = Accountant { let mut acc = Accountant {
historian: hist, historian: hist,
balances: HashMap::new(), balances: HashMap::new(),
first_id: start_hash,
last_id: start_hash, last_id: start_hash,
}; };
for (i, event) in gen.create_events().into_iter().enumerate() {
acc.process_verified_event(event, i < 2).unwrap(); // The second item in the log is a special transaction where the to and from
// fields are the same. That entry should be treated as a deposit, not a
// transfer to oneself.
let entry1 = entries.next().unwrap();
acc.process_verified_event(&entry1.event, true).unwrap();
for entry in entries {
acc.process_verified_event(&entry.event, false).unwrap();
} }
acc acc
} }
pub fn sync(self: &mut Self) -> Vec<Entry<u64>> { pub fn new(gen: &Genesis, ms_per_tick: Option<u64>) -> Self {
let mut entries = vec![]; Self::new_from_entries(gen.create_entries(), ms_per_tick)
}
pub fn sync(self: &mut Self) -> Sha256Hash {
while let Ok(entry) = self.historian.receiver.try_recv() { while let Ok(entry) = self.historian.receiver.try_recv() {
entries.push(entry); self.last_id = entry.id;
} }
self.last_id
if let Some(last_entry) = entries.last() {
self.last_id = last_entry.id;
}
entries
} }
fn is_deposit(allow_deposits: bool, from: &PublicKey, to: &PublicKey) -> bool { fn is_deposit(allow_deposits: bool, from: &PublicKey, to: &PublicKey) -> bool {
@ -61,46 +75,49 @@ impl Accountant {
} }
pub fn process_event(self: &mut Self, event: Event<u64>) -> Result<()> { pub fn process_event(self: &mut Self, event: Event<u64>) -> Result<()> {
if !self.historian.verify_event(&event) { if !verify_event(&event) {
return Err(AccountingError::InvalidEvent); return Err(AccountingError::InvalidEvent);
} }
self.process_verified_event(event, false)
if let Event::Transaction { from, data, .. } = event {
if self.get_balance(&from).unwrap_or(0) < data {
return Err(AccountingError::InsufficientFunds);
}
}
self.process_verified_event(&event, false)?;
if let Err(SendError(_)) = self.historian.sender.send(event) {
return Err(AccountingError::SendError);
}
Ok(())
} }
fn process_verified_event( fn process_verified_event(
self: &mut Self, self: &mut Self,
event: Event<u64>, event: &Event<u64>,
allow_deposits: bool, allow_deposits: bool,
) -> Result<()> { ) -> Result<()> {
match event { if !reserve_signature(&mut self.historian.signatures, event) {
Event::Tick => Ok(()), return Err(AccountingError::InvalidEvent);
Event::Transaction { from, to, data, .. } => { }
if !Self::is_deposit(allow_deposits, &from, &to) {
if self.get_balance(&from).unwrap_or(0) < data {
return Err(AccountingError::InsufficientFunds);
}
}
if let Err(SendError(_)) = self.historian.sender.send(event) { if let Event::Transaction { from, to, data, .. } = *event {
return Err(AccountingError::SendError); if !Self::is_deposit(allow_deposits, &from, &to) {
if let Some(x) = self.balances.get_mut(&from) {
*x -= data;
} }
}
if !Self::is_deposit(allow_deposits, &from, &to) { if self.balances.contains_key(&to) {
if let Some(x) = self.balances.get_mut(&from) { if let Some(x) = self.balances.get_mut(&to) {
*x -= data; *x += data;
}
} }
} else {
if self.balances.contains_key(&to) { self.balances.insert(to, data);
if let Some(x) = self.balances.get_mut(&to) {
*x += data;
}
} else {
self.balances.insert(to, data);
}
Ok(())
} }
} }
Ok(())
} }
pub fn transfer( pub fn transfer(
@ -110,11 +127,13 @@ impl Accountant {
to: PublicKey, to: PublicKey,
) -> Result<Signature> { ) -> Result<Signature> {
let from = get_pubkey(keypair); let from = get_pubkey(keypair);
let sig = sign_transaction_data(&n, keypair, &to); let last_id = self.last_id;
let sig = sign_transaction_data(&n, keypair, &to, &last_id);
let event = Event::Transaction { let event = Event::Transaction {
from, from,
to, to,
data: n, data: n,
last_id,
sig, sig,
}; };
self.process_event(event).map(|_| sig) self.process_event(event).map(|_| sig)
@ -123,21 +142,6 @@ impl Accountant {
pub fn get_balance(self: &Self, pubkey: &PublicKey) -> Option<u64> { pub fn get_balance(self: &Self, pubkey: &PublicKey) -> Option<u64> {
self.balances.get(pubkey).map(|x| *x) self.balances.get(pubkey).map(|x| *x)
} }
pub fn wait_on_signature(self: &mut Self, wait_sig: &Signature) {
let mut entries = self.sync();
let mut found = false;
while !found {
found = entries.iter().any(|e| match e.event {
Event::Transaction { sig, .. } => sig == *wait_sig,
_ => false,
});
if !found {
sleep(Duration::from_millis(30));
entries = self.sync();
}
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -146,8 +150,6 @@ mod tests {
use event::{generate_keypair, get_pubkey}; use event::{generate_keypair, get_pubkey};
use logger::ExitReason; use logger::ExitReason;
use genesis::Creator; use genesis::Creator;
use std::thread::sleep;
use std::time::Duration;
#[test] #[test]
fn test_accountant() { fn test_accountant() {
@ -156,14 +158,12 @@ mod tests {
let alice = Genesis::new(10_000, vec![bob]); let alice = Genesis::new(10_000, vec![bob]);
let mut acc = Accountant::new(&alice, Some(2)); let mut acc = Accountant::new(&alice, Some(2));
let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey).unwrap(); acc.transfer(500, &alice.get_keypair(), bob_pubkey).unwrap();
acc.wait_on_signature(&sig);
assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500);
drop(acc.historian.sender); drop(acc.historian.sender);
assert_eq!( assert_eq!(
acc.historian.thread_hdl.join().unwrap().1, acc.historian.thread_hdl.join().unwrap(),
ExitReason::RecvDisconnected ExitReason::RecvDisconnected
); );
} }
@ -174,12 +174,10 @@ mod tests {
let bob_pubkey = bob.pubkey; let bob_pubkey = bob.pubkey;
let alice = Genesis::new(11_000, vec![bob]); let alice = Genesis::new(11_000, vec![bob]);
let mut acc = Accountant::new(&alice, Some(2)); let mut acc = Accountant::new(&alice, Some(2));
assert_eq!( assert_eq!(
acc.transfer(10_001, &alice.get_keypair(), bob_pubkey), acc.transfer(10_001, &alice.get_keypair(), bob_pubkey),
Err(AccountingError::InsufficientFunds) Err(AccountingError::InsufficientFunds)
); );
sleep(Duration::from_millis(30));
let alice_pubkey = get_pubkey(&alice.get_keypair()); let alice_pubkey = get_pubkey(&alice.get_keypair());
assert_eq!(acc.get_balance(&alice_pubkey).unwrap(), 10_000); assert_eq!(acc.get_balance(&alice_pubkey).unwrap(), 10_000);
@ -187,7 +185,7 @@ mod tests {
drop(acc.historian.sender); drop(acc.historian.sender);
assert_eq!( assert_eq!(
acc.historian.thread_hdl.join().unwrap().1, acc.historian.thread_hdl.join().unwrap(),
ExitReason::RecvDisconnected ExitReason::RecvDisconnected
); );
} }
@ -199,13 +197,12 @@ mod tests {
let alice_keypair = alice.get_keypair(); let alice_keypair = alice.get_keypair();
let bob_keypair = generate_keypair(); let bob_keypair = generate_keypair();
let bob_pubkey = get_pubkey(&bob_keypair); let bob_pubkey = get_pubkey(&bob_keypair);
let sig = acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); acc.transfer(500, &alice_keypair, bob_pubkey).unwrap();
acc.wait_on_signature(&sig);
assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 500); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 500);
drop(acc.historian.sender); drop(acc.historian.sender);
assert_eq!( assert_eq!(
acc.historian.thread_hdl.join().unwrap().1, acc.historian.thread_hdl.join().unwrap(),
ExitReason::RecvDisconnected ExitReason::RecvDisconnected
); );
} }

View File

@ -1,11 +1,12 @@
use std::io; use std::io;
use accountant::Accountant; use accountant::Accountant;
use event::{Event, PublicKey, Signature}; use event::{Event, PublicKey, Signature};
use log::{Entry, Sha256Hash};
use std::net::UdpSocket; use std::net::UdpSocket;
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
pub struct AccountantSkel { pub struct AccountantSkel {
pub obj: Accountant, pub acc: Accountant,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -14,49 +15,67 @@ pub enum Request {
from: PublicKey, from: PublicKey,
to: PublicKey, to: PublicKey,
val: u64, val: u64,
last_id: Sha256Hash,
sig: Signature, sig: Signature,
}, },
GetBalance { GetBalance {
key: PublicKey, key: PublicKey,
}, },
Wait { GetEntries {
sig: Signature, last_id: Sha256Hash,
},
GetId {
is_last: bool,
}, },
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum Response { pub enum Response {
Balance { key: PublicKey, val: u64 }, Balance { key: PublicKey, val: Option<u64> },
Confirmed { sig: Signature }, Entries { entries: Vec<Entry<u64>> },
Id { id: Sha256Hash, is_last: bool },
} }
impl AccountantSkel { impl AccountantSkel {
pub fn new(obj: Accountant) -> Self { pub fn new(acc: Accountant) -> Self {
AccountantSkel { obj } AccountantSkel { acc }
} }
pub fn process_request(self: &mut Self, msg: Request) -> Option<Response> { pub fn process_request(self: &mut Self, msg: Request) -> Option<Response> {
match msg { match msg {
Request::Transfer { from, to, val, sig } => { Request::Transfer {
from,
to,
val,
last_id,
sig,
} => {
let event = Event::Transaction { let event = Event::Transaction {
from, from,
to, to,
data: val, data: val,
last_id,
sig, sig,
}; };
if let Err(err) = self.obj.process_event(event) { if let Err(err) = self.acc.process_event(event) {
println!("Transfer error: {:?}", err); eprintln!("Transfer error: {:?}", err);
} }
None None
} }
Request::GetBalance { key } => { Request::GetBalance { key } => {
let val = self.obj.get_balance(&key).unwrap(); let val = self.acc.get_balance(&key);
Some(Response::Balance { key, val }) Some(Response::Balance { key, val })
} }
Request::Wait { sig } => { Request::GetEntries { .. } => Some(Response::Entries { entries: vec![] }),
self.obj.wait_on_signature(&sig); Request::GetId { is_last } => Some(Response::Id {
Some(Response::Confirmed { sig }) id: if is_last {
} self.acc.sync();
self.acc.last_id
} else {
self.acc.first_id
},
is_last,
}),
} }
} }

View File

@ -5,13 +5,15 @@
use std::net::UdpSocket; use std::net::UdpSocket;
use std::io; use std::io;
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use event::{get_pubkey, sign_transaction_data, PublicKey, Signature}; use event::{get_pubkey, get_signature, sign_transaction_data, PublicKey, Signature};
use log::{Entry, Sha256Hash};
use ring::signature::Ed25519KeyPair; use ring::signature::Ed25519KeyPair;
use accountant_skel::{Request, Response}; use accountant_skel::{Request, Response};
pub struct AccountantStub { pub struct AccountantStub {
pub addr: String, pub addr: String,
pub socket: UdpSocket, pub socket: UdpSocket,
pub last_id: Option<Sha256Hash>,
} }
impl AccountantStub { impl AccountantStub {
@ -19,33 +21,43 @@ impl AccountantStub {
AccountantStub { AccountantStub {
addr: addr.to_string(), addr: addr.to_string(),
socket, socket,
last_id: None,
} }
} }
pub fn transfer_signed( pub fn transfer_signed(
self: &Self, &self,
from: PublicKey, from: PublicKey,
to: PublicKey, to: PublicKey,
val: u64, val: u64,
last_id: Sha256Hash,
sig: Signature, sig: Signature,
) -> io::Result<usize> { ) -> io::Result<usize> {
let req = Request::Transfer { from, to, val, sig }; let req = Request::Transfer {
from,
to,
val,
last_id,
sig,
};
let data = serialize(&req).unwrap(); let data = serialize(&req).unwrap();
self.socket.send_to(&data, &self.addr) self.socket.send_to(&data, &self.addr)
} }
pub fn transfer( pub fn transfer(
self: &Self, &self,
n: u64, n: u64,
keypair: &Ed25519KeyPair, keypair: &Ed25519KeyPair,
to: PublicKey, to: PublicKey,
last_id: &Sha256Hash,
) -> io::Result<Signature> { ) -> io::Result<Signature> {
let from = get_pubkey(keypair); let from = get_pubkey(keypair);
let sig = sign_transaction_data(&n, keypair, &to); let sig = sign_transaction_data(&n, keypair, &to, last_id);
self.transfer_signed(from, to, n, sig).map(|_| sig) self.transfer_signed(from, to, n, *last_id, sig)
.map(|_| sig)
} }
pub fn get_balance(self: &Self, pubkey: &PublicKey) -> io::Result<u64> { pub fn get_balance(&self, pubkey: &PublicKey) -> io::Result<Option<u64>> {
let req = Request::GetBalance { key: *pubkey }; let req = Request::GetBalance { key: *pubkey };
let data = serialize(&req).expect("serialize GetBalance"); let data = serialize(&req).expect("serialize GetBalance");
self.socket.send_to(&data, &self.addr)?; self.socket.send_to(&data, &self.addr)?;
@ -56,20 +68,55 @@ impl AccountantStub {
assert_eq!(key, *pubkey); assert_eq!(key, *pubkey);
return Ok(val); return Ok(val);
} }
Ok(0) Ok(None)
} }
pub fn wait_on_signature(self: &Self, wait_sig: &Signature) -> io::Result<()> { fn get_id(&self, is_last: bool) -> io::Result<Sha256Hash> {
let req = Request::Wait { sig: *wait_sig }; let req = Request::GetId { is_last };
let data = serialize(&req).expect("serialize GetId");
self.socket.send_to(&data, &self.addr)?;
let mut buf = vec![0u8; 1024];
self.socket.recv_from(&mut buf)?;
let resp = deserialize(&buf).expect("deserialize Id");
if let Response::Id { id, .. } = resp {
return Ok(id);
}
Ok(Default::default())
}
pub fn get_last_id(&self) -> io::Result<Sha256Hash> {
self.get_id(true)
}
pub fn wait_on_signature(&mut self, wait_sig: &Signature) -> io::Result<()> {
let last_id = match self.last_id {
None => {
let first_id = self.get_id(false)?;
self.last_id = Some(first_id);
first_id
}
Some(last_id) => last_id,
};
let req = Request::GetEntries { last_id };
let data = serialize(&req).unwrap(); let data = serialize(&req).unwrap();
self.socket.send_to(&data, &self.addr).map(|_| ())?; self.socket.send_to(&data, &self.addr).map(|_| ())?;
let mut buf = vec![0u8; 1024]; let mut buf = vec![0u8; 1024];
self.socket.recv_from(&mut buf)?; self.socket.recv_from(&mut buf)?;
let resp = deserialize(&buf).expect("deserialize signature"); let resp = deserialize(&buf).expect("deserialize signature");
if let Response::Confirmed { sig } = resp { if let Response::Entries { entries } = resp {
assert_eq!(sig, *wait_sig); for Entry { id, event, .. } in entries {
self.last_id = Some(id);
if let Some(sig) = get_signature(&event) {
if sig == *wait_sig {
return Ok(());
}
}
}
} }
// TODO: Loop until we found it.
Ok(()) Ok(())
} }
} }
@ -95,9 +142,11 @@ mod tests {
sleep(Duration::from_millis(30)); sleep(Duration::from_millis(30));
let socket = UdpSocket::bind(send_addr).unwrap(); let socket = UdpSocket::bind(send_addr).unwrap();
let acc = AccountantStub::new(addr, socket); let mut acc = AccountantStub::new(addr, socket);
let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey).unwrap(); let last_id = acc.get_last_id().unwrap();
let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey, &last_id)
.unwrap();
acc.wait_on_signature(&sig).unwrap(); acc.wait_on_signature(&sig).unwrap();
assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); assert_eq!(acc.get_balance(&bob_pubkey).unwrap().unwrap(), 1_500);
} }
} }

View File

@ -1,35 +1,28 @@
//extern crate serde_json; extern crate serde_json;
extern crate silk; extern crate silk;
use silk::accountant_stub::AccountantStub; use silk::accountant_stub::AccountantStub;
use silk::accountant_skel::AccountantSkel;
use silk::accountant::Accountant;
use silk::event::{generate_keypair, get_pubkey, sign_transaction_data, verify_event, Event}; use silk::event::{generate_keypair, get_pubkey, sign_transaction_data, verify_event, Event};
use silk::genesis::Genesis; use silk::genesis::Genesis;
use std::time::Instant; use std::time::Instant;
use std::net::UdpSocket; use std::net::UdpSocket;
use std::thread::{sleep, spawn}; use std::io::stdin;
use std::time::Duration;
//use std::io::stdin;
fn main() { fn main() {
let addr = "127.0.0.1:8000"; let addr = "127.0.0.1:8000";
let send_addr = "127.0.0.1:8001"; let send_addr = "127.0.0.1:8001";
let txs = 200; let gen: Genesis = serde_json::from_reader(stdin()).unwrap();
let alice_keypair = gen.get_keypair();
let gen = Genesis::new(txs, vec![]); let alice_pubkey = gen.get_pubkey();
let alice_keypair = generate_keypair();
//let gen: Genesis = serde_json::from_reader(stdin()).unwrap();
//let alice_keypair = gen.get_keypair();
let acc = Accountant::new(&gen, None);
spawn(move || AccountantSkel::new(acc).serve(addr).unwrap());
sleep(Duration::from_millis(30));
let socket = UdpSocket::bind(send_addr).unwrap(); let socket = UdpSocket::bind(send_addr).unwrap();
let acc = AccountantStub::new(addr, socket); let mut acc = AccountantStub::new(addr, socket);
let alice_pubkey = get_pubkey(&alice_keypair); let last_id = acc.get_last_id().unwrap();
let txs = acc.get_balance(&alice_pubkey).unwrap().unwrap();
println!("Alice's Initial Balance {}", txs);
let one = 1; let one = 1;
println!("Signing transactions..."); println!("Signing transactions...");
let now = Instant::now(); let now = Instant::now();
@ -37,7 +30,7 @@ fn main() {
.map(|_| { .map(|_| {
let rando_keypair = generate_keypair(); let rando_keypair = generate_keypair();
let rando_pubkey = get_pubkey(&rando_keypair); let rando_pubkey = get_pubkey(&rando_keypair);
let sig = sign_transaction_data(&one, &alice_keypair, &rando_pubkey); let sig = sign_transaction_data(&one, &alice_keypair, &rando_pubkey, &last_id);
(rando_pubkey, sig) (rando_pubkey, sig)
}) })
.collect(); .collect();
@ -58,6 +51,7 @@ fn main() {
from: alice_pubkey, from: alice_pubkey,
to: k, to: k,
data: one, data: one,
last_id,
sig: s, sig: s,
}; };
assert!(verify_event(&e)); assert!(verify_event(&e));
@ -76,7 +70,8 @@ fn main() {
let now = Instant::now(); let now = Instant::now();
let mut sig = Default::default(); let mut sig = Default::default();
for (k, s) in sigs { for (k, s) in sigs {
acc.transfer_signed(alice_pubkey, k, one, s).unwrap(); acc.transfer_signed(alice_pubkey, k, one, last_id, s)
.unwrap();
sig = s; sig = s;
} }
println!("Waiting for last transaction to be confirmed...",); println!("Waiting for last transaction to be confirmed...",);
@ -86,7 +81,7 @@ fn main() {
let ns = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64; let ns = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64;
let tps = (txs * 1_000_000_000) as f64 / ns as f64; let tps = (txs * 1_000_000_000) as f64 / ns as f64;
println!("Done. {} tps!", tps); println!("Done. {} tps!", tps);
let val = acc.get_balance(&alice_pubkey).unwrap(); let val = acc.get_balance(&alice_pubkey).unwrap().unwrap();
println!("Alice's Final Balance {}", val); println!("Alice's Final Balance {}", val);
assert_eq!(val, 0); assert_eq!(val, 0);
} }

View File

@ -7,11 +7,19 @@ use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use std::sync::mpsc::SendError; use std::sync::mpsc::SendError;
fn create_log(hist: &Historian<Sha256Hash>) -> Result<(), SendError<Event<Sha256Hash>>> { fn create_log(
hist: &Historian<Sha256Hash>,
seed: &Sha256Hash,
) -> Result<(), SendError<Event<Sha256Hash>>> {
sleep(Duration::from_millis(15)); sleep(Duration::from_millis(15));
let data = Sha256Hash::default(); let data = Sha256Hash::default();
let keypair = generate_keypair(); let keypair = generate_keypair();
let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_claim_data(&data, &keypair)); let event0 = Event::new_claim(
get_pubkey(&keypair),
data,
*seed,
sign_claim_data(&data, &keypair, seed),
);
hist.sender.send(event0)?; hist.sender.send(event0)?;
sleep(Duration::from_millis(10)); sleep(Duration::from_millis(10));
Ok(()) Ok(())
@ -20,7 +28,7 @@ fn create_log(hist: &Historian<Sha256Hash>) -> Result<(), SendError<Event<Sha256
fn main() { fn main() {
let seed = Sha256Hash::default(); let seed = Sha256Hash::default();
let hist = Historian::new(&seed, Some(10)); let hist = Historian::new(&seed, Some(10));
create_log(&hist).expect("send error"); create_log(&hist, &seed).expect("send error");
drop(hist.sender); drop(hist.sender);
let entries: Vec<Entry<Sha256Hash>> = hist.receiver.iter().collect(); let entries: Vec<Entry<Sha256Hash>> = hist.receiver.iter().collect();
for entry in &entries { for entry in &entries {

View File

@ -5,20 +5,14 @@ extern crate serde_json;
extern crate silk; extern crate silk;
use silk::genesis::Genesis; use silk::genesis::Genesis;
use silk::log::{create_entries, hash, verify_slice_u64}; use silk::log::verify_slice_u64;
use std::io::stdin; use std::io::stdin;
fn main() { fn main() {
let gen: Genesis = serde_json::from_reader(stdin()).unwrap(); let gen: Genesis = serde_json::from_reader(stdin()).unwrap();
let entries = create_entries(&hash(&gen.pkcs8), gen.create_events()); let entries = gen.create_entries();
verify_slice_u64(&entries, &entries[0].id); verify_slice_u64(&entries, &entries[0].id);
println!("["); for x in entries {
let len = entries.len(); println!("{}", serde_json::to_string(&x).unwrap());
for (i, x) in entries.iter().enumerate() {
let s = serde_json::to_string(&x).unwrap();
let terminator = if i + 1 == len { "" } else { "," };
println!(" {}{}", s, terminator);
} }
println!("]");
} }

View File

@ -14,6 +14,6 @@ fn main() {
pubkey: get_pubkey(&generate_keypair()), pubkey: get_pubkey(&generate_keypair()),
}; };
let creators = vec![alice, bob]; let creators = vec![alice, bob];
let gen = Genesis::new(300, creators); let gen = Genesis::new(500, creators);
println!("{}", serde_json::to_string(&gen).unwrap()); println!("{}", serde_json::to_string(&gen).unwrap());
} }

View File

@ -3,14 +3,17 @@ extern crate silk;
use silk::accountant_skel::AccountantSkel; use silk::accountant_skel::AccountantSkel;
use silk::accountant::Accountant; use silk::accountant::Accountant;
use silk::genesis::Genesis; use std::io::{self, BufRead};
use std::io::stdin;
fn main() { fn main() {
let addr = "127.0.0.1:8000"; let addr = "127.0.0.1:8000";
let gen: Genesis = serde_json::from_reader(stdin()).unwrap(); let stdin = io::stdin();
let acc = Accountant::new(&gen, Some(1000)); let entries = stdin
.lock()
.lines()
.map(|line| serde_json::from_str(&line.unwrap()).unwrap());
let acc = Accountant::new_from_entries(entries, Some(1000));
let mut skel = AccountantSkel::new(acc); let mut skel = AccountantSkel::new(acc);
println!("Listening on {}", addr); eprintln!("Listening on {}", addr);
skel.serve(addr).unwrap(); skel.serve(addr).unwrap();
} }

View File

@ -20,6 +20,7 @@ use ring::{rand, signature};
use untrusted; use untrusted;
use serde::Serialize; use serde::Serialize;
use bincode::serialize; use bincode::serialize;
use log::Sha256Hash;
pub type PublicKey = GenericArray<u8, U32>; pub type PublicKey = GenericArray<u8, U32>;
pub type Signature = GenericArray<u8, U64>; pub type Signature = GenericArray<u8, U64>;
@ -36,16 +37,18 @@ pub enum Event<T> {
from: PublicKey, from: PublicKey,
to: PublicKey, to: PublicKey,
data: T, data: T,
last_id: Sha256Hash,
sig: Signature, sig: Signature,
}, },
} }
impl<T> Event<T> { impl<T> Event<T> {
pub fn new_claim(to: PublicKey, data: T, sig: Signature) -> Self { pub fn new_claim(to: PublicKey, data: T, last_id: Sha256Hash, sig: Signature) -> Self {
Event::Transaction { Event::Transaction {
from: to, from: to,
to, to,
data, data,
last_id,
sig, sig,
} }
} }
@ -74,14 +77,19 @@ pub fn sign_transaction_data<T: Serialize>(
data: &T, data: &T,
keypair: &Ed25519KeyPair, keypair: &Ed25519KeyPair,
to: &PublicKey, to: &PublicKey,
last_id: &Sha256Hash,
) -> Signature { ) -> Signature {
let from = &get_pubkey(keypair); let from = &get_pubkey(keypair);
sign_serialized(&(from, to, data), keypair) sign_serialized(&(from, to, data, last_id), keypair)
} }
/// Return a signature for the given data using the private key from the given keypair. /// Return a signature for the given data using the private key from the given keypair.
pub fn sign_claim_data<T: Serialize>(data: &T, keypair: &Ed25519KeyPair) -> Signature { pub fn sign_claim_data<T: Serialize>(
sign_transaction_data(data, keypair, &get_pubkey(keypair)) data: &T,
keypair: &Ed25519KeyPair,
last_id: &Sha256Hash,
) -> Signature {
sign_transaction_data(data, keypair, &get_pubkey(keypair), last_id)
} }
/// Verify a signed message with the given public key. /// Verify a signed message with the given public key.
@ -104,10 +112,11 @@ pub fn verify_event<T: Serialize>(event: &Event<T>) -> bool {
from, from,
to, to,
ref data, ref data,
last_id,
sig, sig,
} = *event } = *event
{ {
let sign_data = serialize(&(&from, &to, &data)).unwrap(); let sign_data = serialize(&(&from, &to, &data, &last_id)).unwrap();
if !verify_signature(&from, &sign_data, &sig) { if !verify_signature(&from, &sign_data, &sig) {
return false; return false;
} }
@ -122,7 +131,12 @@ mod tests {
#[test] #[test]
fn test_serialize_claim() { fn test_serialize_claim() {
let claim0 = Event::new_claim(Default::default(), 0u8, Default::default()); let claim0 = Event::new_claim(
Default::default(),
0u8,
Default::default(),
Default::default(),
);
let buf = serialize(&claim0).unwrap(); let buf = serialize(&claim0).unwrap();
let claim1: Event<u8> = deserialize(&buf).unwrap(); let claim1: Event<u8> = deserialize(&buf).unwrap();
assert_eq!(claim1, claim0); assert_eq!(claim1, claim0);

View File

@ -1,6 +1,7 @@
//! A library for generating the chain's genesis block. //! A library for generating the chain's genesis block.
use event::{generate_keypair, get_pubkey, sign_transaction_data, Event, PublicKey}; use event::{generate_keypair, get_pubkey, sign_transaction_data, Event, PublicKey};
use log::{create_entries, hash, Entry, Sha256Hash};
use ring::rand::SystemRandom; use ring::rand::SystemRandom;
use ring::signature::Ed25519KeyPair; use ring::signature::Ed25519KeyPair;
use untrusted::Input; use untrusted::Input;
@ -31,6 +32,7 @@ impl Genesis {
pub fn new(tokens: u64, creators: Vec<Creator>) -> Self { pub fn new(tokens: u64, creators: Vec<Creator>) -> Self {
let rnd = SystemRandom::new(); let rnd = SystemRandom::new();
let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap().to_vec(); let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap().to_vec();
println!("{:?}", pkcs8);
Genesis { Genesis {
pkcs8, pkcs8,
tokens, tokens,
@ -38,6 +40,10 @@ impl Genesis {
} }
} }
pub fn get_seed(&self) -> Sha256Hash {
hash(&self.pkcs8)
}
pub fn get_keypair(&self) -> Ed25519KeyPair { pub fn get_keypair(&self) -> Ed25519KeyPair {
Ed25519KeyPair::from_pkcs8(Input::from(&self.pkcs8)).unwrap() Ed25519KeyPair::from_pkcs8(Input::from(&self.pkcs8)).unwrap()
} }
@ -47,12 +53,14 @@ impl Genesis {
} }
pub fn create_transaction(&self, data: u64, to: &PublicKey) -> Event<u64> { pub fn create_transaction(&self, data: u64, to: &PublicKey) -> Event<u64> {
let last_id = self.get_seed();
let from = self.get_pubkey(); let from = self.get_pubkey();
let sig = sign_transaction_data(&data, &self.get_keypair(), to); let sig = sign_transaction_data(&data, &self.get_keypair(), to, &last_id);
Event::Transaction { Event::Transaction {
from, from,
to: *to, to: *to,
data, data,
last_id,
sig, sig,
} }
} }
@ -70,11 +78,16 @@ impl Genesis {
events events
} }
pub fn create_entries(&self) -> Vec<Entry<u64>> {
create_entries(&self.get_seed(), self.create_events())
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use log::verify_slice_u64;
#[test] #[test]
fn test_create_events() { fn test_create_events() {
@ -97,4 +110,16 @@ mod tests {
3 3
); );
} }
#[test]
fn test_verify_entries() {
let entries = Genesis::new(100, vec![]).create_entries();
assert!(verify_slice_u64(&entries, &entries[0].id));
}
#[test]
fn test_verify_entries_with_transactions() {
let entries = Genesis::new(100, vec![Creator::new(42)]).create_entries();
assert!(verify_slice_u64(&entries, &entries[0].id));
}
} }

View File

@ -1,21 +1,20 @@
//! The `historian` crate provides a microservice for generating a Proof-of-History. //! The `historian` crate provides a microservice for generating a Proof-of-History.
//! It manages a thread containing a Proof-of-History Logger. //! It manages a thread containing a Proof-of-History Logger.
use std::thread::JoinHandle; use std::thread::{spawn, JoinHandle};
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::time::Instant; use std::time::Instant;
use log::{hash, Entry, Sha256Hash}; use log::{hash, Entry, Sha256Hash};
use logger::{verify_event_and_reserve_signature, ExitReason, Logger}; use logger::{ExitReason, Logger};
use event::{Event, Signature}; use event::{get_signature, Event, Signature};
use serde::Serialize; use serde::Serialize;
use std::fmt::Debug; use std::fmt::Debug;
use std::thread;
pub struct Historian<T> { pub struct Historian<T> {
pub sender: SyncSender<Event<T>>, pub sender: SyncSender<Event<T>>,
pub receiver: Receiver<Entry<T>>, pub receiver: Receiver<Entry<T>>,
pub thread_hdl: JoinHandle<(Entry<T>, ExitReason)>, pub thread_hdl: JoinHandle<ExitReason>,
pub signatures: HashSet<Signature>, pub signatures: HashSet<Signature>,
} }
@ -34,10 +33,6 @@ impl<T: 'static + Serialize + Clone + Debug + Send> Historian<T> {
} }
} }
pub fn verify_event(self: &mut Self, event: &Event<T>) -> bool {
return verify_event_and_reserve_signature(&mut self.signatures, event);
}
/// A background thread that will continue tagging received Event messages and /// A background thread that will continue tagging received Event messages and
/// sending back Entry messages until either the receiver or sender channel is closed. /// sending back Entry messages until either the receiver or sender channel is closed.
fn create_logger( fn create_logger(
@ -45,12 +40,12 @@ impl<T: 'static + Serialize + Clone + Debug + Send> Historian<T> {
ms_per_tick: Option<u64>, ms_per_tick: Option<u64>,
receiver: Receiver<Event<T>>, receiver: Receiver<Event<T>>,
sender: SyncSender<Entry<T>>, sender: SyncSender<Entry<T>>,
) -> JoinHandle<(Entry<T>, ExitReason)> { ) -> JoinHandle<ExitReason> {
thread::spawn(move || { spawn(move || {
let mut logger = Logger::new(receiver, sender, start_hash); let mut logger = Logger::new(receiver, sender, start_hash);
let now = Instant::now(); let now = Instant::now();
loop { loop {
if let Err(err) = logger.log_events(now, ms_per_tick) { if let Err(err) = logger.process_events(now, ms_per_tick) {
return err; return err;
} }
logger.last_id = hash(&logger.last_id); logger.last_id = hash(&logger.last_id);
@ -60,6 +55,16 @@ impl<T: 'static + Serialize + Clone + Debug + Send> Historian<T> {
} }
} }
pub fn reserve_signature<T>(sigs: &mut HashSet<Signature>, event: &Event<T>) -> bool {
if let Some(sig) = get_signature(&event) {
if sigs.contains(&sig) {
return false;
}
sigs.insert(sig);
}
true
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -85,7 +90,7 @@ mod tests {
drop(hist.sender); drop(hist.sender);
assert_eq!( assert_eq!(
hist.thread_hdl.join().unwrap().1, hist.thread_hdl.join().unwrap(),
ExitReason::RecvDisconnected ExitReason::RecvDisconnected
); );
@ -99,26 +104,38 @@ mod tests {
drop(hist.receiver); drop(hist.receiver);
hist.sender.send(Event::Tick).unwrap(); hist.sender.send(Event::Tick).unwrap();
assert_eq!( assert_eq!(
hist.thread_hdl.join().unwrap().1, hist.thread_hdl.join().unwrap(),
ExitReason::SendDisconnected ExitReason::SendDisconnected
); );
} }
#[test]
fn test_duplicate_event_signature() {
let keypair = generate_keypair();
let to = get_pubkey(&keypair);
let data = b"hello, world";
let zero = Sha256Hash::default();
let sig = sign_claim_data(&data, &keypair, &zero);
let event0 = Event::new_claim(to, &data, zero, sig);
let mut sigs = HashSet::new();
assert!(reserve_signature(&mut sigs, &event0));
assert!(!reserve_signature(&mut sigs, &event0));
}
#[test] #[test]
fn test_ticking_historian() { fn test_ticking_historian() {
let zero = Sha256Hash::default(); let zero = Sha256Hash::default();
let hist = Historian::new(&zero, Some(20)); let hist = Historian::new(&zero, Some(20));
sleep(Duration::from_millis(30)); sleep(Duration::from_millis(30));
hist.sender.send(Event::Tick).unwrap(); hist.sender.send(Event::Tick).unwrap();
sleep(Duration::from_millis(15));
drop(hist.sender); drop(hist.sender);
assert_eq!(
hist.thread_hdl.join().unwrap().1,
ExitReason::RecvDisconnected
);
let entries: Vec<Entry<Sha256Hash>> = hist.receiver.iter().collect(); let entries: Vec<Entry<Sha256Hash>> = hist.receiver.iter().collect();
assert!(entries.len() > 1);
assert!(verify_slice(&entries, &zero)); // Ensure one entry is sent back for each tick sent in.
assert_eq!(entries.len(), 1);
// Ensure the ID is not the seed, which indicates another Tick
// was logged before the one we sent.
assert_ne!(entries[0].id, zero);
} }
} }

View File

@ -14,5 +14,6 @@ extern crate ring;
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
extern crate serde_json;
extern crate sha2; extern crate sha2;
extern crate untrusted; extern crate untrusted;

View File

@ -217,8 +217,18 @@ mod tests {
// First, verify entries // First, verify entries
let keypair = generate_keypair(); let keypair = generate_keypair();
let event0 = Event::new_claim(get_pubkey(&keypair), zero, sign_claim_data(&zero, &keypair)); let event0 = Event::new_claim(
let event1 = Event::new_claim(get_pubkey(&keypair), one, sign_claim_data(&one, &keypair)); get_pubkey(&keypair),
zero,
zero,
sign_claim_data(&zero, &keypair, &zero),
);
let event1 = Event::new_claim(
get_pubkey(&keypair),
one,
zero,
sign_claim_data(&one, &keypair, &zero),
);
let events = vec![event0, event1]; let events = vec![event0, event1];
let mut entries = create_entries(&zero, events); let mut entries = create_entries(&zero, events);
assert!(verify_slice(&entries, &zero)); assert!(verify_slice(&entries, &zero));
@ -235,8 +245,13 @@ mod tests {
fn test_claim() { fn test_claim() {
let keypair = generate_keypair(); let keypair = generate_keypair();
let data = hash(b"hello, world"); let data = hash(b"hello, world");
let event0 = Event::new_claim(get_pubkey(&keypair), data, sign_claim_data(&data, &keypair));
let zero = Sha256Hash::default(); let zero = Sha256Hash::default();
let event0 = Event::new_claim(
get_pubkey(&keypair),
data,
zero,
sign_claim_data(&data, &keypair, &zero),
);
let entries = create_entries(&zero, vec![event0]); let entries = create_entries(&zero, vec![event0]);
assert!(verify_slice(&entries, &zero)); assert!(verify_slice(&entries, &zero));
} }
@ -244,18 +259,20 @@ mod tests {
#[test] #[test]
fn test_wrong_data_claim_attack() { fn test_wrong_data_claim_attack() {
let keypair = generate_keypair(); let keypair = generate_keypair();
let zero = Sha256Hash::default();
let event0 = Event::new_claim( let event0 = Event::new_claim(
get_pubkey(&keypair), get_pubkey(&keypair),
hash(b"goodbye cruel world"), hash(b"goodbye cruel world"),
sign_claim_data(&hash(b"hello, world"), &keypair), zero,
sign_claim_data(&hash(b"hello, world"), &keypair, &zero),
); );
let zero = Sha256Hash::default();
let entries = create_entries(&zero, vec![event0]); let entries = create_entries(&zero, vec![event0]);
assert!(!verify_slice(&entries, &zero)); assert!(!verify_slice(&entries, &zero));
} }
#[test] #[test]
fn test_transfer() { fn test_transfer() {
let zero = Sha256Hash::default();
let keypair0 = generate_keypair(); let keypair0 = generate_keypair();
let keypair1 = generate_keypair(); let keypair1 = generate_keypair();
let pubkey1 = get_pubkey(&keypair1); let pubkey1 = get_pubkey(&keypair1);
@ -264,9 +281,9 @@ mod tests {
from: get_pubkey(&keypair0), from: get_pubkey(&keypair0),
to: pubkey1, to: pubkey1,
data, data,
sig: sign_transaction_data(&data, &keypair0, &pubkey1), last_id: zero,
sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero),
}; };
let zero = Sha256Hash::default();
let entries = create_entries(&zero, vec![event0]); let entries = create_entries(&zero, vec![event0]);
assert!(verify_slice(&entries, &zero)); assert!(verify_slice(&entries, &zero));
} }
@ -277,13 +294,14 @@ mod tests {
let keypair1 = generate_keypair(); let keypair1 = generate_keypair();
let pubkey1 = get_pubkey(&keypair1); let pubkey1 = get_pubkey(&keypair1);
let data = hash(b"hello, world"); let data = hash(b"hello, world");
let zero = Sha256Hash::default();
let event0 = Event::Transaction { let event0 = Event::Transaction {
from: get_pubkey(&keypair0), from: get_pubkey(&keypair0),
to: pubkey1, to: pubkey1,
data: hash(b"goodbye cruel world"), // <-- attack! data: hash(b"goodbye cruel world"), // <-- attack!
sig: sign_transaction_data(&data, &keypair0, &pubkey1), last_id: zero,
sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero),
}; };
let zero = Sha256Hash::default();
let entries = create_entries(&zero, vec![event0]); let entries = create_entries(&zero, vec![event0]);
assert!(!verify_slice(&entries, &zero)); assert!(!verify_slice(&entries, &zero));
} }
@ -295,13 +313,14 @@ mod tests {
let thief_keypair = generate_keypair(); let thief_keypair = generate_keypair();
let pubkey1 = get_pubkey(&keypair1); let pubkey1 = get_pubkey(&keypair1);
let data = hash(b"hello, world"); let data = hash(b"hello, world");
let zero = Sha256Hash::default();
let event0 = Event::Transaction { let event0 = Event::Transaction {
from: get_pubkey(&keypair0), from: get_pubkey(&keypair0),
to: get_pubkey(&thief_keypair), // <-- attack! to: get_pubkey(&thief_keypair), // <-- attack!
data: hash(b"goodbye cruel world"), data: hash(b"goodbye cruel world"),
sig: sign_transaction_data(&data, &keypair0, &pubkey1), last_id: zero,
sig: sign_transaction_data(&data, &keypair0, &pubkey1, &zero),
}; };
let zero = Sha256Hash::default();
let entries = create_entries(&zero, vec![event0]); let entries = create_entries(&zero, vec![event0]);
assert!(!verify_slice(&entries, &zero)); assert!(!verify_slice(&entries, &zero));
} }

View File

@ -5,13 +5,13 @@
//! Event, the latest hash, and the number of hashes since the last event. //! Event, the latest hash, and the number of hashes since the last event.
//! The resulting stream of entries represents ordered events in time. //! The resulting stream of entries represents ordered events in time.
use std::collections::HashSet;
use std::sync::mpsc::{Receiver, SyncSender, TryRecvError}; use std::sync::mpsc::{Receiver, SyncSender, TryRecvError};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use log::{create_entry_mut, Entry, Sha256Hash}; use log::{create_entry_mut, Entry, Sha256Hash};
use event::{get_signature, verify_event, Event, Signature}; use event::Event;
use serde::Serialize; use serde::Serialize;
use std::fmt::Debug; use std::fmt::Debug;
use serde_json;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum ExitReason { pub enum ExitReason {
@ -27,22 +27,6 @@ pub struct Logger<T> {
pub num_ticks: u64, pub num_ticks: u64,
} }
pub fn verify_event_and_reserve_signature<T: Serialize>(
signatures: &mut HashSet<Signature>,
event: &Event<T>,
) -> bool {
if !verify_event(&event) {
return false;
}
if let Some(sig) = get_signature(&event) {
if signatures.contains(&sig) {
return false;
}
signatures.insert(sig);
}
true
}
impl<T: Serialize + Clone + Debug> Logger<T> { impl<T: Serialize + Clone + Debug> Logger<T> {
pub fn new( pub fn new(
receiver: Receiver<Event<T>>, receiver: Receiver<Event<T>>,
@ -58,19 +42,17 @@ impl<T: Serialize + Clone + Debug> Logger<T> {
} }
} }
pub fn log_event(&mut self, event: Event<T>) -> Result<(), (Entry<T>, ExitReason)> { pub fn log_event(&mut self, event: Event<T>) -> Result<Entry<T>, ExitReason> {
let entry = create_entry_mut(&mut self.last_id, &mut self.num_hashes, event); let entry = create_entry_mut(&mut self.last_id, &mut self.num_hashes, event);
if let Err(_) = self.sender.send(entry.clone()) { println!("{}", serde_json::to_string(&entry).unwrap());
return Err((entry, ExitReason::SendDisconnected)); Ok(entry)
}
Ok(())
} }
pub fn log_events( pub fn process_events(
&mut self, &mut self,
epoch: Instant, epoch: Instant,
ms_per_tick: Option<u64>, ms_per_tick: Option<u64>,
) -> Result<(), (Entry<T>, ExitReason)> { ) -> Result<(), ExitReason> {
loop { loop {
if let Some(ms) = ms_per_tick { if let Some(ms) = ms_per_tick {
if epoch.elapsed() > Duration::from_millis((self.num_ticks + 1) * ms) { if epoch.elapsed() > Duration::from_millis((self.num_ticks + 1) * ms) {
@ -78,22 +60,17 @@ impl<T: Serialize + Clone + Debug> Logger<T> {
self.num_ticks += 1; self.num_ticks += 1;
} }
} }
match self.receiver.try_recv() { match self.receiver.try_recv() {
Ok(event) => { Ok(event) => {
self.log_event(event)?; let entry = self.log_event(event)?;
self.sender
.send(entry)
.or(Err(ExitReason::SendDisconnected))?;
} }
Err(TryRecvError::Empty) => { Err(TryRecvError::Empty) => return Ok(()),
return Ok(()); Err(TryRecvError::Disconnected) => return Err(ExitReason::RecvDisconnected),
} };
Err(TryRecvError::Disconnected) => {
let entry = Entry {
id: self.last_id,
num_hashes: self.num_hashes,
event: Event::Tick,
};
return Err((entry, ExitReason::RecvDisconnected));
}
}
} }
} }
} }
@ -103,51 +80,18 @@ mod tests {
use super::*; use super::*;
use log::*; use log::*;
use event::*; use event::*;
use genesis::*;
use std::sync::mpsc::sync_channel;
#[test] #[test]
fn test_bad_event_signature() { fn test_bad_event_signature() {
let zero = Sha256Hash::default();
let keypair = generate_keypair(); let keypair = generate_keypair();
let sig = sign_claim_data(&hash(b"hello, world"), &keypair); let sig = sign_claim_data(&hash(b"hello, world"), &keypair, &zero);
let event0 = Event::new_claim(get_pubkey(&keypair), hash(b"goodbye cruel world"), sig); let event0 = Event::new_claim(
let mut sigs = HashSet::new(); get_pubkey(&keypair),
assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); hash(b"goodbye cruel world"),
assert!(!sigs.contains(&sig)); zero,
} sig,
);
#[test] assert!(!verify_event(&event0));
fn test_duplicate_event_signature() {
let keypair = generate_keypair();
let to = get_pubkey(&keypair);
let data = &hash(b"hello, world");
let sig = sign_claim_data(data, &keypair);
let event0 = Event::new_claim(to, data, sig);
let mut sigs = HashSet::new();
assert!(verify_event_and_reserve_signature(&mut sigs, &event0));
assert!(!verify_event_and_reserve_signature(&mut sigs, &event0));
}
fn run_genesis(gen: Genesis) -> Vec<Entry<u64>> {
let (_sender, event_receiver) = sync_channel(100);
let (entry_sender, receiver) = sync_channel(100);
let mut logger = Logger::new(event_receiver, entry_sender, hash(&gen.pkcs8));
for tx in gen.create_events() {
logger.log_event(tx).unwrap();
}
drop(logger.sender);
receiver.iter().collect::<Vec<_>>()
}
#[test]
fn test_genesis_no_creators() {
let entries = run_genesis(Genesis::new(100, vec![]));
assert!(verify_slice_u64(&entries, &entries[0].id));
}
#[test]
fn test_genesis() {
let entries = run_genesis(Genesis::new(100, vec![Creator::new(42)]));
assert!(verify_slice_u64(&entries, &entries[0].id));
} }
} }