diff --git a/Cargo.toml b/Cargo.toml index 83ac0e5957..7b6da8b559 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,10 @@ 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" + [badges] codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" } @@ -37,6 +41,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/bin/genesis-block.rs b/src/bin/genesis-block.rs new file mode 100644 index 0000000000..875456451b --- /dev/null +++ b/src/bin/genesis-block.rs @@ -0,0 +1,36 @@ +//! A command-line executable for generating the chain's genesis block. + +extern crate ring; +extern crate serde; +extern crate serde_json; +extern crate silk; + +use silk::genesis::Genesis; +use silk::log::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, gen.seed); + for tx in gen.create_events() { + logger.log_event(tx).unwrap(); + } + drop(logger.sender); + + let entries = receiver.iter().collect::>(); + verify_slice_u64(&entries, &gen.seed); + 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/genesis.rs b/src/genesis.rs new file mode 100644 index 0000000000..47d50716bd --- /dev/null +++ b/src/genesis.rs @@ -0,0 +1,98 @@ +//! A library for generating the chain's genesis block. + +use event::{generate_keypair, get_pubkey, sign_claim_data, sign_transaction_data, Event, PublicKey}; +use log::Sha256Hash; +use ring::rand::{SecureRandom, SystemRandom}; +use ring::signature::Ed25519KeyPair; +use untrusted::Input; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Creator { + pub name: String, + pub pubkey: PublicKey, + pub tokens: u64, +} + +impl Creator { + pub fn new(name: &str, tokens: u64) -> Self { + Creator { + name: name.to_string(), + pubkey: get_pubkey(&generate_keypair()), + tokens, + } + } + + pub fn create_transaction(&self, keypair: &Ed25519KeyPair) -> Event { + let from = Some(get_pubkey(keypair)); + let to = self.pubkey; + let data = self.tokens; + let sig = sign_transaction_data(&data, keypair, &to); + Event::Transaction { + from, + to, + data, + sig, + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Genesis { + pub seed: Sha256Hash, + pub pkcs8: Vec, + pub tokens: u64, + pub creators: Vec, +} + +impl Genesis { + pub fn new(tokens: u64, creators: Vec) -> Self { + let rnd = SystemRandom::new(); + let mut seed_arr = [0; 32]; + rnd.fill(&mut seed_arr).unwrap(); + let seed = *Sha256Hash::from_slice(&seed_arr); + let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rnd).unwrap().to_vec(); + Genesis { + seed, + pkcs8, + tokens, + creators, + } + } + + pub fn create_events(&self) -> Vec> { + let org_keypair = Ed25519KeyPair::from_pkcs8(Input::from(&self.pkcs8)).unwrap(); + let sig = sign_claim_data(&self.tokens, &org_keypair); + let event0 = Event::new_claim(get_pubkey(&org_keypair), self.tokens, sig); + let mut events = vec![event0]; + + for creator in &self.creators { + let tx = creator.create_transaction(&org_keypair); + events.push(tx); + } + + events + } +} + +#[cfg(test)] +mod tests { + use super::*; + use event::verify_event; + + #[test] + fn test_creator_transaction() { + assert!(verify_event(&Creator::new("Satoshi", 42) + .create_transaction(&generate_keypair()))); + } + + #[test] + fn test_create_events() { + assert_eq!(Genesis::new(100, vec![]).create_events().len(), 1); + assert_eq!( + Genesis::new(100, vec![Creator::new("Satoshi", 42)]) + .create_events() + .len(), + 2 + ); + } +} 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/logger.rs b/src/logger.rs index ce8040711c..53ad387c2d 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, gen.seed); + for tx in gen.create_events() { + logger.log_event(tx).unwrap(); + } + drop(logger.sender); + receiver.iter().collect::>() + } + + #[test] + fn test_genesis_no_creators() { + let gen = Genesis::new(100, vec![]); + assert!(verify_slice_u64(&run_genesis(&gen), &gen.seed)); + } + + #[test] + fn test_genesis() { + let gen = Genesis::new(100, vec![Creator::new("Satoshi", 42)]); + assert!(verify_slice_u64(&run_genesis(&gen), &gen.seed)); + } }