solana/src/ledger.rs

234 lines
7.5 KiB
Rust
Raw Normal View History

2018-03-30 10:43:38 -07:00
//! The `ledger` module provides functions for parallel verification of the
2018-03-29 11:20:54 -07:00
//! Proof of History ledger.
use bincode::{self, deserialize, serialize_into, serialized_size};
use entry::Entry;
use hash::Hash;
use packet::{self, SharedBlob, BLOB_DATA_SIZE, BLOB_SIZE};
2018-03-04 06:28:51 -08:00
use rayon::prelude::*;
use std::collections::VecDeque;
use std::io::Cursor;
2018-05-23 23:29:01 -07:00
use transaction::Transaction;
// a Block is a slice of Entries
2018-04-02 10:36:51 -07:00
pub trait Block {
2018-05-25 14:51:41 -07:00
/// Verifies the hashes and counts of a slice of transactions are all consistent.
2018-04-02 10:36:51 -07:00
fn verify(&self, start_hash: &Hash) -> bool;
2018-05-29 10:52:00 -07:00
fn to_blobs(&self, blob_recycler: &packet::BlobRecycler, q: &mut VecDeque<SharedBlob>);
2018-04-02 10:36:51 -07:00
}
impl Block for [Entry] {
fn verify(&self, start_hash: &Hash) -> bool {
let genesis = [Entry::new_tick(0, start_hash)];
let entry_pairs = genesis.par_iter().chain(self).zip(self);
entry_pairs.all(|(x0, x1)| x1.verify(&x0.id))
}
2018-05-29 10:52:00 -07:00
fn to_blobs(&self, blob_recycler: &packet::BlobRecycler, q: &mut VecDeque<SharedBlob>) {
for entry in self {
let blob = blob_recycler.allocate();
let pos = {
let mut bd = blob.write().unwrap();
let mut out = Cursor::new(bd.data_mut());
serialize_into(&mut out, &entry).expect("failed to serialize output");
out.position() as usize
};
assert!(pos < BLOB_SIZE);
blob.write().unwrap().set_size(pos);
q.push_back(blob);
2018-05-29 10:52:00 -07:00
}
}
}
pub fn reconstruct_entries_from_blobs(
blobs: VecDeque<SharedBlob>,
blob_recycler: &packet::BlobRecycler,
) -> bincode::Result<Vec<Entry>> {
let mut entries: Vec<Entry> = Vec::with_capacity(blobs.len());
for blob in blobs {
let entry = {
let msg = blob.read().unwrap();
deserialize(&msg.data()[..msg.meta.size])
};
blob_recycler.recycle(blob);
match entry {
Ok(entry) => entries.push(entry),
Err(err) => {
trace!("reconstruct_entry_from_blobs: {}", err);
return Err(err);
}
}
}
Ok(entries)
}
/// Creates the next entries for given transactions, outputs
/// updates start_hash to id of last Entry, sets cur_hashes to 0
pub fn next_entries_mut(
start_hash: &mut Hash,
cur_hashes: &mut u64,
transactions: Vec<Transaction>,
) -> Vec<Entry> {
if transactions.is_empty() {
vec![Entry::new_mut(start_hash, cur_hashes, transactions)]
} else {
let mut chunk_len = transactions.len();
// check for fit, make sure they can be serialized
while serialized_size(&Entry {
num_hashes: 0,
id: Hash::default(),
transactions: transactions[0..chunk_len].to_vec(),
}).unwrap() > BLOB_DATA_SIZE as u64
{
chunk_len /= 2;
}
let mut entries = Vec::with_capacity(transactions.len() / chunk_len + 1);
for chunk in transactions.chunks(chunk_len) {
entries.push(Entry::new_mut(start_hash, cur_hashes, chunk.to_vec()));
}
entries
}
}
/// Creates the next Entries for given transactions
pub fn next_entries(
start_hash: &Hash,
cur_hashes: u64,
transactions: Vec<Transaction>,
) -> Vec<Entry> {
let mut id = *start_hash;
let mut num_hashes = cur_hashes;
next_entries_mut(&mut id, &mut num_hashes, transactions)
}
#[cfg(test)]
mod tests {
use super::*;
use entry::{next_entry, Entry};
use hash::hash;
use packet::BlobRecycler;
use signature::{KeyPair, KeyPairUtil};
2018-06-16 08:18:42 -07:00
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use transaction::Transaction;
/// Create a vector of Entries of length `transaction_batches.len()`
/// from `start_hash` hash, `num_hashes`, and `transaction_batches`.
fn next_entries_batched(
start_hash: &Hash,
cur_hashes: u64,
transaction_batches: Vec<Vec<Transaction>>,
) -> Vec<Entry> {
let mut id = *start_hash;
let mut entries = vec![];
let mut num_hashes = cur_hashes;
for transactions in transaction_batches {
let mut entry_batch = next_entries_mut(&mut id, &mut num_hashes, transactions);
entries.append(&mut entry_batch);
}
entries
}
#[test]
fn test_verify_slice() {
let zero = Hash::default();
let one = hash(&zero);
2018-04-02 10:36:51 -07:00
assert!(vec![][..].verify(&zero)); // base case
assert!(vec![Entry::new_tick(0, &zero)][..].verify(&zero)); // singleton case 1
assert!(!vec![Entry::new_tick(0, &zero)][..].verify(&one)); // singleton case 2, bad
assert!(next_entries_batched(&zero, 0, vec![vec![]; 2])[..].verify(&zero)); // inductive step
let mut bad_ticks = next_entries_batched(&zero, 0, vec![vec![]; 2]);
2018-03-04 06:34:38 -08:00
bad_ticks[1].id = one;
2018-04-02 10:36:51 -07:00
assert!(!bad_ticks.verify(&zero)); // inductive step, bad
}
#[test]
fn test_entries_to_blobs() {
let zero = Hash::default();
let one = hash(&zero);
let keypair = KeyPair::new();
2018-05-29 09:12:27 -07:00
let tx0 = Transaction::new(&keypair, keypair.pubkey(), 1, one);
let transactions = vec![tx0; 10_000];
let entries = next_entries(&zero, 0, transactions);
let blob_recycler = BlobRecycler::default();
let mut blob_q = VecDeque::new();
2018-05-29 10:52:00 -07:00
entries.to_blobs(&blob_recycler, &mut blob_q);
assert_eq!(
reconstruct_entries_from_blobs(blob_q, &blob_recycler).unwrap(),
entries
);
2018-06-16 08:18:42 -07:00
}
#[test]
fn test_bad_blobs_attack() {
let blob_recycler = BlobRecycler::default();
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
let blobs_q = packet::to_blobs(vec![(0, addr)], &blob_recycler).unwrap(); // <-- attack!
assert!(reconstruct_entries_from_blobs(blobs_q, &blob_recycler).is_err());
}
#[test]
fn test_next_entries_batched() {
// this also tests next_entries, ugly, but is an easy way to do vec of vec (batch)
let mut id = Hash::default();
let next_id = hash(&id);
let keypair = KeyPair::new();
2018-05-29 09:12:27 -07:00
let tx0 = Transaction::new(&keypair, keypair.pubkey(), 1, next_id);
2018-06-21 16:02:52 -07:00
let transactions = vec![tx0; 5];
2018-05-25 14:51:41 -07:00
let transaction_batches = vec![transactions.clone(); 5];
let entries0 = next_entries_batched(&id, 0, transaction_batches);
2018-05-11 09:34:46 -07:00
assert_eq!(entries0.len(), 5);
let mut entries1 = vec![];
2018-05-11 09:40:31 -07:00
for _ in 0..5 {
let entry = next_entry(&id, 1, transactions.clone());
id = entry.id;
entries1.push(entry);
}
2018-05-11 09:34:46 -07:00
assert_eq!(entries0, entries1);
}
}
#[cfg(all(feature = "unstable", test))]
mod bench {
extern crate test;
use self::test::Bencher;
use hash::hash;
use ledger::*;
use packet::BlobRecycler;
use signature::{KeyPair, KeyPairUtil};
use transaction::Transaction;
#[bench]
fn bench_block_to_blobs_to_block(bencher: &mut Bencher) {
let zero = Hash::default();
let one = hash(&zero);
let keypair = KeyPair::new();
let tx0 = Transaction::new(&keypair, keypair.pubkey(), 1, one);
let transactions = vec![tx0; 10];
let entries = next_entries(&zero, 1, transactions);
let blob_recycler = BlobRecycler::default();
bencher.iter(|| {
let mut blob_q = VecDeque::new();
entries.to_blobs(&blob_recycler, &mut blob_q);
assert_eq!(
reconstruct_entries_from_blobs(blob_q, &blob_recycler).unwrap(),
entries
);
});
}
}