2018-12-04 20:37:12 +00:00
|
|
|
const CHUNKSIZE: usize = 128;
|
|
|
|
const HASHSIZE: usize = 32;
|
2018-11-28 20:13:25 +00:00
|
|
|
|
2018-12-04 20:37:12 +00:00
|
|
|
pub trait TreeHash {
|
|
|
|
fn tree_hash(&self) -> Vec<u8>;
|
|
|
|
}
|
|
|
|
|
2018-12-08 23:25:59 +00:00
|
|
|
/// Returns a 32 byte hash of 'list' - a vector of byte vectors.
|
|
|
|
/// Note that this will consume 'list'.
|
2018-12-04 20:37:12 +00:00
|
|
|
pub fn merkle_hash(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
|
|
|
|
// flatten list
|
|
|
|
let data = &mut list_to_blob(list);
|
|
|
|
|
|
|
|
// get data_len as bytes. It will hashed will the merkle root
|
|
|
|
let dlen = data.len() as u64;
|
|
|
|
let data_len_bytes = &mut dlen.tree_hash();
|
|
|
|
data_len_bytes.resize(32, 0);
|
|
|
|
|
|
|
|
// merklize
|
|
|
|
let mut mhash = hash_level(data, CHUNKSIZE);
|
|
|
|
while mhash.len() > HASHSIZE {
|
|
|
|
mhash = hash_level(&mut mhash, HASHSIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
mhash.append(data_len_bytes);
|
|
|
|
mhash.tree_hash()
|
|
|
|
}
|
|
|
|
|
2018-12-08 23:25:59 +00:00
|
|
|
/// Takes a flat vector of bytes. It then hashes 'chunk_size * 2' slices into
|
|
|
|
/// a byte vector of hashes, divisible by HASHSIZE
|
2018-12-04 20:37:12 +00:00
|
|
|
fn hash_level(data: &mut Vec<u8>, chunk_size: usize) -> Vec<u8> {
|
|
|
|
assert!(data.len() % chunk_size == 0);
|
|
|
|
|
|
|
|
let mut result: Vec<u8> = Vec::new();
|
|
|
|
for two_chunks in data.chunks(chunk_size * 2) {
|
|
|
|
if two_chunks.len() == chunk_size && data.len() > chunk_size {
|
|
|
|
// if there is only one chunk here, hash it with a zero-byte
|
|
|
|
// CHUNKSIZE vector
|
|
|
|
let mut c = two_chunks.to_vec();
|
|
|
|
c.append(&mut vec![0; CHUNKSIZE]);
|
|
|
|
result.append(&mut c.tree_hash());
|
|
|
|
} else {
|
|
|
|
result.append(&mut two_chunks.tree_hash());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
|
|
|
fn list_to_blob(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
|
|
|
|
let mut data_len = 0;
|
|
|
|
if list[0].len().is_power_of_two() == false {
|
|
|
|
for x in list.iter_mut() {
|
|
|
|
extend_to_power_of_2(x);
|
|
|
|
data_len += x.len();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// do we need padding?
|
|
|
|
let extend_by = if data_len % CHUNKSIZE > 0 {
|
|
|
|
CHUNKSIZE - (data_len % CHUNKSIZE)
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
// allocate buffer and append each list element (flatten the vec of vecs)
|
|
|
|
data_len += extend_by;
|
|
|
|
let mut data: Vec<u8> = Vec::with_capacity(data_len);
|
|
|
|
for x in list.iter_mut() {
|
|
|
|
data.append(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add padding
|
|
|
|
let mut i = 0;
|
|
|
|
while i < extend_by {
|
|
|
|
data.push(0);
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
data
|
|
|
|
}
|
2018-11-28 20:13:25 +00:00
|
|
|
|
2018-12-08 23:25:59 +00:00
|
|
|
/// Extends data length to a power of 2 by minimally right-zero-padding
|
2018-11-28 20:13:25 +00:00
|
|
|
fn extend_to_power_of_2(data: &mut Vec<u8>) {
|
|
|
|
let len = data.len();
|
|
|
|
let new_len = len.next_power_of_two();
|
|
|
|
if new_len > len {
|
2018-12-04 20:37:12 +00:00
|
|
|
data.resize(new_len, 0);
|
2018-11-28 20:13:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_extend_to_power_of_2() {
|
|
|
|
let mut data = vec![1, 2, 3, 4, 5];
|
|
|
|
extend_to_power_of_2(&mut data);
|
|
|
|
assert_eq!(data, [1, 2, 3, 4, 5, 0, 0, 0]);
|
|
|
|
}
|
2018-12-04 20:37:12 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_merkle_hash() {
|
|
|
|
let data1 = vec![1; 100];
|
|
|
|
let data2 = vec![2; 100];
|
|
|
|
let data3 = vec![3; 100];
|
|
|
|
let mut list = vec![data1, data2, data3];
|
|
|
|
let result = merkle_hash(&mut list);
|
|
|
|
|
|
|
|
//note: should test againt a known test hash value
|
|
|
|
assert_eq!(HASHSIZE, result.len());
|
|
|
|
println!("merkle_hash: {:?}", result);
|
|
|
|
}
|
|
|
|
|
2018-11-28 20:13:25 +00:00
|
|
|
}
|