Skip to content

Commit

Permalink
Explain proof-of-history in the readme
Browse files Browse the repository at this point in the history
And Hash userdata so that verification works as the readme describes.

Fixes solana-labs#8
  • Loading branch information
garious committed Feb 20, 2018
1 parent e57bba1 commit 15b2a6a
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 21 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use std::sync::mpsc::SendError;
fn create_log(hist: &Historian) -> Result<(), SendError<Event>> {
hist.sender.send(Event::Tick)?;
thread::sleep(time::Duration::new(0, 100_000));
hist.sender.send(Event::UserDataKey(0xdeadbeef))?;
hist.sender.send(Event::UserDataKey(Sha256Hash::default()))?;
thread::sleep(time::Duration::new(0, 100_000));
hist.sender.send(Event::Tick)?;
Ok(())
Expand All @@ -50,6 +50,9 @@ fn main() {
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));
}
```
Expand All @@ -62,6 +65,21 @@ Entry { num_hashes: 6, end_hash: [67, ...], event: UserDataKey(3735928559) }
Entry { num_hashes: 5, end_hash: [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/Curry–Howard_correspondence) For each entry returned by the
historian, we can verify that `end_hash` is the result of applying a sha256 hash to the previous `end_hash`
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. So long as this
proof is verified and signed faster than a malicious historian could regenerate those hashes *and* be
verified, this history is, for all practical purposes, carved in stone.

# Developing

Expand Down
2 changes: 1 addition & 1 deletion src/bin/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::sync::mpsc::SendError;
fn create_log(hist: &Historian) -> Result<(), SendError<Event>> {
hist.sender.send(Event::Tick)?;
thread::sleep(time::Duration::new(0, 100_000));
hist.sender.send(Event::UserDataKey(0xdeadbeef))?;
hist.sender.send(Event::UserDataKey(Sha256Hash::default()))?;
thread::sleep(time::Duration::new(0, 100_000));
hist.sender.send(Event::Tick)?;
Ok(())
Expand Down
31 changes: 16 additions & 15 deletions src/historian.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use std::thread::JoinHandle;
use std::sync::mpsc::{Receiver, Sender};
use log::{hash, Entry, Event, Sha256Hash};
use log::{extend_and_hash, hash, Entry, Event, Sha256Hash};

pub struct Historian {
pub sender: Sender<Event>,
Expand All @@ -24,31 +24,33 @@ pub enum ExitReason {
fn log_events(
receiver: &Receiver<Event>,
sender: &Sender<Entry>,
num_hashes: u64,
end_hash: Sha256Hash,
) -> Result<u64, (Entry, ExitReason)> {
num_hashes: &mut u64,
end_hash: &mut Sha256Hash,
) -> Result<(), (Entry, ExitReason)> {
use std::sync::mpsc::TryRecvError;
let mut num_hashes = num_hashes;
loop {
match receiver.try_recv() {
Ok(event) => {
if let Event::UserDataKey(key) = event {
*end_hash = extend_and_hash(end_hash, &key);
}
let entry = Entry {
end_hash,
num_hashes,
end_hash: *end_hash,
num_hashes: *num_hashes,
event,
};
if let Err(_) = sender.send(entry.clone()) {
return Err((entry, ExitReason::SendDisconnected));
}
num_hashes = 0;
*num_hashes = 0;
}
Err(TryRecvError::Empty) => {
return Ok(num_hashes);
return Ok(());
}
Err(TryRecvError::Disconnected) => {
let entry = Entry {
end_hash,
num_hashes,
end_hash: *end_hash,
num_hashes: *num_hashes,
event: Event::Tick,
};
return Err((entry, ExitReason::RecvDisconnected));
Expand All @@ -69,9 +71,8 @@ pub fn create_logger(
let mut end_hash = start_hash;
let mut num_hashes = 0;
loop {
match log_events(&receiver, &sender, num_hashes, end_hash) {
Ok(n) => num_hashes = n,
Err(err) => return err,
if let Err(err) = log_events(&receiver, &sender, &mut num_hashes, &mut end_hash) {
return err;
}
end_hash = hash(&end_hash);
num_hashes += 1;
Expand Down Expand Up @@ -108,7 +109,7 @@ mod tests {

hist.sender.send(Event::Tick).unwrap();
sleep(Duration::new(0, 1_000_000));
hist.sender.send(Event::UserDataKey(0xdeadbeef)).unwrap();
hist.sender.send(Event::UserDataKey(zero)).unwrap();
sleep(Duration::new(0, 1_000_000));
hist.sender.send(Event::Tick).unwrap();

Expand Down
21 changes: 17 additions & 4 deletions src/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,30 @@ pub struct Entry {
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Event {
Tick,
UserDataKey(u64),
UserDataKey(Sha256Hash),
}

impl Entry {
/// Creates a Entry from the number of hashes 'num_hashes' since the previous event
/// and that resulting 'end_hash'.
pub fn new_tick(num_hashes: u64, end_hash: &Sha256Hash) -> Self {
let event = Event::Tick;
Entry {
num_hashes,
end_hash: *end_hash,
event,
event: Event::Tick,
}
}

/// Verifies self.end_hash is the result of hashing a 'start_hash' 'self.num_hashes' times.
/// If the event is a UserDataKey, then hash that as well.
pub fn verify(self: &Self, start_hash: &Sha256Hash) -> bool {
self.end_hash == next_tick(start_hash, self.num_hashes).end_hash
let tick = next_tick(start_hash, self.num_hashes);
let end_hash = if let Event::UserDataKey(key) = self.event {
extend_and_hash(&tick.end_hash, &key)
} else {
tick.end_hash
};
self.end_hash == end_hash
}
}

Expand All @@ -60,6 +66,13 @@ pub fn hash(val: &[u8]) -> Sha256Hash {
hasher.result()
}

/// Return the hash of the given hash extended with the given value.
pub fn extend_and_hash(end_hash: &Sha256Hash, val: &[u8]) -> Sha256Hash {
let mut hash_data = end_hash.to_vec();
hash_data.extend_from_slice(val);
hash(&hash_data)
}

/// Creates the next Tick Entry 'num_hashes' after 'start_hash'.
pub fn next_tick(start_hash: &Sha256Hash, num_hashes: u64) -> Entry {
let mut end_hash = *start_hash;
Expand Down

0 comments on commit 15b2a6a

Please sign in to comment.