aboutsummaryrefslogtreecommitdiffstats
path: root/src/register.rs
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2017-10-26 21:10:19 -0700
committerBryan Newbold <bnewbold@robocracy.org>2017-10-26 21:10:22 -0700
commit177d639fab67b790f43bc0573d785271d8afb858 (patch)
tree7eaefcfc7bc508e00aa633c80983ed90179f84ec /src/register.rs
parent594807d6ef0954ff8ca0b99cf41329c0ad3252e7 (diff)
downloadgeniza-177d639fab67b790f43bc0573d785271d8afb858.tar.gz
geniza-177d639fab67b790f43bc0573d785271d8afb858.zip
refactor file/module names
I had some early confusion around whether SLEEP refered to individual files or the collection of files making a register (it seems to mean the whole register). Will probably need to refactor again.
Diffstat (limited to 'src/register.rs')
-rw-r--r--src/register.rs556
1 files changed, 0 insertions, 556 deletions
diff --git a/src/register.rs b/src/register.rs
deleted file mode 100644
index 0313497..0000000
--- a/src/register.rs
+++ /dev/null
@@ -1,556 +0,0 @@
-
-use std::io::prelude::*;
-use std::fs::File;
-use std::path::Path;
-use std::io::SeekFrom;
-use integer_encoding::FixedInt;
-use std::fs::OpenOptions;
-use crypto::blake2b::Blake2b;
-use crypto::digest::Digest;
-use crypto::ed25519;
-use rand::{Rng, OsRng};
-
-use errors::*;
-use sleep::*;
-
-/// Abstract access to Hypercore register
-pub trait HyperRegister {
-
- /// Whether the register store contains the given (data) entry
- fn has(&self, index: u64) -> Result<bool>;
-
- /// Whether the register store contains *all* known (data) entries
- fn has_all(&self) -> Result<bool>;
-
- /// If the contiguous range of entries is in the store
- fn has_range(&self, start: u64, end: u64) -> Result<bool>;
-
- /// Reads a single data entry from the store.
- fn get_data_entry(&mut self, index: u64) -> Result<Vec<u8>>;
-
- /// Writes an entry to the store. Requires the private key to be present.
- fn append(&mut self, data: &[u8]) -> Result<u64>;
-
- /// Count of data entries for this register. This is the total count (highest entry index plus
- /// one); this particular store might be sparse.
- fn len(&self) -> Result<u64>;
-
- /// Total size of this register in bytes.
- fn len_bytes(&mut self) -> Result<u64>;
-
- /// [UNIMPLEMENTED] Intended to do a deeper merkel-tree verification of all stored data
- fn verify(&mut self) -> Result<()>;
-
- /// Quick sanity checks on register store robust-ness
- fn check(&mut self) -> Result<()>;
-
- /// Can this register be appended to?
- fn writable(&self) -> bool;
-
- /// Returns a single tree entry (using tree indexing, not data indexing).
- fn get_tree_entry(&mut self, index: u64) -> Result<Vec<u8>>;
-}
-
-impl HyperRegister {
-
- fn hash_leaf(data: &[u8]) -> [u8; 40] {
- let mut buf = [0; 40];
- u64::to_be(data.len() as u64)
- .encode_fixed(&mut buf[32..40]);
- let mut hash = Blake2b::new(32);
- hash.input(&[0; 1]);
- hash.input(&buf[32..40]);
- hash.input(&data);
- hash.result(&mut buf[0..32]);
- buf
- }
-
- fn hash_parent(lhash: &[u8], rhash: &[u8]) -> [u8; 40] {
- let mut buf = [0; 40];
- // TODO: check overflow
- let sum_size = u64::from_be(FixedInt::decode_fixed(&lhash[32..40])) +
- u64::from_be(FixedInt::decode_fixed(&rhash[32..40]));
- u64::to_be(sum_size as u64)
- .encode_fixed(&mut buf[32..40]);
-
- let mut hash = Blake2b::new(32);
- hash.input(&[1; 1]);
- hash.input(&buf[32..40]);
- hash.input(&lhash[..]);
- hash.input(&rhash[..]);
- hash.result(&mut buf[0..32]);
- buf
- }
-
- pub fn hash_roots(reg: &mut HyperRegister, index: u64) -> Result<Vec<u8>> {
- let mut buf = [0; 40];
- let mut hash = Blake2b::new(32);
- let mut index_buf = [0; 8];
- hash.input(&[2; 1]);
- for ri in HyperRegister::tree_root_nodes(index) {
- u64::to_be(ri).encode_fixed(&mut index_buf);
- let node = reg.get_tree_entry(ri)?;
- hash.input(&node[0..32]);
- hash.input(&index_buf);
- hash.input(&node[32..40]);
- }
- hash.result(&mut buf[0..32]);
- Ok(buf.to_vec())
-
- }
-
- fn tree_root_nodes(data_count: u64) -> Vec<u64> {
- // Calculates the root notes for a given length (of data entries, not tree entries)
- // TODO: this should be an iterator
- // NB: this is a relatively "hot" function, gets called (repeatedly?) on every mutation,
- // and potentially in inner loops of lookups.
- if data_count == 0 {
- return vec![];
- }
-
- // Convert the count to a (descending) list of power-of-2 components
- let mut x = 0;
- let mut components = vec![];
- while 2u64.pow(x) <= data_count {
- if (data_count & 2u64.pow(x)) != 0 {
- components.push(2u64.pow(x));
- }
- x += 1;
- }
- components.reverse();
-
- // Add and accumulate
- let mut accum = 0;
- let mut roots = vec![];
- for x in components {
- roots.push(accum + (x - 1));
- accum += 2*x;
- }
- roots
- }
-
- pub fn get_data_offset(reg: &mut HyperRegister, index: u64) -> Result<u64> {
- // TODO: this is a naive (linear) implementation
- // log(N) would go up previous parent nodes (eg, use root_nodes())
- let mut sum: u64 = 0;
- for i in 0..index {
- let leaf = reg.get_tree_entry(i*2)?;
- sum += u64::from_be(FixedInt::decode_fixed(&leaf[32..40]));
- }
- Ok(sum)
- }
-
- /// Every node has a parent, so this function won't fail unless index is over 2^62, in which
- /// case it would overflow and panics instead.
- fn tree_parent_index(index: u64) -> u64 {
- for i in 0..62 {
- // find lowest-significant zero bit
- if (index & (1 << i)) == 0 {
- // set that bit and clear next higher
- return ((index | (1 << i))) & !(1 << (i+1));
- }
- }
- panic!("Parent lookup overflowed, huge index!");
- }
-
- /// Calling this on a leaf node is an error, as is calling very high node numbers (> 2^62)
- fn tree_child_indices(index: u64) -> Result<(u64,u64)> {
- if index % 2 == 0 {
- bail!("Leaf tree nodes have no children");
- }
- for i in 0..62 {
- // find lowest-significant zero bit...
- if (index & (1 << i)) == 0 {
- // larger child has this bit high, next lower bit cleared
- let right = ((index | (1 << i))) & !(1 << (i-1));
- // smaller child has next lower bit cleared
- let left = index & !(1 << (i-1));
- return Ok((left, right));
- }
- }
- bail!("Child lookup overflowed, huge index!");
- }
-}
-
-#[test]
-fn test_tree_root_nodes() {
- assert_eq!(HyperRegister::tree_root_nodes(0), vec![]);
- assert_eq!(HyperRegister::tree_root_nodes(1), vec![0]);
- assert_eq!(HyperRegister::tree_root_nodes(2), vec![1]);
- assert_eq!(HyperRegister::tree_root_nodes(3), vec![1,4]);
- assert_eq!(HyperRegister::tree_root_nodes(4), vec![3]);
- assert_eq!(HyperRegister::tree_root_nodes(5), vec![3,8]);
- assert_eq!(HyperRegister::tree_root_nodes(6), vec![3,9]);
- assert_eq!(HyperRegister::tree_root_nodes(7), vec![3,9,12]);
- assert_eq!(HyperRegister::tree_root_nodes(8), vec![7]);
-}
-
-#[test]
-fn test_tree_parent_index() {
- assert_eq!(HyperRegister::tree_parent_index(0), 1);
- assert_eq!(HyperRegister::tree_parent_index(1), 3);
- assert_eq!(HyperRegister::tree_parent_index(2), 1);
- assert_eq!(HyperRegister::tree_parent_index(3), 7);
- assert_eq!(HyperRegister::tree_parent_index(4), 5);
- assert_eq!(HyperRegister::tree_parent_index(5), 3);
- assert_eq!(HyperRegister::tree_parent_index(6), 5);
- assert_eq!(HyperRegister::tree_parent_index(7), 15);
- assert_eq!(HyperRegister::tree_parent_index(8), 9);
- assert_eq!(HyperRegister::tree_parent_index(9), 11);
- assert_eq!(HyperRegister::tree_parent_index(21), 19);
- assert_eq!(HyperRegister::tree_parent_index(22), 21);
-}
-
-#[test]
-fn test_tree_child_indices() {
- assert!(HyperRegister::tree_child_indices(0).is_err());
- assert!(HyperRegister::tree_child_indices(1024).is_err());
- assert_eq!(HyperRegister::tree_child_indices(1).unwrap(), (0, 2));
-
- assert_eq!(HyperRegister::tree_child_indices(3).unwrap(), (1, 5));
- assert_eq!(HyperRegister::tree_child_indices(5).unwrap(), (4, 6));
- assert_eq!(HyperRegister::tree_child_indices(7).unwrap(), (3, 11));
- assert_eq!(HyperRegister::tree_child_indices(9).unwrap(), (8, 10));
- assert_eq!(HyperRegister::tree_child_indices(11).unwrap(), (9, 13));
- assert_eq!(HyperRegister::tree_child_indices(13).unwrap(), (12, 14));
- assert_eq!(HyperRegister::tree_child_indices(15).unwrap(), (7, 23));
- assert_eq!(HyperRegister::tree_child_indices(19).unwrap(), (17, 21));
-}
-
-/// Implementation of HyperRegister using a local directory of SLEEP files
-#[derive(Debug)]
-pub struct SleepDirRegister {
- tree_sleep: SleepFile,
- sign_sleep: SleepFile,
- bitfield_sleep: SleepFile,
- data_file: Option<File>,
- // Except, these should be Ed25519 keys, not bytes
- pub_key: Vec<u8>,
- secret_key: Option<Vec<u8>>,
-}
-
-fn read_key_file(path: &Path, is_secret: bool) -> Result<Vec<u8>> {
-
- let expected = if is_secret { 64 } else { 32 };
- let mut key = vec![];
- let mut key_file = OpenOptions::new()
- .read(true)
- .write(false)
- .open(path)?;
- key_file.read_to_end(&mut key)?;
- if key.len() != expected {
- bail!("Bad key file (len {} != {}): {:?}", key.len(), expected, path);
- }
- Ok(key)
-}
-
-fn write_key_file(path: &Path, key: &[u8], is_secret: bool) -> Result<()> {
-
- let expected = if is_secret { 64 } else { 32 };
- if key.len() != expected {
- bail!("Bad key file (len {} != {}): {:?}", key.len(), expected, path);
- }
- let mut key_file = OpenOptions::new()
- .write(true)
- .create_new(true)
- .open(path)?;
- key_file.write_all(&key)?;
- Ok(())
-}
-
-impl SleepDirRegister {
-
- pub fn open(directory: &Path, prefix: &str, writable: bool) -> Result<SleepDirRegister> {
- // read public key from disk
- let pub_key: Vec<u8> = read_key_file(
- &directory.join(Path::new(&(prefix.to_owned() + ".key"))),
- false)?;
- let mut secret_key = None;
- if writable {
- secret_key = Some(read_key_file(
- &directory.join(Path::new(&(prefix.to_owned() + ".secret_key"))),
- true)?);
- }
- let data_path = &(prefix.to_owned() + ".data");
- let data_path = Path::new(data_path);
- let data_file = if data_path.is_file() {
- Some(OpenOptions::new()
- .read(true)
- .write(writable)
- .open(data_path)?)
- } else {
- None
- };
- let tree_sleep = SleepFile::open(
- &directory.join(Path::new(&(prefix.to_owned() + ".tree"))), writable)?;
- let sign_sleep = SleepFile::open(
- &directory.join(Path::new(&(prefix.to_owned() + ".signatures"))), writable)?;
- let bitfield_sleep = SleepFile::open(
- &directory.join(Path::new(&(prefix.to_owned() + ".bitfield"))), writable)?;
- let mut sf = SleepDirRegister {
- tree_sleep,
- sign_sleep,
- bitfield_sleep,
- data_file,
- pub_key,
- secret_key,
- };
- sf.check()?;
- Ok(sf)
- }
-
- /// In addition to what one would expect, also creates an Ed25519 key-pair using OsRng
- pub fn create(directory: &Path, prefix: &str) -> Result<SleepDirRegister> {
- let mut rand_seed = vec![0; 32];
- let mut rng = OsRng::new()?;
- rng.fill_bytes(&mut rand_seed);
- let (secret_key, pub_key) = ed25519::keypair(&rand_seed);
- write_key_file(
- &directory.join(Path::new(&(prefix.to_owned() + ".key"))),
- &pub_key,
- false)?;
- write_key_file(
- &directory.join(Path::new(&(prefix.to_owned() + ".secret_key"))),
- &secret_key,
- true)?;
- let data_file = OpenOptions::new()
- .read(true)
- .write(true)
- .create_new(true)
- .open(directory.join(Path::new(&(prefix.to_owned() + ".data"))))?;
- let tree_sleep = SleepFile::create(
- &directory.join(Path::new(&(prefix.to_owned() + ".tree"))),
- 0x05025702,
- 40,
- Some("BLAKE2b".to_string()))?;
- let sign_sleep = SleepFile::create(
- &directory.join(Path::new(&(prefix.to_owned() + ".signatures"))),
- 0x05025701,
- 64,
- Some("Ed25519".to_string()))?;
- let bitfield_sleep = SleepFile::create(
- &directory.join(Path::new(&(prefix.to_owned() + ".bitfield"))),
- 0x05025700,
- 3328,
- None)?;
- let mut sf = SleepDirRegister {
- tree_sleep,
- sign_sleep,
- bitfield_sleep,
- data_file: Some(data_file),
- pub_key: pub_key.to_vec(),
- secret_key: Some(secret_key.to_vec()),
- };
- sf.check()?;
- Ok(sf)
- }
-}
-
-impl HyperRegister for SleepDirRegister {
-
- /// TODO: this version only works for "dense" registers: it just checks if the index is in the
- /// total length, instead of using the bitfield.
- fn has(&self, index: u64) -> Result<bool> {
- return Ok(index < self.len()?);
- }
-
- fn has_all(&self) -> Result<bool> {
- self.has_range(0, self.len()?)
- }
-
- fn has_range(&self, start: u64, end: u64) -> Result<bool> {
- // This function is un-motivated and could be removed
- assert!(end > start);
- for i in start..end {
- if !self.has(i)? {
- return Ok(false);
- }
- }
- Ok(true)
- }
-
- fn get_data_entry(&mut self, index: u64) -> Result<Vec<u8>> {
-
- // Get metadata about chunk (offset and length)
- let offset = HyperRegister::get_data_offset(self, index)?;
-
- // Do we even have this chunk?
- if !self.has(index)? {
- bail!("Don't have that chunk");
- }
-
- let data_file = if let Some(ref mut df) = self.data_file {
- df
- } else {
- bail!("No data file in this register");
- };
- let leaf = self.tree_sleep.read(index*2)?;
- let data_len = u64::from_be(FixedInt::decode_fixed(&leaf[32..40]));
- // avoid foot-gun in development: cap at ~1 billion bytes
- assert!(data_len < 2u64.pow(29));
-
- // Read chunk
- let mut data = vec![0; data_len as usize];
- data_file.seek(SeekFrom::Start(offset))?;
- data_file.read_exact(&mut data)?;
-
- // TODO: check the hash? separate function?
- Ok(data)
- }
-
- fn get_tree_entry(&mut self, index: u64) -> Result<Vec<u8>> {
- self.tree_sleep.read(index)
- }
-
- fn append(&mut self, data: &[u8]) -> Result<u64> {
-
- if !self.data_file.is_some() {
- bail!("No data file in this register");
- };
-
- let index = self.len()?;
- // 1. Hash data chunk
- let leaf_hash = HyperRegister::hash_leaf(data);
-
- // 2. Append data to data file
- if let Some(ref mut df) = self.data_file {
- df.seek(SeekFrom::End(0))?;
- df.write_all(data)?;
- df.sync_data()?;
- }
-
- // 3. Add hash to tree file, update merkel tree
- self.tree_sleep.write(index*2, &leaf_hash)?;
- let mut parent = HyperRegister::tree_parent_index(index*2);
- while parent < index*2 {
- let (left, right) = HyperRegister::tree_child_indices(parent)?;
- let (left, right) = (self.tree_sleep.read(left)?,
- self.tree_sleep.read(right)?);
- let parent_hash = HyperRegister::hash_parent(&left[0..40], &right[0..40]);
- self.tree_sleep.write(parent, &parent_hash[0..40])?;
- parent = HyperRegister::tree_parent_index(parent);
- }
-
- // 4. Add signature to signature file
- let root_hash = HyperRegister::hash_roots(self, index+1)?;
- let root_sig = ed25519::signature(&root_hash, &self.secret_key.clone().unwrap());
- self.sign_sleep.append(&root_sig)?;
-
- // 5. Update bitfile
- Ok(index)
- }
-
- fn len(&self) -> Result<u64> {
- // Length in entry count.
- let tree_len = self.tree_sleep.len()?;
- if tree_len == 0 {
- Ok(0)
- } else if tree_len % 2 != 1 {
- bail!("Even number of tree file SLEEP entries");
- } else {
- Ok((self.tree_sleep.len()? / 2) + 1)
- }
- }
-
- fn len_bytes(&mut self) -> Result<u64> {
- // TODO: this is a naive (linear) implementation
- // log(N) would go up previous parent nodes (eg, use tree_root_nodes())
- let mut sum: u64 = 0;
- for i in 0..self.len()? {
- let leaf = self.get_tree_entry(i*2)?;
- sum += u64::from_be(FixedInt::decode_fixed(&leaf[32..40]));
- }
- Ok(sum)
- }
-
- fn verify(&mut self) -> Result<()> {
- unimplemented!()
- }
-
- fn check(&mut self) -> Result<()> {
- let sign_len = self.sign_sleep.len()?;
- let tree_len = self.tree_sleep.len()?;
- if (tree_len == 0) && (sign_len == 0) {
- return Ok(())
- }
- if tree_len != (sign_len * 2) - 1 {
- bail!("Inconsistent SLEEP signature/tree file sizes");
- }
- let computed = self.len_bytes()?;
- if let Some(ref df) = self.data_file {
- let file_size = df.metadata()?.len();
- if file_size != computed {
- bail!("Computed vs. data file size mismatch");
- }
- }
- Ok(())
- }
-
- /// Checks if we have the secret key (such that we can append to this register)
- fn writable(&self) -> bool {
- return self.secret_key.is_some()
- }
-}
-
-#[test]
-fn test_sdr_open() {
-
- let mut sdr = SleepDirRegister::open(
- Path::new("test-data/dat/simple/.dat/"), "metadata", false).unwrap();
-
- // Values from 'dat log'
- assert_eq!(sdr.len().unwrap(), 3);
- assert_eq!(sdr.len_bytes().unwrap(), 145);
-
- let mut sdr = SleepDirRegister::open(
- Path::new("test-data/dat/simple/.dat/"), "content", false).unwrap();
-
- // Values from 'dat log'
- assert_eq!(sdr.len().unwrap(), 2);
- assert_eq!(sdr.len_bytes().unwrap(), 204);
-}
-
-#[test]
-fn test_sdr_create() {
-
- use tempdir::TempDir;
- let tmp_dir = TempDir::new("geniza-test").unwrap();
- let mut sdr = SleepDirRegister::create(tmp_dir.path(), "dummy").unwrap();
-
- assert_eq!(sdr.len().unwrap(), 0);
- assert_eq!(sdr.len_bytes().unwrap(), 0);
-}
-
-#[test]
-fn test_sdr_append() {
-
- use tempdir::TempDir;
- let tmp_dir = TempDir::new("geniza-test").unwrap();
- let mut sdr = SleepDirRegister::create(tmp_dir.path(), "dummy").unwrap();
-
- sdr.append("hello world!".as_bytes()).unwrap();
- sdr.check().unwrap();
- assert_eq!(sdr.len().unwrap(), 1);
- assert_eq!(sdr.len_bytes().unwrap(), 12);
- let count = 100; // TODO: make this >1000 when things are faster
- for _ in 0..count {
- sdr.append(&[1,2,3,4,5]).unwrap();
- }
- sdr.check().unwrap();
- assert_eq!(sdr.len().unwrap(), 1+count);
- assert_eq!(sdr.len_bytes().unwrap(), 12 + (count*5));
-}
-
-#[test]
-fn test_sdr_has() {
-
- use tempdir::TempDir;
- let tmp_dir = TempDir::new("geniza-test").unwrap();
- let mut sdr = SleepDirRegister::create(tmp_dir.path(), "dummy").unwrap();
-
- sdr.append("hello world!".as_bytes()).unwrap();
- sdr.check().unwrap();
- assert_eq!(sdr.has_all().unwrap(), true);
- assert_eq!(sdr.has(0).unwrap(), true);
- assert_eq!(sdr.has(40).unwrap(), false);
-}