Merge pull request #128 from garious/faster-demo

Utilize parallelized accountant in demo
This commit is contained in:
Greg Fitzgerald 2018-04-27 08:47:42 -06:00 committed by GitHub
commit 7077f4cbe2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 283 additions and 102 deletions

View File

@ -36,6 +36,10 @@ path = "src/bin/genesis-demo.rs"
name = "solana-mint" name = "solana-mint"
path = "src/bin/mint.rs" path = "src/bin/mint.rs"
[[bin]]
name = "solana-mint-demo"
path = "src/bin/mint-demo.rs"
[badges] [badges]
codecov = { repository = "solana-labs/solana", branch = "master", service = "github" } codecov = { repository = "solana-labs/solana", branch = "master", service = "github" }

View File

@ -39,25 +39,28 @@ $ cd solana
The testnode server is initialized with a ledger from stdin and The testnode server is initialized with a ledger from stdin and
generates new ledger entries on stdout. To create the input ledger, we'll need generates new ledger entries on stdout. To create the input ledger, we'll need
to create *the mint* and use it to generate a *genesis ledger*. It's done in to create *the mint* and use it to generate a *genesis ledger*. It's done in
two steps because the mint.json file contains a private key that will be two steps because the mint-demo.json file contains private keys that will be
used later in this demo. used later in this demo.
```bash ```bash
$ echo 1000000000 | cargo run --release --bin solana-mint | tee mint.json $ echo 1000000000 | cargo run --release --bin solana-mint-demo > mint-demo.json
$ cat mint.json | cargo run --release --bin solana-genesis | tee genesis.log $ cat mint-demo.json | cargo run --release --bin solana-genesis-demo > genesis.log
``` ```
Now you can start the server: Now you can start the server:
```bash ```bash
$ cat genesis.log | cargo run --release --bin solana-testnode | tee transactions0.log $ cat genesis.log | cargo run --release --bin solana-testnode > transactions0.log
``` ```
Wait a few seconds for the server to initialize. It will print "Ready." when it's safe
to start sending it transactions.
Then, in a separate shell, let's execute some transactions. Note we pass in Then, in a separate shell, let's execute some transactions. Note we pass in
the JSON configuration file here, not the genesis ledger. the JSON configuration file here, not the genesis ledger.
```bash ```bash
$ cat mint.json | cargo run --release --bin solana-client-demo $ cat mint-demo.json | cargo run --release --bin solana-client-demo
``` ```
Now kill the server with Ctrl-C, and take a look at the ledger. You should Now kill the server with Ctrl-C, and take a look at the ledger. You should
@ -73,14 +76,14 @@ Now restart the server from where we left off. Pass it both the genesis ledger,
the transaction ledger. the transaction ledger.
```bash ```bash
$ cat genesis.log transactions0.log | cargo run --release --bin solana-testnode | tee transactions1.log $ cat genesis.log transactions0.log | cargo run --release --bin solana-testnode > transactions1.log
``` ```
Lastly, run the client demo again, and verify that all funds were spent in the Lastly, run the client demo again, and verify that all funds were spent in the
previous round, and so no additional transactions are added. previous round, and so no additional transactions are added.
```bash ```bash
$ cat mint.json | cargo run --release --bin solana-client-demo $ cat mint-demo.json | cargo run --release --bin solana-client-demo
``` ```
Stop the server again, and verify there are only Tick entries, and no Transaction entries. Stop the server again, and verify there are only Tick entries, and no Transaction entries.

View File

@ -33,6 +33,7 @@ pub struct AccountantSkel<W: Write + Send + 'static> {
last_id: Hash, last_id: Hash,
writer: W, writer: W,
historian: Historian, historian: Historian,
entry_info_subscribers: Vec<SocketAddr>,
} }
#[cfg_attr(feature = "cargo-clippy", allow(large_enum_variant))] #[cfg_attr(feature = "cargo-clippy", allow(large_enum_variant))]
@ -41,6 +42,19 @@ pub enum Request {
Transaction(Transaction), Transaction(Transaction),
GetBalance { key: PublicKey }, GetBalance { key: PublicKey },
GetLastId, GetLastId,
Subscribe { subscriptions: Vec<Subscription> },
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Subscription {
EntryInfo,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EntryInfo {
pub id: Hash,
pub num_hashes: u64,
pub num_events: u64,
} }
impl Request { impl Request {
@ -56,7 +70,7 @@ impl Request {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum Response { pub enum Response {
Balance { key: PublicKey, val: Option<i64> }, Balance { key: PublicKey, val: Option<i64> },
Entries { entries: Vec<Entry> }, EntryInfo(EntryInfo),
LastId { id: Hash }, LastId { id: Hash },
} }
@ -68,6 +82,22 @@ impl<W: Write + Send + 'static> AccountantSkel<W> {
last_id, last_id,
writer, writer,
historian, historian,
entry_info_subscribers: vec![],
}
}
fn notify_entry_info_subscribers(&mut self, entry: &Entry) {
// TODO: No need to bind().
let socket = UdpSocket::bind("127.0.0.1:0").expect("bind");
for addr in &self.entry_info_subscribers {
let entry_info = EntryInfo {
id: entry.id,
num_hashes: entry.num_hashes,
num_events: entry.events.len() as u64,
};
let data = serialize(&Response::EntryInfo(entry_info)).expect("serialize EntryInfo");
let _res = socket.send_to(&data, addr);
} }
} }
@ -77,6 +107,7 @@ impl<W: Write + Send + 'static> AccountantSkel<W> {
self.last_id = entry.id; self.last_id = entry.id;
self.acc.register_entry_id(&self.last_id); self.acc.register_entry_id(&self.last_id);
writeln!(self.writer, "{}", serde_json::to_string(&entry).unwrap()).unwrap(); writeln!(self.writer, "{}", serde_json::to_string(&entry).unwrap()).unwrap();
self.notify_entry_info_subscribers(&entry);
} }
self.last_id self.last_id
} }
@ -94,6 +125,14 @@ impl<W: Write + Send + 'static> AccountantSkel<W> {
} }
Request::GetLastId => Some((Response::LastId { id: self.sync() }, rsp_addr)), Request::GetLastId => Some((Response::LastId { id: self.sync() }, rsp_addr)),
Request::Transaction(_) => unreachable!(), Request::Transaction(_) => unreachable!(),
Request::Subscribe { subscriptions } => {
for subscription in subscriptions {
match subscription {
Subscription::EntryInfo => self.entry_info_subscribers.push(rsp_addr),
}
}
None
}
} }
} }
@ -242,7 +281,11 @@ impl<W: Write + Send + 'static> AccountantSkel<W> {
blob_sender.send(blobs)?; blob_sender.send(blobs)?;
} }
packet_recycler.recycle(msgs); packet_recycler.recycle(msgs);
// Write new entries to the ledger and notify subscribers.
obj.lock().unwrap().sync();
} }
Ok(()) Ok(())
} }
@ -286,8 +329,13 @@ impl<W: Write + Send + 'static> AccountantSkel<W> {
&packet_recycler, &packet_recycler,
&blob_recycler, &blob_recycler,
); );
if e.is_err() && exit.load(Ordering::Relaxed) { if e.is_err() {
break; // Assume this was a timeout, so sync any empty entries.
skel.lock().unwrap().sync();
if exit.load(Ordering::Relaxed) {
break;
}
} }
}); });
Ok(vec![t_receiver, t_responder, t_server, t_verifier]) Ok(vec![t_receiver, t_responder, t_server, t_verifier])
@ -426,7 +474,7 @@ mod tests {
let socket = UdpSocket::bind(send_addr).unwrap(); let socket = UdpSocket::bind(send_addr).unwrap();
socket.set_read_timeout(Some(Duration::new(5, 0))).unwrap(); socket.set_read_timeout(Some(Duration::new(5, 0))).unwrap();
let acc = AccountantStub::new(&addr, socket); let mut acc = AccountantStub::new(&addr, socket);
let last_id = acc.get_last_id().wait().unwrap(); let last_id = acc.get_last_id().wait().unwrap();
let tr = Transaction::new(&alice.keypair(), bob_pubkey, 500, last_id); let tr = Transaction::new(&alice.keypair(), bob_pubkey, 500, last_id);

View File

@ -3,11 +3,12 @@
//! this object instead of writing messages to the network directly. The binary //! this object instead of writing messages to the network directly. The binary
//! encoding of its messages are unstable and may change in future releases. //! encoding of its messages are unstable and may change in future releases.
use accountant_skel::{Request, Response}; use accountant_skel::{Request, Response, Subscription};
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use futures::future::{err, ok, FutureResult}; use futures::future::{ok, FutureResult};
use hash::Hash; use hash::Hash;
use signature::{KeyPair, PublicKey, Signature}; use signature::{KeyPair, PublicKey, Signature};
use std::collections::HashMap;
use std::io; use std::io;
use std::net::UdpSocket; use std::net::UdpSocket;
use transaction::Transaction; use transaction::Transaction;
@ -15,6 +16,9 @@ use transaction::Transaction;
pub struct AccountantStub { pub struct AccountantStub {
pub addr: String, pub addr: String,
pub socket: UdpSocket, pub socket: UdpSocket,
last_id: Option<Hash>,
num_events: u64,
balances: HashMap<PublicKey, Option<i64>>,
} }
impl AccountantStub { impl AccountantStub {
@ -22,9 +26,43 @@ impl AccountantStub {
/// over `socket`. To receive responses, the caller must bind `socket` /// over `socket`. To receive responses, the caller must bind `socket`
/// to a public address before invoking AccountantStub methods. /// to a public address before invoking AccountantStub methods.
pub fn new(addr: &str, socket: UdpSocket) -> Self { pub fn new(addr: &str, socket: UdpSocket) -> Self {
AccountantStub { let stub = AccountantStub {
addr: addr.to_string(), addr: addr.to_string(),
socket, socket,
last_id: None,
num_events: 0,
balances: HashMap::new(),
};
stub.init();
stub
}
pub fn init(&self) {
let subscriptions = vec![Subscription::EntryInfo];
let req = Request::Subscribe { subscriptions };
let data = serialize(&req).expect("serialize Subscribe");
let _res = self.socket.send_to(&data, &self.addr);
}
pub fn recv_response(&self) -> io::Result<Response> {
let mut buf = vec![0u8; 1024];
self.socket.recv_from(&mut buf)?;
let resp = deserialize(&buf).expect("deserialize balance");
Ok(resp)
}
pub fn process_response(&mut self, resp: Response) {
match resp {
Response::Balance { key, val } => {
self.balances.insert(key, val);
}
Response::LastId { id } => {
self.last_id = Some(id);
}
Response::EntryInfo(entry_info) => {
self.last_id = Some(entry_info.id);
self.num_events += entry_info.num_events;
}
} }
} }
@ -52,42 +90,67 @@ impl AccountantStub {
/// Request the balance of the user holding `pubkey`. This method blocks /// Request the balance of the user holding `pubkey`. This method blocks
/// until the server sends a response. If the response packet is dropped /// until the server sends a response. If the response packet is dropped
/// by the network, this method will hang indefinitely. /// by the network, this method will hang indefinitely.
pub fn get_balance(&self, pubkey: &PublicKey) -> FutureResult<i64, i64> { pub fn get_balance(&mut self, pubkey: &PublicKey) -> FutureResult<i64, i64> {
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 self.socket
.send_to(&data, &self.addr) .send_to(&data, &self.addr)
.expect("buffer error"); .expect("buffer error");
let mut buf = vec![0u8; 1024]; let mut done = false;
self.socket.recv_from(&mut buf).expect("buffer error"); while !done {
let resp = deserialize(&buf).expect("deserialize balance"); let resp = self.recv_response().expect("recv response");
if let Response::Balance { key, val } = resp { if let &Response::Balance { ref key, .. } = &resp {
assert_eq!(key, *pubkey); done = key == pubkey;
return match val { }
Some(x) => ok(x), self.process_response(resp);
_ => err(0),
};
} }
err(0) ok(self.balances[pubkey].unwrap())
} }
/// Request the last Entry ID from the server. This method blocks /// Request the last Entry ID from the server. This method blocks
/// until the server sends a response. At the time of this writing, /// until the server sends a response. At the time of this writing,
/// it also has the side-effect of causing the server to log any /// it also has the side-effect of causing the server to log any
/// entries that have been published by the Historian. /// entries that have been published by the Historian.
pub fn get_last_id(&self) -> FutureResult<Hash, ()> { pub fn get_last_id(&mut self) -> FutureResult<Hash, ()> {
let req = Request::GetLastId; let req = Request::GetLastId;
let data = serialize(&req).expect("serialize GetId"); let data = serialize(&req).expect("serialize GetId");
self.socket self.socket
.send_to(&data, &self.addr) .send_to(&data, &self.addr)
.expect("buffer error"); .expect("buffer error");
let mut buf = vec![0u8; 1024]; let mut done = false;
self.socket.recv_from(&mut buf).expect("buffer error"); while !done {
let resp = deserialize(&buf).expect("deserialize Id"); let resp = self.recv_response().expect("recv response");
if let Response::LastId { id } = resp { if let &Response::LastId { .. } = &resp {
return ok(id); done = true;
}
self.process_response(resp);
} }
ok(Default::default()) ok(self.last_id.unwrap_or(Hash::default()))
}
/// Return the number of transactions the server processed since creating
/// this stub instance.
pub fn transaction_count(&mut self) -> u64 {
// Wait for at least one EntryInfo.
let mut done = false;
while !done {
let resp = self.recv_response().expect("recv response");
if let &Response::EntryInfo(_) = &resp {
done = true;
}
self.process_response(resp);
}
// Then take the rest.
self.socket.set_nonblocking(true).expect("set nonblocking");
loop {
match self.recv_response() {
Err(_) => break,
Ok(resp) => self.process_response(resp),
}
}
self.socket.set_nonblocking(false).expect("set blocking");
self.num_events
} }
} }
@ -128,7 +191,7 @@ mod tests {
let socket = UdpSocket::bind(send_addr).unwrap(); let socket = UdpSocket::bind(send_addr).unwrap();
socket.set_read_timeout(Some(Duration::new(5, 0))).unwrap(); socket.set_read_timeout(Some(Duration::new(5, 0))).unwrap();
let acc = AccountantStub::new(addr, socket); let mut acc = AccountantStub::new(addr, socket);
let last_id = acc.get_last_id().wait().unwrap(); let last_id = acc.get_last_id().wait().unwrap();
let _sig = acc.transfer(500, &alice.keypair(), bob_pubkey, &last_id) let _sig = acc.transfer(500, &alice.keypair(), bob_pubkey, &last_id)
.unwrap(); .unwrap();

View File

@ -4,21 +4,22 @@ extern crate isatty;
extern crate rayon; extern crate rayon;
extern crate serde_json; extern crate serde_json;
extern crate solana; extern crate solana;
extern crate untrusted;
use futures::Future; use futures::Future;
use getopts::Options; use getopts::Options;
use isatty::stdin_isatty; use isatty::stdin_isatty;
use rayon::prelude::*; use rayon::prelude::*;
use solana::accountant_stub::AccountantStub; use solana::accountant_stub::AccountantStub;
use solana::mint::Mint; use solana::mint::MintDemo;
use solana::signature::{KeyPair, KeyPairUtil}; use solana::signature::{KeyPair, KeyPairUtil};
use solana::transaction::Transaction; use solana::transaction::Transaction;
use std::env; use std::env;
use std::io::{stdin, Read}; use std::io::{stdin, Read};
use std::net::UdpSocket; use std::net::UdpSocket;
use std::process::exit; use std::process::exit;
use std::thread::sleep; use std::time::Instant;
use std::time::{Duration, Instant}; use untrusted::Input;
fn print_usage(program: &str, opts: Options) { fn print_usage(program: &str, opts: Options) {
let mut brief = format!("Usage: cat <mint.json> | {} [options]\n\n", program); let mut brief = format!("Usage: cat <mint.json> | {} [options]\n\n", program);
@ -75,74 +76,68 @@ fn main() {
exit(1); exit(1);
} }
let mint: Mint = serde_json::from_str(&buffer).unwrap_or_else(|e| { println!("Parsing stdin...");
let demo: MintDemo = serde_json::from_str(&buffer).unwrap_or_else(|e| {
eprintln!("failed to parse json: {}", e); eprintln!("failed to parse json: {}", e);
exit(1); exit(1);
}); });
let mint_keypair = mint.keypair();
let mint_pubkey = mint.pubkey();
let socket = UdpSocket::bind(&send_addr).unwrap(); let socket = UdpSocket::bind(&send_addr).unwrap();
println!("Stub new"); let mut acc = AccountantStub::new(&addr, socket);
let acc = AccountantStub::new(&addr, socket);
println!("Get last id"); println!("Get last ID...");
let last_id = acc.get_last_id().wait().unwrap(); let last_id = acc.get_last_id().wait().unwrap();
println!("Get Balance"); println!("Creating keypairs...");
let mint_balance = acc.get_balance(&mint_pubkey).wait().unwrap(); let txs = demo.users.len() / 2;
println!("Mint's Initial Balance {}", mint_balance); let keypairs: Vec<_> = demo.users
.into_par_iter()
.map(|(pkcs8, _)| KeyPair::from_pkcs8(Input::from(&pkcs8)).unwrap())
.collect();
let keypair_pairs: Vec<_> = keypairs.chunks(2).collect();
println!("Signing transactions..."); println!("Signing transactions...");
let txs = 1_000_000;
let now = Instant::now(); let now = Instant::now();
let transactions: Vec<_> = (0..txs) let transactions: Vec<_> = keypair_pairs
.into_par_iter() .into_par_iter()
.map(|_| { .map(|chunk| Transaction::new(&chunk[0], chunk[1].pubkey(), 1, last_id))
let rando_pubkey = KeyPair::new().pubkey();
Transaction::new(&mint_keypair, rando_pubkey, 1, last_id)
})
.collect(); .collect();
let duration = now.elapsed(); let duration = now.elapsed();
let ns = duration.as_secs() * 2_000_000_000 + u64::from(duration.subsec_nanos()); let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
let bsps = f64::from(txs) / ns as f64; let bsps = txs as f64 / ns as f64;
let nsps = ns as f64 / f64::from(txs); let nsps = ns as f64 / txs as f64;
println!( println!(
"Done. {} thousand signatures per second, {}us per signature", "Done. {} thousand signatures per second, {}us per signature",
bsps * 1_000_000_f64, bsps * 1_000_000_f64,
nsps / 1_000_f64 nsps / 1_000_f64
); );
let initial_tx_count = acc.transaction_count();
println!("Transfering {} transactions in {} batches", txs, threads); println!("Transfering {} transactions in {} batches", txs, threads);
let now = Instant::now(); let now = Instant::now();
let sz = transactions.len() / threads; let sz = transactions.len() / threads;
let chunks: Vec<_> = transactions.chunks(sz).collect(); let chunks: Vec<_> = transactions.chunks(sz).collect();
let _: Vec<_> = chunks chunks.into_par_iter().for_each(|trs| {
.into_par_iter() println!("Transferring 1 unit {} times...", trs.len());
.map(|trs| { let send_addr = "0.0.0.0:0";
println!("Transferring 1 unit {} times...", trs.len()); let socket = UdpSocket::bind(send_addr).unwrap();
let send_addr = "0.0.0.0:0"; let acc = AccountantStub::new(&addr, socket);
let socket = UdpSocket::bind(send_addr).unwrap(); for tr in trs {
let acc = AccountantStub::new(&addr, socket); acc.transfer_signed(tr.clone()).unwrap();
for tr in trs { }
acc.transfer_signed(tr.clone()).unwrap(); });
}
() println!("Waiting for half the transactions to complete...",);
}) let mut tx_count = acc.transaction_count();
.collect(); while tx_count < transactions.len() as u64 / 2 {
println!("Waiting for last transaction to be confirmed...",); tx_count = acc.transaction_count();
let mut val = mint_balance;
let mut prev = 0;
while val != prev {
sleep(Duration::from_millis(20));
prev = val;
val = acc.get_balance(&mint_pubkey).wait().unwrap();
} }
println!("Mint's Final Balance {}", val); let txs = tx_count - initial_tx_count;
let txs = mint_balance - val; println!("Transactions processed {}", txs);
println!("Successful transactions {}", txs);
let duration = now.elapsed(); let duration = now.elapsed();
let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos()); let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
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);
} }

View File

@ -1,21 +1,23 @@
extern crate isatty; extern crate isatty;
extern crate rayon;
extern crate ring;
extern crate serde_json; extern crate serde_json;
extern crate solana; extern crate solana;
extern crate untrusted;
use isatty::stdin_isatty; use isatty::stdin_isatty;
use solana::entry::create_entry; use rayon::prelude::*;
use solana::accountant::MAX_ENTRY_IDS;
use solana::entry::{create_entry, next_tick};
use solana::event::Event; use solana::event::Event;
use solana::hash::Hash; use solana::mint::MintDemo;
use solana::mint::Mint; use solana::signature::{KeyPair, KeyPairUtil};
use solana::signature::{KeyPair, KeyPairUtil, PublicKey};
use solana::transaction::Transaction; use solana::transaction::Transaction;
use std::io::{stdin, Read}; use std::io::{stdin, Read};
use std::process::exit; use std::process::exit;
use untrusted::Input;
fn transfer(from: &KeyPair, (to, tokens): (PublicKey, i64), last_id: Hash) -> Event { // Generate a ledger with lots and lots of accounts.
Event::Transaction(Transaction::new(from, to, tokens, last_id))
}
fn main() { fn main() {
if stdin_isatty() { if stdin_isatty() {
eprintln!("nothing found on stdin, expected a json file"); eprintln!("nothing found on stdin, expected a json file");
@ -29,20 +31,39 @@ fn main() {
exit(1); exit(1);
} }
let mint: Mint = serde_json::from_str(&buffer).unwrap_or_else(|e| { let demo: MintDemo = serde_json::from_str(&buffer).unwrap_or_else(|e| {
eprintln!("failed to parse json: {}", e); eprintln!("failed to parse json: {}", e);
exit(1); exit(1);
}); });
let mut entries = mint.create_entries();
let from = mint.keypair(); let num_accounts = demo.users.len();
let seed = mint.seed(); let last_id = demo.mint.last_id();
let alice = (KeyPair::new().pubkey(), 200); let mint_keypair = demo.mint.keypair();
let bob = (KeyPair::new().pubkey(), 100);
let events = vec![transfer(&from, alice, seed), transfer(&from, bob, seed)];
entries.push(create_entry(&seed, 0, events));
for entry in entries { eprintln!("Signing {} transactions...", num_accounts);
let events: Vec<_> = demo.users
.into_par_iter()
.map(|(pkcs8, tokens)| {
let rando = KeyPair::from_pkcs8(Input::from(&pkcs8)).unwrap();
let tr = Transaction::new(&mint_keypair, rando.pubkey(), tokens, last_id);
Event::Transaction(tr)
})
.collect();
for entry in demo.mint.create_entries() {
println!("{}", serde_json::to_string(&entry).unwrap());
}
eprintln!("Logging the creation of {} accounts...", num_accounts);
let entry = create_entry(&last_id, 0, events);
println!("{}", serde_json::to_string(&entry).unwrap());
eprintln!("Creating {} empty entries...", MAX_ENTRY_IDS);
// Offer client lots of entry IDs to use for each transaction's last_id.
let mut last_id = last_id;
for _ in 0..MAX_ENTRY_IDS {
let entry = next_tick(&last_id, 1);
last_id = entry.id;
let serialized = serde_json::to_string(&entry).unwrap_or_else(|e| { let serialized = serde_json::to_string(&entry).unwrap_or_else(|e| {
eprintln!("failed to serialize: {}", e); eprintln!("failed to serialize: {}", e);
exit(1); exit(1);

33
src/bin/mint-demo.rs Normal file
View File

@ -0,0 +1,33 @@
extern crate rayon;
extern crate ring;
extern crate serde_json;
extern crate solana;
use rayon::prelude::*;
use ring::rand::SystemRandom;
use solana::mint::{Mint, MintDemo};
use solana::signature::KeyPair;
use std::io;
fn main() {
let mut input_text = String::new();
io::stdin().read_line(&mut input_text).unwrap();
let trimmed = input_text.trim();
let tokens = trimmed.parse::<i64>().unwrap();
let mint = Mint::new(tokens);
let tokens_per_user = 1_000;
let num_accounts = tokens / tokens_per_user;
let rnd = SystemRandom::new();
let users: Vec<_> = (0..num_accounts)
.into_par_iter()
.map(|_| {
let pkcs8 = KeyPair::generate_pkcs8(&rnd).unwrap().to_vec();
(pkcs8, tokens_per_user)
})
.collect();
let demo = MintDemo { mint, users };
println!("{}", serde_json::to_string(&demo).unwrap());
}

View File

@ -62,6 +62,7 @@ fn main() {
exit(1); exit(1);
} }
eprintln!("Initializing...");
let mut entries = buffer.lines().map(|line| { let mut entries = buffer.lines().map(|line| {
serde_json::from_str(&line).unwrap_or_else(|e| { serde_json::from_str(&line).unwrap_or_else(|e| {
eprintln!("failed to parse json: {}", e); eprintln!("failed to parse json: {}", e);
@ -71,7 +72,7 @@ fn main() {
// The first item in the ledger is required to be an entry with zero num_hashes, // The first item in the ledger is required to be an entry with zero num_hashes,
// which implies its id can be used as the ledger's seed. // which implies its id can be used as the ledger's seed.
entries.next().unwrap(); let entry0 = entries.next().unwrap();
// The second item in the ledger is a special transaction where the to and from // The second item in the ledger is a special transaction where the to and from
// fields are the same. That entry should be treated as a deposit, not a // fields are the same. That entry should be treated as a deposit, not a
@ -84,11 +85,14 @@ fn main() {
}; };
let acc = Accountant::new_from_deposit(&deposit.unwrap()); let acc = Accountant::new_from_deposit(&deposit.unwrap());
acc.register_entry_id(&entry0.id);
acc.register_entry_id(&entry1.id);
let mut last_id = entry1.id; let mut last_id = entry1.id;
for entry in entries { for entry in entries {
last_id = entry.id; last_id = entry.id;
acc.process_verified_events(entry.events).unwrap(); acc.process_verified_events(entry.events).unwrap();
acc.register_entry_id(&last_id);
} }
let historian = Historian::new(&last_id, Some(1000)); let historian = Historian::new(&last_id, Some(1000));
@ -99,8 +103,8 @@ fn main() {
stdout(), stdout(),
historian, historian,
))); )));
eprintln!("Listening on {}", addr);
let threads = AccountantSkel::serve(&skel, &addr, exit.clone()).unwrap(); let threads = AccountantSkel::serve(&skel, &addr, exit.clone()).unwrap();
eprintln!("Ready. Listening on {}", addr);
for t in threads { for t in threads {
t.join().expect("join"); t.join().expect("join");
} }

View File

@ -61,7 +61,7 @@ fn add_event_data(hash_data: &mut Vec<u8>, event: &Event) {
} }
/// Creates the hash `num_hashes` after `start_hash`. If the event contains /// Creates the hash `num_hashes` after `start_hash`. If the event contains
/// signature, the final hash will be a hash of both the previous ID and /// a signature, the final hash will be a hash of both the previous ID and
/// the signature. /// the signature.
pub fn next_hash(start_hash: &Hash, num_hashes: u64, events: &[Event]) -> Hash { pub fn next_hash(start_hash: &Hash, num_hashes: u64, events: &[Event]) -> Hash {
let mut id = *start_hash; let mut id = *start_hash;
@ -76,10 +76,12 @@ pub fn next_hash(start_hash: &Hash, num_hashes: u64, events: &[Event]) -> Hash {
} }
if !hash_data.is_empty() { if !hash_data.is_empty() {
return extend_and_hash(&id, &hash_data); extend_and_hash(&id, &hash_data)
} else if num_hashes != 0 {
hash(&id)
} else {
id
} }
id
} }
/// Creates the next Entry `num_hashes` after `start_hash`. /// Creates the next Entry `num_hashes` after `start_hash`.
@ -167,6 +169,8 @@ mod tests {
#[test] #[test]
fn test_next_tick() { fn test_next_tick() {
let zero = Hash::default(); let zero = Hash::default();
assert_eq!(next_tick(&zero, 1).num_hashes, 1) let tick = next_tick(&zero, 1);
assert_eq!(tick.num_hashes, 1);
assert_ne!(tick.id, zero);
} }
} }

View File

@ -58,6 +58,12 @@ impl Mint {
} }
} }
#[derive(Serialize, Deserialize, Debug)]
pub struct MintDemo {
pub mint: Mint,
pub users: Vec<(Vec<u8>, i64)>,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;