diff --git a/Cargo.toml b/Cargo.toml index 83ac0e5957..99637e0cbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,14 @@ path = "src/bin/client-demo.rs" name = "silk-testnode" path = "src/bin/testnode.rs" +[[bin]] +name = "silk-genesis-block" +path = "src/bin/genesis-block.rs" + +[[bin]] +name = "silk-genesis-file-demo" +path = "src/bin/genesis-file-demo.rs" + [badges] codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" } @@ -37,6 +45,7 @@ sha2-asm = {version="0.3", optional=true} generic-array = { version = "0.9.0", default-features = false, features = ["serde"] } serde = "1.0.27" serde_derive = "1.0.27" +serde_json = "1.0.10" ring = "0.12.1" untrusted = "0.5.1" bincode = "1.0.0" diff --git a/src/accountant.rs b/src/accountant.rs index ebaec39c4d..8f9f3595ab 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -2,8 +2,9 @@ //! event log to record transactions. Its users can deposit funds and //! transfer funds to other users. -use log::{Entry, Sha256Hash}; +use log::{hash, Entry, Sha256Hash}; use event::{Event, PublicKey, Signature}; +use genesis::Genesis; use historian::Historian; use ring::signature::Ed25519KeyPair; use std::sync::mpsc::SendError; @@ -26,13 +27,18 @@ pub struct Accountant { } impl Accountant { - pub fn new(start_hash: &Sha256Hash, ms_per_tick: Option) -> Self { - let hist = Historian::::new(start_hash, ms_per_tick); - Accountant { + pub fn new(gen: &Genesis, ms_per_tick: Option) -> Self { + let start_hash = hash(&gen.pkcs8); + let hist = Historian::::new(&start_hash, ms_per_tick); + let mut acc = Accountant { historian: hist, balances: HashMap::new(), - end_hash: *start_hash, + end_hash: start_hash, + }; + for (i, event) in gen.create_events().into_iter().enumerate() { + acc.process_verified_event(event, i < 2).unwrap(); } + acc } pub fn sync(self: &mut Self) -> Vec> { @@ -48,70 +54,62 @@ impl Accountant { entries } - pub fn deposit_signed(self: &mut Self, to: PublicKey, data: u64, sig: Signature) -> Result<()> { - let event = Event::new_claim(to, data, sig); - if !self.historian.verify_event(&event) { - return Err(AccountingError::InvalidEvent); - } - if let Err(SendError(_)) = self.historian.sender.send(event) { - return Err(AccountingError::SendError); - } - - if self.balances.contains_key(&to) { - if let Some(x) = self.balances.get_mut(&to) { - *x += data; - } - } else { - self.balances.insert(to, data); - } - - Ok(()) - } - pub fn deposit(self: &mut Self, n: u64, keypair: &Ed25519KeyPair) -> Result { use event::{get_pubkey, sign_claim_data}; - let key = get_pubkey(keypair); + let to = get_pubkey(keypair); let sig = sign_claim_data(&n, keypair); - self.deposit_signed(key, n, sig).map(|_| sig) - } - - pub fn transfer_signed( - self: &mut Self, - from: PublicKey, - to: PublicKey, - data: u64, - sig: Signature, - ) -> Result<()> { - if self.get_balance(&from).unwrap_or(0) < data { - return Err(AccountingError::InsufficientFunds); - } - - let event = Event::Transaction { - from: Some(from), - to, - data, - sig, - }; + let event = Event::new_claim(to, n, sig); if !self.historian.verify_event(&event) { return Err(AccountingError::InvalidEvent); } - if let Err(SendError(_)) = self.historian.sender.send(event) { - return Err(AccountingError::SendError); - } + self.process_verified_event(event, true).map(|_| sig) + } - if let Some(x) = self.balances.get_mut(&from) { - *x -= data; - } + fn is_deposit(allow_deposits: bool, from: &PublicKey, to: &PublicKey) -> bool { + allow_deposits && from == to + } - if self.balances.contains_key(&to) { - if let Some(x) = self.balances.get_mut(&to) { - *x += data; + pub fn process_event(self: &mut Self, event: Event) -> Result<()> { + if !self.historian.verify_event(&event) { + return Err(AccountingError::InvalidEvent); + } + self.process_verified_event(event, false) + } + + fn process_verified_event( + self: &mut Self, + event: Event, + allow_deposits: bool, + ) -> Result<()> { + match event { + Event::Tick => Ok(()), + 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) { + 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.balances.contains_key(&to) { + if let Some(x) = self.balances.get_mut(&to) { + *x += data; + } + } else { + self.balances.insert(to, data); + } + Ok(()) } - } else { - self.balances.insert(to, data); } - - Ok(()) } pub fn transfer( @@ -123,7 +121,13 @@ impl Accountant { use event::{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).map(|_| sig) + let event = Event::Transaction { + from, + to, + data: n, + sig, + }; + self.process_event(event).map(|_| sig) } pub fn get_balance(self: &Self, pubkey: &PublicKey) -> Option { @@ -153,19 +157,16 @@ mod tests { use super::*; use event::{generate_keypair, get_pubkey}; use logger::ExitReason; + use genesis::Creator; #[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(); - let sig = acc.deposit(1_000, &bob_keypair).unwrap(); - acc.wait_on_signature(&sig); + let bob = Creator::new(1_000); + let bob_pubkey = bob.pubkey; + let alice = Genesis::new(10_000, vec![bob]); + let mut acc = Accountant::new(&alice, Some(2)); - let bob_pubkey = get_pubkey(&bob_keypair); - let sig = acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); + let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey).unwrap(); acc.wait_on_signature(&sig); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); @@ -181,22 +182,18 @@ mod tests { fn test_invalid_transfer() { use std::thread::sleep; use std::time::Duration; - 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(); - let sig = acc.deposit(1_000, &bob_keypair).unwrap(); - acc.wait_on_signature(&sig); + let bob = Creator::new(1_000); + let bob_pubkey = bob.pubkey; + let alice = Genesis::new(11_000, vec![bob]); + let mut acc = Accountant::new(&alice, Some(2)); - let bob_pubkey = get_pubkey(&bob_keypair); assert_eq!( - acc.transfer(10_001, &alice_keypair, bob_pubkey), + acc.transfer(10_001, &alice.get_keypair(), bob_pubkey), Err(AccountingError::InsufficientFunds) ); sleep(Duration::from_millis(30)); - let alice_pubkey = get_pubkey(&alice_keypair); + let alice_pubkey = get_pubkey(&alice.get_keypair()); assert_eq!(acc.get_balance(&alice_pubkey).unwrap(), 10_000); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_000); @@ -207,30 +204,11 @@ mod tests { ); } - #[test] - 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(); - let sig = acc.deposit(2, &keypair).unwrap(); - acc.wait_on_signature(&sig); - - let pubkey = get_pubkey(&keypair); - assert_eq!(acc.get_balance(&pubkey).unwrap(), 3); - - drop(acc.historian.sender); - assert_eq!( - acc.historian.thread_hdl.join().unwrap().1, - ExitReason::RecvDisconnected - ); - } - #[test] fn test_transfer_to_newb() { - let zero = Sha256Hash::default(); - let mut acc = Accountant::new(&zero, Some(2)); - let alice_keypair = generate_keypair(); + let alice = Genesis::new(10_000, vec![]); + let mut acc = Accountant::new(&alice, Some(2)); + let alice_keypair = alice.get_keypair(); let bob_keypair = generate_keypair(); let sig = acc.deposit(10_000, &alice_keypair).unwrap(); acc.wait_on_signature(&sig); diff --git a/src/accountant_skel.rs b/src/accountant_skel.rs index 2aaefde117..7164e690e3 100644 --- a/src/accountant_skel.rs +++ b/src/accountant_skel.rs @@ -1,7 +1,6 @@ use std::io; use accountant::Accountant; -use event::{PublicKey, Signature}; -//use serde::Serialize; +use event::{Event, PublicKey, Signature}; pub struct AccountantSkel { pub obj: Accountant, @@ -9,11 +8,6 @@ pub struct AccountantSkel { #[derive(Serialize, Deserialize, Debug)] pub enum Request { - Deposit { - key: PublicKey, - val: u64, - sig: Signature, - }, Transfer { from: PublicKey, to: PublicKey, @@ -41,14 +35,14 @@ impl AccountantSkel { pub fn process_request(self: &mut Self, msg: Request) -> Option { match msg { - Request::Deposit { key, val, sig } => { - if let Err(err) = self.obj.deposit_signed(key, val, sig) { - println!("Deposit error: {:?}", err); - } - None - } Request::Transfer { from, to, val, sig } => { - if let Err(err) = self.obj.transfer_signed(from, to, val, sig) { + let event = Event::Transaction { + from, + to, + data: val, + sig, + }; + if let Err(err) = self.obj.process_event(event) { println!("Transfer error: {:?}", err); } None diff --git a/src/accountant_stub.rs b/src/accountant_stub.rs index add8608dba..8ce5feb3b9 100644 --- a/src/accountant_stub.rs +++ b/src/accountant_stub.rs @@ -22,24 +22,6 @@ impl AccountantStub { } } - pub fn deposit_signed( - self: &Self, - key: PublicKey, - val: u64, - sig: Signature, - ) -> io::Result { - let req = Request::Deposit { key, val, sig }; - let data = serialize(&req).unwrap(); - self.socket.send_to(&data, &self.addr) - } - - pub fn deposit(self: &Self, n: u64, keypair: &Ed25519KeyPair) -> io::Result { - use event::{get_pubkey, sign_claim_data}; - let key = get_pubkey(keypair); - let sig = sign_claim_data(&n, keypair); - self.deposit_signed(key, n, sig).map(|_| sig) - } - pub fn transfer_signed( self: &Self, from: PublicKey, @@ -100,32 +82,22 @@ mod tests { use accountant_skel::AccountantSkel; use std::thread::{sleep, spawn}; use std::time::Duration; - use log::Sha256Hash; - use event::{generate_keypair, get_pubkey}; + use genesis::{Creator, Genesis}; #[test] fn test_accountant_stub() { let addr = "127.0.0.1:9000"; let send_addr = "127.0.0.1:9001"; - spawn(move || { - let zero = Sha256Hash::default(); - let acc = Accountant::new(&zero, None); - let mut skel = AccountantSkel::new(acc); - skel.serve(addr).unwrap(); - }); - + let bob = Creator::new(1_000); + let bob_pubkey = bob.pubkey; + let alice = Genesis::new(10_000, vec![bob]); + let acc = Accountant::new(&alice, None); + spawn(move || AccountantSkel::new(acc).serve(addr).unwrap()); sleep(Duration::from_millis(30)); let socket = UdpSocket::bind(send_addr).unwrap(); let acc = AccountantStub::new(addr, socket); - let alice_keypair = generate_keypair(); - let bob_keypair = generate_keypair(); - acc.deposit(10_000, &alice_keypair).unwrap(); - let sig = acc.deposit(1_000, &bob_keypair).unwrap(); - acc.wait_on_signature(&sig).unwrap(); - - let bob_pubkey = get_pubkey(&bob_keypair); - let sig = acc.transfer(500, &alice_keypair, bob_pubkey).unwrap(); + let sig = acc.transfer(500, &alice.get_keypair(), bob_pubkey).unwrap(); acc.wait_on_signature(&sig).unwrap(); assert_eq!(acc.get_balance(&bob_pubkey).unwrap(), 1_500); } diff --git a/src/bin/client-demo.rs b/src/bin/client-demo.rs index e40d837677..a5dc6ab8f3 100644 --- a/src/bin/client-demo.rs +++ b/src/bin/client-demo.rs @@ -1,24 +1,36 @@ +//extern crate serde_json; extern crate silk; +//use std::io::stdin; + fn main() { use silk::accountant_stub::AccountantStub; + use silk::accountant_skel::AccountantSkel; + use silk::accountant::Accountant; + use silk::event::{generate_keypair, get_pubkey, sign_transaction_data}; + use silk::genesis::Genesis; use std::time::Instant; use std::net::UdpSocket; - use silk::event::{generate_keypair, get_pubkey, sign_transaction_data}; + use std::thread::{sleep, spawn}; + use std::time::Duration; let addr = "127.0.0.1:8000"; let send_addr = "127.0.0.1:8001"; + + let txs = 200; + + let gen = Genesis::new(txs, vec![]); + 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 acc = AccountantStub::new(addr, socket); - let alice_keypair = generate_keypair(); let alice_pubkey = get_pubkey(&alice_keypair); - let txs = 2_000; - println!("Depositing {} units in Alice's account...", txs); - let sig = acc.deposit(txs, &alice_keypair).unwrap(); - acc.wait_on_signature(&sig).unwrap(); - assert_eq!(acc.get_balance(&alice_pubkey).unwrap(), txs); - println!("Done."); - let one = 1; println!("Signing transactions..."); let now = Instant::now(); @@ -45,7 +57,7 @@ fn main() { let now = Instant::now(); for &(k, s) in &sigs { let e = Event::Transaction { - from: Some(alice_pubkey), + from: alice_pubkey, to: k, data: one, sig: s, @@ -64,7 +76,7 @@ fn main() { println!("Transferring 1 unit {} times...", txs); let now = Instant::now(); - let mut sig = sig; + let mut sig = Default::default(); for (k, s) in sigs { acc.transfer_signed(alice_pubkey, k, one, s).unwrap(); sig = s; diff --git a/src/bin/genesis-block.rs b/src/bin/genesis-block.rs new file mode 100644 index 0000000000..eeb7c1730c --- /dev/null +++ b/src/bin/genesis-block.rs @@ -0,0 +1,35 @@ +//! A command-line executable for generating the chain's genesis block. + +extern crate ring; +extern crate serde_json; +extern crate silk; + +use silk::genesis::Genesis; +use silk::log::{hash, verify_slice_u64}; +use silk::logger::Logger; +use std::sync::mpsc::sync_channel; +use std::io::stdin; + +fn main() { + let gen: Genesis = serde_json::from_reader(stdin()).unwrap(); + + 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); + + let entries = receiver.iter().collect::>(); + verify_slice_u64(&entries, &entries[0].end_hash); + println!("["); + let len = entries.len(); + 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!("]"); +} diff --git a/src/bin/genesis-file-demo.rs b/src/bin/genesis-file-demo.rs new file mode 100644 index 0000000000..65f28edaf2 --- /dev/null +++ b/src/bin/genesis-file-demo.rs @@ -0,0 +1,19 @@ +extern crate serde_json; +extern crate silk; + +use silk::genesis::{Creator, Genesis}; +use silk::event::{generate_keypair, get_pubkey}; + +fn main() { + let alice = Creator { + tokens: 200, + pubkey: get_pubkey(&generate_keypair()), + }; + let bob = Creator { + tokens: 100, + pubkey: get_pubkey(&generate_keypair()), + }; + let creators = vec![alice, bob]; + let gen = Genesis::new(300, creators); + println!("{}", serde_json::to_string(&gen).unwrap()); +} diff --git a/src/bin/testnode.rs b/src/bin/testnode.rs index 4231eda358..141fffb904 100644 --- a/src/bin/testnode.rs +++ b/src/bin/testnode.rs @@ -1,13 +1,15 @@ +extern crate serde_json; extern crate silk; use silk::accountant_skel::AccountantSkel; use silk::accountant::Accountant; -use silk::log::Sha256Hash; +use silk::genesis::Genesis; +use std::io::stdin; fn main() { let addr = "127.0.0.1:8000"; - let zero = Sha256Hash::default(); - let acc = Accountant::new(&zero, Some(1000)); + let gen: Genesis = serde_json::from_reader(stdin()).unwrap(); + let acc = Accountant::new(&gen, Some(1000)); let mut skel = AccountantSkel::new(acc); println!("Listening on {}", addr); skel.serve(addr).unwrap(); diff --git a/src/event.rs b/src/event.rs index 3342bcd2d5..ef57121c64 100644 --- a/src/event.rs +++ b/src/event.rs @@ -30,7 +30,7 @@ pub type Signature = GenericArray; pub enum Event { Tick, Transaction { - from: Option, + from: PublicKey, to: PublicKey, data: T, sig: Signature, @@ -40,7 +40,7 @@ pub enum Event { impl Event { pub fn new_claim(to: PublicKey, data: T, sig: Signature) -> Self { Event::Transaction { - from: None, + from: to, to, data, sig, @@ -75,15 +75,13 @@ pub fn sign_transaction_data( keypair: &Ed25519KeyPair, to: &PublicKey, ) -> Signature { - let from = &Some(get_pubkey(keypair)); + let from = &get_pubkey(keypair); sign_serialized(&(from, to, data), keypair) } /// Return a signature for the given data using the private key from the given keypair. pub fn sign_claim_data(data: &T, keypair: &Ed25519KeyPair) -> Signature { - let to = get_pubkey(keypair); - let from: Option = None; - sign_serialized(&(&from, &to, data), keypair) + sign_transaction_data(data, keypair, &get_pubkey(keypair)) } /// Verify a signed message with the given public key. @@ -113,7 +111,7 @@ pub fn verify_event(event: &Event) -> bool { } = *event { let sign_data = serialize(&(&from, &to, &data)).unwrap(); - if !verify_signature(&from.unwrap_or(to), &sign_data, &sig) { + if !verify_signature(&from, &sign_data, &sig) { return false; } } diff --git a/src/genesis.rs b/src/genesis.rs new file mode 100644 index 0000000000..7982411796 --- /dev/null +++ b/src/genesis.rs @@ -0,0 +1,100 @@ +//! A library for generating the chain's genesis block. + +use event::{generate_keypair, get_pubkey, sign_transaction_data, Event, PublicKey}; +use ring::rand::SystemRandom; +use ring::signature::Ed25519KeyPair; +use untrusted::Input; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Creator { + pub pubkey: PublicKey, + pub tokens: u64, +} + +impl Creator { + pub fn new(tokens: u64) -> Self { + Creator { + pubkey: get_pubkey(&generate_keypair()), + tokens, + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Genesis { + pub pkcs8: Vec, + pub tokens: u64, + pub creators: Vec, +} + +impl Genesis { + pub fn new(tokens: u64, creators: Vec) -> Self { + let rnd = SystemRandom::new(); + let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap().to_vec(); + Genesis { + pkcs8, + tokens, + creators, + } + } + + pub fn get_keypair(&self) -> Ed25519KeyPair { + Ed25519KeyPair::from_pkcs8(Input::from(&self.pkcs8)).unwrap() + } + + pub fn get_pubkey(&self) -> PublicKey { + get_pubkey(&self.get_keypair()) + } + + pub fn create_transaction(&self, data: u64, to: &PublicKey) -> Event { + let from = self.get_pubkey(); + let sig = sign_transaction_data(&data, &self.get_keypair(), to); + Event::Transaction { + from, + to: *to, + data, + sig, + } + } + + pub fn create_events(&self) -> Vec> { + let pubkey = self.get_pubkey(); + let event0 = Event::Tick; + let event1 = self.create_transaction(self.tokens, &pubkey); + let mut events = vec![event0, event1]; + + for x in &self.creators { + let tx = self.create_transaction(x.tokens, &x.pubkey); + events.push(tx); + } + + events + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_events() { + let mut events = Genesis::new(100, vec![]).create_events().into_iter(); + assert_eq!(events.next().unwrap(), Event::Tick); + if let Event::Transaction { from, to, .. } = events.next().unwrap() { + assert_eq!(from, to); + } else { + assert!(false); + } + assert_eq!(events.next(), None); + } + + #[test] + fn test_create_creator() { + assert_eq!( + Genesis::new(100, vec![Creator::new(42)]) + .create_events() + .len(), + 3 + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 588c798493..78dbeadbcd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod log; pub mod logger; pub mod event; +pub mod genesis; pub mod historian; pub mod accountant; pub mod accountant_skel; diff --git a/src/log.rs b/src/log.rs index c1a4a7ee4f..8878445f21 100644 --- a/src/log.rs +++ b/src/log.rs @@ -255,7 +255,7 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let event0 = Event::Transaction { - from: Some(get_pubkey(&keypair0)), + from: get_pubkey(&keypair0), to: pubkey1, data, sig: sign_transaction_data(&data, &keypair0, &pubkey1), @@ -272,7 +272,7 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let event0 = Event::Transaction { - from: Some(get_pubkey(&keypair0)), + from: get_pubkey(&keypair0), to: pubkey1, data: hash(b"goodbye cruel world"), // <-- attack! sig: sign_transaction_data(&data, &keypair0, &pubkey1), @@ -290,7 +290,7 @@ mod tests { let pubkey1 = get_pubkey(&keypair1); let data = hash(b"hello, world"); let event0 = Event::Transaction { - from: Some(get_pubkey(&keypair0)), + from: get_pubkey(&keypair0), to: get_pubkey(&thief_keypair), // <-- attack! data: hash(b"goodbye cruel world"), sig: sign_transaction_data(&data, &keypair0, &pubkey1), diff --git a/src/logger.rs b/src/logger.rs index ce8040711c..3d930814c4 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -110,6 +110,8 @@ mod tests { use super::*; use log::*; use event::*; + use genesis::*; + use std::sync::mpsc::sync_channel; #[test] fn test_bad_event_signature() { @@ -132,4 +134,27 @@ mod tests { assert!(verify_event_and_reserve_signature(&mut sigs, &event0)); assert!(!verify_event_and_reserve_signature(&mut sigs, &event0)); } + + fn run_genesis(gen: Genesis) -> Vec> { + 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::>() + } + + #[test] + fn test_genesis_no_creators() { + let entries = run_genesis(Genesis::new(100, vec![])); + assert!(verify_slice_u64(&entries, &entries[0].end_hash)); + } + + #[test] + fn test_genesis() { + let entries = run_genesis(Genesis::new(100, vec![Creator::new(42)])); + assert!(verify_slice_u64(&entries, &entries[0].end_hash)); + } }