mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-02-07 06:44:11 +00:00
initial commit
This commit is contained in:
parent
081937162c
commit
ae364f3ae7
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
||||||
|
.idea
|
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "zcash-mmr"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["NikVolf <nikvolf@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
owning_ref = "0.3"
|
66
src/lib.rs
Normal file
66
src/lib.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//! MMR library for Zcash
|
||||||
|
//!
|
||||||
|
//! To be used in zebra and via FFI bindings in zcashd
|
||||||
|
|
||||||
|
extern crate owning_ref;
|
||||||
|
|
||||||
|
mod tree;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NodeData {
|
||||||
|
subtree_commitment: [u8; 32],
|
||||||
|
start_time: u32,
|
||||||
|
end_time: u32,
|
||||||
|
start_target: u32,
|
||||||
|
end_target: u32,
|
||||||
|
start_sapling_root: [u8; 32],
|
||||||
|
end_sapling_root: [u8; 32],
|
||||||
|
subtree_total_work: u64,
|
||||||
|
start_height: u32,
|
||||||
|
end_height: u32,
|
||||||
|
shielded_tx: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum NodeLink {
|
||||||
|
Stored(u32),
|
||||||
|
Generated(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MMRNode {
|
||||||
|
left: Option<NodeLink>,
|
||||||
|
right: Option<NodeLink>,
|
||||||
|
data: NodeData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MMRNode {
|
||||||
|
fn complete(&self) -> bool {
|
||||||
|
let leaves = self.data.end_height - self.data.start_height + 1;
|
||||||
|
leaves & (leaves - 1) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NodeData> for MMRNode {
|
||||||
|
fn from(s: NodeData) -> Self {
|
||||||
|
MMRNode { left: None, right: None, data: s }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern fn append(
|
||||||
|
stored: *const MMRNode,
|
||||||
|
stored_count: u32,
|
||||||
|
generated: *const MMRNode,
|
||||||
|
generated_count: u32,
|
||||||
|
append_count: *mut u32,
|
||||||
|
append_buffer: *mut MMRNode,
|
||||||
|
) -> MMRNode {
|
||||||
|
|
||||||
|
// TODO: construct tree and write to (append_count, append_buffer)
|
||||||
|
// TODO: also return generated??
|
||||||
|
unimplemented!()
|
||||||
|
}
|
290
src/tree.rs
Normal file
290
src/tree.rs
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{MMRNode, NodeLink, NodeData};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Tree {
|
||||||
|
stored: HashMap<u32, MMRNode>,
|
||||||
|
|
||||||
|
generated: HashMap<u32, MMRNode>,
|
||||||
|
|
||||||
|
// number of persistent(!) tree entries
|
||||||
|
stored_count: u32,
|
||||||
|
|
||||||
|
// number of virtual nodes generated
|
||||||
|
generated_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tree {
|
||||||
|
fn resolve_link(&self, link: NodeLink) -> IndexedNode {
|
||||||
|
match link {
|
||||||
|
NodeLink::Generated(index) => {
|
||||||
|
// TODO: maybe graceful error?
|
||||||
|
let node = self.generated.get(&index).expect("caller should ensure id generated");
|
||||||
|
IndexedNode {
|
||||||
|
node,
|
||||||
|
link,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
NodeLink::Stored(index) => {
|
||||||
|
// TODO: maybe graceful error?
|
||||||
|
let node = self.stored.get(&index).expect("caller should ensure id stored");
|
||||||
|
IndexedNode {
|
||||||
|
node,
|
||||||
|
link,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, data: MMRNode) -> NodeLink {
|
||||||
|
let idx = self.stored_count;
|
||||||
|
self.stored_count = self.stored_count + 1;
|
||||||
|
self.stored.insert(idx, data);
|
||||||
|
NodeLink::Stored(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_generated(&mut self, data: MMRNode) -> NodeLink {
|
||||||
|
let idx = self.generated_count;
|
||||||
|
self.generated_count = self.generated_count + 1;
|
||||||
|
self.generated.insert(idx, data);
|
||||||
|
NodeLink::Generated(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn populate(loaded: Vec<MMRNode>) -> Self {
|
||||||
|
let mut result = Tree::default();
|
||||||
|
result.stored_count = loaded.len() as u32;
|
||||||
|
for (idx, item) in loaded.into_iter().enumerate() {
|
||||||
|
result.stored.insert(idx as u32, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// plain list of nodes that has to be appended to the end of the tree as the result of append operation
|
||||||
|
/// along with new root
|
||||||
|
pub struct AppendTransaction {
|
||||||
|
pub appended: Vec<NodeLink>,
|
||||||
|
pub new_root: NodeLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IndexedNode<'a> {
|
||||||
|
node: &'a MMRNode,
|
||||||
|
link: NodeLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combine_data(left: &NodeData, right: &NodeData) -> NodeData {
|
||||||
|
NodeData {
|
||||||
|
// TODO: hash children
|
||||||
|
subtree_commitment: [0u8; 32],
|
||||||
|
start_time: left.start_time,
|
||||||
|
end_time: right.end_time,
|
||||||
|
start_target: left.start_target,
|
||||||
|
end_target: right.end_target,
|
||||||
|
start_sapling_root: left.start_sapling_root,
|
||||||
|
end_sapling_root: right.end_sapling_root,
|
||||||
|
|
||||||
|
// TODO: sum work?
|
||||||
|
subtree_total_work: 0,
|
||||||
|
start_height: left.start_height,
|
||||||
|
end_height: right.end_height,
|
||||||
|
shielded_tx: left.shielded_tx + right.shielded_tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn combine_nodes<'a>(left: IndexedNode<'a>, right: IndexedNode<'a>) -> MMRNode {
|
||||||
|
MMRNode {
|
||||||
|
left: Some(left.link),
|
||||||
|
right: Some(right.link),
|
||||||
|
data: combine_data(&left.node.data, &right.node.data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append(tree: &mut Tree, root: NodeLink, new_leaf: NodeData) -> AppendTransaction {
|
||||||
|
|
||||||
|
let is_complete= tree.resolve_link(root).node.complete();
|
||||||
|
|
||||||
|
let (new_root_node, mut appended) = if is_complete {
|
||||||
|
let new_leaf_link = tree.push(new_leaf.into());
|
||||||
|
|
||||||
|
let mut appended = Vec::new();
|
||||||
|
appended.push(new_leaf_link);
|
||||||
|
|
||||||
|
// since we dethrone stored root, new one is always generated
|
||||||
|
let new_root_node = combine_nodes(
|
||||||
|
tree.resolve_link(root),
|
||||||
|
tree.resolve_link(new_leaf_link),
|
||||||
|
);
|
||||||
|
|
||||||
|
(new_root_node, appended)
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
let (root_left_child, root_right_child) = {
|
||||||
|
let root = tree.resolve_link(root).node;
|
||||||
|
(
|
||||||
|
root.left.expect("Root should always have left child"),
|
||||||
|
root.right.expect("Root should always have right child"),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let nested_append = append(tree, root_right_child, new_leaf);
|
||||||
|
let mut appended = nested_append.appended;
|
||||||
|
let subtree_root = nested_append.new_root;
|
||||||
|
|
||||||
|
let new_root_node = combine_nodes(
|
||||||
|
tree.resolve_link(root_left_child),
|
||||||
|
tree.resolve_link(subtree_root),
|
||||||
|
);
|
||||||
|
|
||||||
|
(new_root_node, appended)
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_root = if new_root_node.complete() {
|
||||||
|
let new_root= tree.push(new_root_node);
|
||||||
|
appended.push(new_root);
|
||||||
|
new_root
|
||||||
|
} else {
|
||||||
|
tree.push_generated(new_root_node)
|
||||||
|
};
|
||||||
|
|
||||||
|
AppendTransaction {
|
||||||
|
new_root,
|
||||||
|
appended,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::{MMRNode, NodeData, Tree, append, NodeLink};
|
||||||
|
|
||||||
|
fn leaf(height: u32) -> NodeData {
|
||||||
|
NodeData {
|
||||||
|
// TODO: hash children
|
||||||
|
subtree_commitment: [0u8; 32],
|
||||||
|
start_time: 0,
|
||||||
|
end_time: 0,
|
||||||
|
start_target: 0,
|
||||||
|
end_target: 0,
|
||||||
|
start_sapling_root: [0u8; 32],
|
||||||
|
end_sapling_root: [0u8; 32],
|
||||||
|
|
||||||
|
// TODO: sum work?
|
||||||
|
subtree_total_work: 0,
|
||||||
|
start_height: height,
|
||||||
|
end_height: height,
|
||||||
|
shielded_tx: 7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node(start_height: u32, end_height: u32) -> NodeData {
|
||||||
|
NodeData {
|
||||||
|
// TODO: hash children
|
||||||
|
subtree_commitment: [0u8; 32],
|
||||||
|
start_time: 0,
|
||||||
|
end_time: 0,
|
||||||
|
start_target: 0,
|
||||||
|
end_target: 0,
|
||||||
|
start_sapling_root: [0u8; 32],
|
||||||
|
end_sapling_root: [0u8; 32],
|
||||||
|
|
||||||
|
// TODO: sum work?
|
||||||
|
subtree_total_work: 0,
|
||||||
|
start_height: start_height,
|
||||||
|
end_height: end_height,
|
||||||
|
shielded_tx: 7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// size should be power of 2-1
|
||||||
|
fn initial() -> Tree {
|
||||||
|
let node1: MMRNode = leaf(1).into();
|
||||||
|
let node2: MMRNode = leaf(2).into();
|
||||||
|
|
||||||
|
let node3 = MMRNode {
|
||||||
|
data: node(1, 2),
|
||||||
|
left: Some(NodeLink::Stored(0)),
|
||||||
|
right: Some(NodeLink::Stored(1)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Tree::populate(vec![node1, node2, node3])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn discrete_append() {
|
||||||
|
let mut tree = initial();
|
||||||
|
let append_tx = append(
|
||||||
|
&mut tree, NodeLink::Stored(2),
|
||||||
|
leaf(3)
|
||||||
|
);
|
||||||
|
let new_root_link = append_tx.new_root;
|
||||||
|
let new_root = tree.resolve_link(new_root_link).node;
|
||||||
|
|
||||||
|
// initial tree: (2)
|
||||||
|
// / \
|
||||||
|
// (0) (1)
|
||||||
|
//
|
||||||
|
// new tree:
|
||||||
|
// (4g)
|
||||||
|
// / \
|
||||||
|
// (2) \
|
||||||
|
// / \ \
|
||||||
|
// (0) (1) (3)
|
||||||
|
//
|
||||||
|
// so only (3) is added as real leaf
|
||||||
|
// while new root, (4g) is generated one
|
||||||
|
assert_eq!(new_root.data.end_height, 3);
|
||||||
|
assert_eq!(append_tx.appended.len(), 1);
|
||||||
|
|
||||||
|
let append_tx = append(&mut tree, new_root_link, leaf(4));
|
||||||
|
let new_root_link = append_tx.new_root;
|
||||||
|
let new_root = tree.resolve_link(new_root_link).node;
|
||||||
|
|
||||||
|
// intermediate tree:
|
||||||
|
// (4g)
|
||||||
|
// / \
|
||||||
|
// (2) \
|
||||||
|
// / \ \
|
||||||
|
// (0) (1) (3)
|
||||||
|
//
|
||||||
|
// new tree:
|
||||||
|
// ( 6 )
|
||||||
|
// / \
|
||||||
|
// (2) (5)
|
||||||
|
// / \ / \
|
||||||
|
// (0) (1) (3) (4)
|
||||||
|
//
|
||||||
|
// so (4), (5), (6) are added as real leaves
|
||||||
|
// and new root, (6) is stored one
|
||||||
|
assert_eq!(new_root.data.end_height, 4);
|
||||||
|
assert_eq!(append_tx.appended.len(), 3);
|
||||||
|
|
||||||
|
let append_tx = append(&mut tree, new_root_link, leaf(5));
|
||||||
|
let new_root_link = append_tx.new_root;
|
||||||
|
let new_root = tree.resolve_link(new_root_link).node;
|
||||||
|
|
||||||
|
// intermediate tree:
|
||||||
|
// ( 6 )
|
||||||
|
// / \
|
||||||
|
// (2) (5)
|
||||||
|
// / \ / \
|
||||||
|
// (0) (1) (3) (4)
|
||||||
|
//
|
||||||
|
// new tree:
|
||||||
|
// ( 8g )
|
||||||
|
// / \
|
||||||
|
// ( 6 ) \
|
||||||
|
// / \ \
|
||||||
|
// (2) (5) \
|
||||||
|
// / \ / \ \
|
||||||
|
// (0) (1) (3) (4) (7)
|
||||||
|
//
|
||||||
|
// so (7) is added as real leaf
|
||||||
|
// and new root, (8g) is generated one
|
||||||
|
assert_eq!(new_root.data.end_height, 5);
|
||||||
|
assert_eq!(append_tx.appended.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user