aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/register.rs110
1 files changed, 91 insertions, 19 deletions
diff --git a/src/register.rs b/src/register.rs
index 7aa4c23..e31559b 100644
--- a/src/register.rs
+++ b/src/register.rs
@@ -65,7 +65,7 @@ impl HyperRegister {
buf
}
- fn hash_parent(lhash: &[u8; 40], rhash: &[u8; 40]) -> [u8; 40] {
+ 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])) +
@@ -87,7 +87,7 @@ impl HyperRegister {
let mut hash = Blake2b::new(32);
let mut index_buf = [0; 8];
hash.input(&[2; 1]);
- for ri in HyperRegister::root_nodes(index) {
+ 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]);
@@ -99,7 +99,7 @@ impl HyperRegister {
}
- fn root_nodes(data_count: u64) -> Vec<u64> {
+ 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,
@@ -139,19 +139,82 @@ impl HyperRegister {
}
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_root_nodes() {
- assert_eq!(HyperRegister::root_nodes(0), vec![]);
- assert_eq!(HyperRegister::root_nodes(1), vec![0]);
- assert_eq!(HyperRegister::root_nodes(2), vec![1]);
- assert_eq!(HyperRegister::root_nodes(3), vec![1,4]);
- assert_eq!(HyperRegister::root_nodes(4), vec![3]);
- assert_eq!(HyperRegister::root_nodes(5), vec![3,8]);
- assert_eq!(HyperRegister::root_nodes(6), vec![3,9]);
- assert_eq!(HyperRegister::root_nodes(7), vec![3,9,12]);
- assert_eq!(HyperRegister::root_nodes(8), vec![7]);
+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
@@ -328,8 +391,15 @@ impl HyperRegister for SleepDirRegister {
// 3. Add hash to tree file, update merkel tree
self.tree_sleep.write(index*2, &leaf_hash)?;
- // TODO: tree_parent_index(u64) -> u64 function
- // TODO: tree_child_entries(u64) function
+ 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(index);
+ }
// 4. Add signature to signature file
let root_hash = HyperRegister::hash_roots(self, index+1)?;
@@ -354,7 +424,7 @@ impl HyperRegister for SleepDirRegister {
fn len_bytes(&mut self) -> Result<u64> {
// TODO: this is a naive (linear) implementation
- // log(N) would go up previous parent nodes (eg, use root_nodes())
+ // 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)?;
@@ -435,10 +505,12 @@ fn test_sdr_append() {
sdr.check().unwrap();
assert_eq!(sdr.len().unwrap(), 1);
assert_eq!(sdr.len_bytes().unwrap(), 12);
- for i in 0..256 {
+ // XXX: some bug here around >= 5 (?)
+ let count = 4; // make this ~1000 when things are faster
+ for i in 0..count {
sdr.append(&[1,2,3,4,5]).unwrap();
}
sdr.check().unwrap();
- assert_eq!(sdr.len().unwrap(), 1+256);
- assert_eq!(sdr.len_bytes().unwrap(), 12 + (256*5));
+ assert_eq!(sdr.len().unwrap(), 1+count);
+ assert_eq!(sdr.len_bytes().unwrap(), 12 + (count*5));
}