wip: Mast zip tree

This commit is contained in:
nazeh
2023-12-14 19:28:19 +03:00
parent b193cb516a
commit 9596efc534
9 changed files with 356 additions and 1 deletions

58
Cargo.lock generated
View File

@@ -130,6 +130,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -170,6 +176,15 @@ version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "mast"
version = "0.1.0"
dependencies = [
"blake3",
"bytes",
"varu64",
]
[[package]]
name = "password-hash"
version = "0.5.0"
@@ -235,12 +250,44 @@ dependencies = [
"getrandom",
]
[[package]]
name = "snafu"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab12d3c261b2308b0d80c26fffb58d17eba81a4be97890101f416b478c79ca7"
dependencies = [
"doc-comment",
"snafu-derive",
]
[[package]]
name = "snafu-derive"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1508efa03c362e23817f96cde18abed596a25219a8b2c66e8db33c03543d315b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.39"
@@ -269,7 +316,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
]
[[package]]
@@ -284,6 +331,15 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "varu64"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0b4bcb210ab3a048eda9a938508b072e474af2838994e77976c817e51af1e3"
dependencies = [
"snafu",
]
[[package]]
name = "version_check"
version = "0.9.4"

View File

@@ -1,6 +1,7 @@
[workspace]
members = [
"kytz",
"mast",
]
# See: https://github.com/rust-lang/rust/issues/90148#issuecomment-949194352

11
mast/Cargo.toml Normal file
View File

@@ -0,0 +1,11 @@
[package]
name = "mast"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
blake3 = "1.5.0"
bytes = "1.5.0"
varu64 = "0.7.0"

18
mast/src/lib.rs Normal file
View File

@@ -0,0 +1,18 @@
#![allow(unused)]
mod node;
mod storage;
mod tree;
use crate::node::Node;
// // TODO: maybe add nonce for encrypted trees.
// // TODO: why add header in each node? or in the Mast commit?
// // Would we need to read a node without traversing down from the Mast commit?
// pub struct Mast {
// /// The name of this Mast to be used as a prefix for all nodes
// /// in the storage, seperating different Masts.
// name: String,
// root: Option<Hash>,
// storage: MastStorage,
// }

3
mast/src/main.rs Normal file
View File

@@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

129
mast/src/node.rs Normal file
View File

@@ -0,0 +1,129 @@
//! Zip tree node.
use blake3::{Hash, Hasher};
use bytes::{BufMut, Bytes, BytesMut};
pub const HASH_LEN: usize = blake3::OUT_LEN;
pub const EMPTY_HASH: Hash = Hash::from_bytes([0_u8; HASH_LEN]);
#[derive(Debug)]
/// A serialized node.
pub struct Node {
key: String,
value: Hash,
left: Option<Hash>,
right: Option<Hash>,
}
impl Node {
pub fn new(key: String, value: Hash) -> Self {
Self {
key,
value,
left: None,
right: None,
}
}
// === Getters ===
// pub fn key(&self) -> &String {
// &self.key
// }
// pub fn left(&self) -> Option<Hash> {
// self.left
// }
// === Public Methods ===
//
pub fn serialize(&self) -> Bytes {
let mut bytes = BytesMut::new();
let mut header = 0_u8;
if self.left.is_some() {
header |= 0b0000_0010;
}
if self.right.is_some() {
header |= 0b0000_0001;
}
bytes.put_u8(0_u8);
// Encode the key
let mut key_len = [0_u8; 9];
let size = varu64::encode(self.key.len() as u64, &mut key_len);
bytes.extend_from_slice(&key_len[..size]);
bytes.extend_from_slice(self.key.as_bytes());
// Encode the value
bytes.extend_from_slice(self.value.as_bytes());
if let Some(left) = self.left {
bytes.extend_from_slice(left.as_bytes());
}
if let Some(right) = self.right {
bytes.extend_from_slice(right.as_bytes());
}
bytes.freeze()
}
// pub fn hash(&self) -> Hash {
// let mut hasher = Hasher::new();
// hasher.update(&self.serialize());
// hasher.finalize().into()
// }
pub fn deserialize(encoded: &Bytes) -> Result<Node, ()> {
let header = encoded.first().ok_or(())?;
let (n, rest) = varu64::decode(&encoded[1..]).map_err(|_| ())?;
let key_len = n as usize;
let key = String::from_utf8(rest[..key_len].to_vec()).map_err(|_| ())?;
let value = Hash::from_bytes(
rest[key_len..key_len + HASH_LEN]
.try_into()
.map_err(|_| ())?,
);
let mut left: Option<Hash> = None;
let mut right: Option<Hash> = None;
if *header & 0b0000_0010 != 0 {
let start = key_len + HASH_LEN;
let end = start + HASH_LEN;
left = Some(Hash::from_bytes(
rest[start..end].try_into().map_err(|_| ())?,
))
}
if *header & 0b0000_0001 != 0 {
let start = key_len + HASH_LEN + if left.is_some() { HASH_LEN } else { 0 };
let end = start + HASH_LEN;
right = Some(Hash::from_bytes(
rest[start..end].try_into().map_err(|_| ())?,
))
}
Ok(Self {
key,
value,
left,
right,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn encode() {
let node = Node::new("key".to_string(), EMPTY_HASH);
let encoded = node.serialize();
let decoded = Node::deserialize(&encoded);
}
}

View File

@@ -0,0 +1,36 @@
//! In memory Mast storage.
use blake3::{Hash, Hasher};
use bytes::{Bytes, BytesMut};
use std::collections::HashMap;
// TODO: storage abstraction.
#[derive(Debug)]
pub struct Storage {
storage: HashMap<Hash, Bytes>,
}
impl Storage {
pub fn new() -> Self {
Self {
storage: HashMap::default(),
}
}
pub fn get(&self, hash: &Hash) -> Option<Bytes> {
self.storage.get(hash).cloned()
}
pub fn insert_bytes(&mut self, bytes: Bytes) -> Hash {
let hash = Hasher::new().update(&bytes).finalize();
// TODO: should I add a prefix here?
self.storage.insert(hash, bytes);
hash
}
}
impl Default for Storage {
fn default() -> Self {
Self::new()
}
}

1
mast/src/storage/mod.rs Normal file
View File

@@ -0,0 +1 @@
pub mod memory;

100
mast/src/tree.rs Normal file
View File

@@ -0,0 +1,100 @@
//! Zip tree implementation.
use blake3::{Hash, Hasher};
use bytes::{Bytes, BytesMut};
use std::collections::btree_map::BTreeMap;
use std::fmt::{self, Debug, Display, Formatter};
use crate::node::Node;
use crate::storage;
#[derive(Debug)]
pub struct ZipTree {
root: Option<Node>,
storage: storage::memory::Storage,
}
impl ZipTree {
pub fn new() -> Self {
Self {
root: None,
storage: storage::memory::Storage::default(),
}
}
pub fn insert(&mut self, key: &[u8], value: &[u8]) -> &mut Self {
// let node = Node::new(key, value);
//
// let hash = node.hash();
//
// self.storage.insert(hash, node.serialize().into());
// self.root = Some(hash);
self
}
// pub fn node(&self, hash: &Hash) -> Option<Node> {
// if let Some(encoded_node) = self.storage.get(hash) {
// return Node::deserialize(encoded_node).ok().or(None);
// };
//
// None
// }
}
// impl Display for ZipTree {
// fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
// writeln!(f, "graph TD;");
//
// let mut stack: Vec<Node> = Vec::new();
//
// if let Some(root_id) = self.root {
// if let Some(node) = self.node(&root_id) {
// stack.push(node);
// }
// }
//
// while let Some(node) = stack.pop() {
// let left = self.node(&node.left());
// let right = self.node(&node.right());
//
// match (&left, &right) {
// (None, None) => {
// writeln!(f, " {:?}", node.key());
// }
// _ => {
// if let Some(left) = left {
// writeln!(f, " {:?} --> {:?}", node.key(), left.key());
// }
// if let Some(right) = right {
// writeln!(f, " {:?} --> {:?}", node.key(), right.key());
// }
// }
// }
// }
//
// Ok(())
// }
// }
// impl Debug for ZipTree {
// fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
// write!(f, "{}", self);
//
// Ok(())
// }
// }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_new() {
let mut tree = ZipTree::new();
tree.insert(b"foo", b"bar");
dbg!(tree);
}
}