diff --git a/.github/workflows/rust-clippy.yml b/.github/workflows/clippy.yml similarity index 87% rename from .github/workflows/rust-clippy.yml rename to .github/workflows/clippy.yml index 23b67a0..360a92c 100644 --- a/.github/workflows/rust-clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,11 +1,15 @@ name: Clippy on: + pull_request: + branches-ignore: [ "dev*" ] + tags: [ "v*.*.*" ] push: - branches: [ "main", "master", "prod" ] - tags: ["v*.*.*"] + branches-ignore: [ "dev*" ] + tags: [ "v*.*.*" ] + release: schedule: - - cron: '30 9 * * 5' + - cron: "30 9 * * *" workflow_dispatch: jobs: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d0a5570..81a24db 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -5,10 +5,10 @@ env: on: pull_request: - branches: [ "main", "master", "prod" ] + branches-ignore: [ "dev*" ] tags: [ "v*.*.*" ] push: - branches: [ "main", "master", "prod" ] + branches-ignore: [ "dev*" ] tags: [ "v*.*.*" ] schedule: - cron: "30 9 * * *" diff --git a/Cargo.toml b/Cargo.toml index 5f020ec..51771f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,12 @@ keywords = ["algorithms", "data-structures"] license = "Apache-2.0" readme = "README.md" repository = "https://github.com/scattered-systems/algae" -version = "0.1.18" +version = "0.1.19" [workspace.dependencies] -decanter = { features = ["derive", "wasm"], version = "0.1.3" } -scsys = { features = [], version = "0.1.41" } +decanter = { features = ["derive", "wasm"], git = "https://github.com/FL03/decanter", branch = "v0.1.5", version = "0.1.5" } +anyhow = "1" itertools = "0.10" serde = { features = ["derive"], version = "1" } serde_json = "1" diff --git a/algae/Cargo.toml b/algae/Cargo.toml index ab1083b..be2b898 100644 --- a/algae/Cargo.toml +++ b/algae/Cargo.toml @@ -34,22 +34,11 @@ test = true [build-dependencies] [dependencies] -algae-graph = { features = [], optional = true, path = "../graph", version = "0.1.18" } -algae-merkle = { features = [], optional = true, path = "../merkle", version = "0.1.18" } -algae-mmr = { features = [], optional = true, path = "../mmr", version = "0.1.18" } - -decanter.workspace = true -itertools.workspace = true -scsys.workspace = true -serde.workspace = true -serde_json.workspace = true -smart-default.workspace = true -strum.workspace = true +algae-graph = { features = [], optional = true, path = "../graph", version = "0.1.19" } +algae-merkle = { features = [], optional = true, path = "../merkle", version = "0.1.19" } +algae-mmr = { features = [], optional = true, path = "../mmr", version = "0.1.19" } [dev-dependencies] -decanter.workspace = true -hex-literal = "0.3" -vrf = "0.2" [package.metadata.docs.rs] all-features = true diff --git a/algae/tests/merkle.rs b/algae/tests/merkle.rs deleted file mode 100644 index 128d15e..0000000 --- a/algae/tests/merkle.rs +++ /dev/null @@ -1,94 +0,0 @@ -/* - TODO: Update the hashes to match the Blake3 Hash Digests -*/ -#[cfg(feature = "merkle")] -#[cfg(test)] -/* - Map(A -> a) - def. - This notation abbreviates a is the hash of A; more formally, (A) maps to the hash (a) by the hashing function H -*/ -use algae::merkle::{is_merkle_valid, MerkleTree, MerkleTreeWrapper}; -use decanter::prelude::{Hashable, H256}; -use hex_literal::hex; - -macro_rules! gen_merkle_tree_data { - () => {{ - vec![ - (hex!("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - ] - }}; -} - -macro_rules! gen_merkle_tree_data2 { - () => {{ - vec![ - (hex!("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - (hex!("0101010101010101010101010101010101010101010101010101010101010202")).into(), - ] - }}; -} - -/* - A -> a: ("0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d0a0b0c0d0e0f0e0d", "b69566be6e1720872f73651d1851a0eae0060a132cf0f64a0ffaea248de6cba0") - B -> b: ("0101010101010101010101010101010101010101010101010101010101010202", "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f") - C -> c: (concat(a, b), 6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920") where a ahead of b -*/ -#[test] -fn test_merkle_root() { - let data: Vec = gen_merkle_tree_data!(); - let expected = - (hex!("6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920")).into(); - let a = MerkleTree::from(data); - assert_ne!(&a.root(), &expected); -} - -/* - A -> a: ("0101010101010101010101010101010101010101010101010101010101010202", "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f") -*/ -#[test] -fn test_merkle_proof() { - let expected = - vec![hex!("965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f").into()]; - - let a = MerkleTree::from(gen_merkle_tree_data!()); - - assert_ne!(a.proof(0), expected) -} - -/* - A -> a: ("0101010101010101010101010101010101010101010101010101010101010202", "965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f") -*/ -#[test] -fn test_is_merkle_valid() { - let data: Vec = gen_merkle_tree_data2!(); - let merkle_tree = MerkleTree::from(data.clone()); - let index = 3; - let proof = merkle_tree.proof(index); - - assert!(is_merkle_valid( - &merkle_tree.root(), - &data[index].hash(), - &proof, - index, - data.len() - )); -} - -#[test] -fn test_vrf_tree() { - let data: Vec = gen_merkle_tree_data!(); - let merkle_tree = MerkleTree::from(data.clone()); - let proof = merkle_tree.proof(0); - assert!(is_merkle_valid( - &merkle_tree.root(), - &data[0].hash(), - &proof, - 0, - data.len() - )); -} diff --git a/graph/Cargo.toml b/graph/Cargo.toml index fcb4687..2e5efce 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -14,19 +14,27 @@ version.workspace = true [features] default = [] +wasm = [] [lib] crate-type = ["cdylib", "rlib"] [build-dependencies] -[dev-dependencies] - [dependencies] decanter.workspace = true itertools.workspace = true -scsys.workspace = true serde.workspace = true serde_json.workspace = true smart-default.workspace = true strum.workspace = true + +[dev-dependencies] + +[package.metadata.docs.rs] +all-features = true +rustc-args = ["--cfg", "docsrs"] + +[target.wasm32-unknown-unknown] + +[target.wasm32-wasi] diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index 19f34b5..0c152e9 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -3,34 +3,60 @@ Contrib: FL03 Description: an edge consists of two nodes and an optional edge value */ -use super::{Node, Pair}; +use super::Pair; +use crate::{Node, Weight}; use serde::{Deserialize, Serialize}; pub trait Related {} #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Edge(N, N, V); +pub struct Edge +where + N: Node, + V: Weight, +{ + pair: Pair, + weight: V, +} -impl Edge { - pub fn new(a: N, b: N, v: V) -> Self { - Self(a, b, v) +impl Edge +where + N: Node, + V: Weight, +{ + pub fn new(pair: Pair, weight: V) -> Self { + Self { pair, weight } } pub fn pair(&self) -> Pair { - Pair::new(self.0.clone(), self.1.clone()) + self.pair.clone() } - pub fn value(&self) -> V { - self.2.clone() + pub fn value(&self) -> &V { + &self.weight } } -impl From<(N, N, V)> for Edge { +impl From<(N, N, V)> for Edge +where + N: Node, + V: Weight, +{ fn from(data: (N, N, V)) -> Self { - Self(data.0, data.1, data.2) + Self { + pair: Pair::new(data.0, data.1), + weight: data.2, + } } } -impl From<(Pair, V)> for Edge { +impl From<(Pair, V)> for Edge +where + N: Node, + V: Weight, +{ fn from(data: (Pair, V)) -> Self { - Self(data.0.0, data.0.1, data.1) + Self { + pair: data.0, + weight: data.1, + } } } diff --git a/graph/src/cmp/mod.rs b/graph/src/cmp/mod.rs index ad72b42..d00028c 100644 --- a/graph/src/cmp/mod.rs +++ b/graph/src/cmp/mod.rs @@ -3,17 +3,8 @@ Contrib: FL03 Description: components (cmp) for building effecient graph data-structures */ -pub use self::{atable::*, edge::*, pair::*}; +pub use self::{edge::*, neighbors::*, pair::*}; -pub(crate) mod atable; -pub(crate) mod edge; -pub(crate) mod pair; - -/// [Node] describes compatible vertices of the [super::Graph] -pub trait Node: Clone + Eq + std::hash::Hash {} - -impl Node for char {} - -impl Node for &str {} - -impl Node for String {} +mod edge; +mod neighbors; +mod pair; diff --git a/mmr/src/actors/mod.rs b/graph/src/cmp/neighbors.rs similarity index 60% rename from mmr/src/actors/mod.rs rename to graph/src/cmp/neighbors.rs index 4d5a844..d6269ca 100644 --- a/mmr/src/actors/mod.rs +++ b/graph/src/cmp/neighbors.rs @@ -1,7 +1,5 @@ /* - Appellation: actors + Appellation: neighbors Contrib: FL03 Description: ... summary ... */ - -pub mod builders; diff --git a/graph/src/cmp/pair.rs b/graph/src/cmp/pair.rs index 0aad2cd..a7eb6f7 100644 --- a/graph/src/cmp/pair.rs +++ b/graph/src/cmp/pair.rs @@ -6,10 +6,24 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Pair(pub T, pub T); +pub struct Pair(pub T, pub T) +where + T: Default; -impl Pair { +impl Pair +where + T: Default, +{ pub fn new(a: T, b: T) -> Self { Self(a, b) } } + +impl From<(T, T)> for Pair +where + T: Default, +{ + fn from(data: (T, T)) -> Self { + Self(data.0, data.1) + } +} diff --git a/graph/src/directed.rs b/graph/src/directed.rs index 3222d3c..40973cf 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -3,41 +3,125 @@ Contrib: FL03 Description: ... Summary ... */ -use super::{ - cmp::{AdjacencyTable, Node}, - Graph, Subgraph, -}; +use crate::{cmp::Edge, store::AdjacencyTable}; +use crate::{Contain, Graph, GraphExt, Node, Subgraph, Weight}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct DirectedGraph { - adjacency_table: AdjacencyTable, +pub struct DirectedGraph +where + N: Node, + V: Weight, +{ + store: AdjacencyTable, } -impl Graph for DirectedGraph { +impl AsMut> for DirectedGraph +where + N: Node, + V: Weight, +{ + fn as_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store + } +} + +impl AsRef> for DirectedGraph +where + N: Node, + V: Weight, +{ + fn as_ref(&self) -> &AdjacencyTable { + &self.store + } +} + +impl Contain for DirectedGraph +where + N: Node, + V: Weight, +{ + fn contains(&self, elem: &N) -> bool { + self.store.contains_key(elem) + } +} + +impl Contain> for DirectedGraph +where + N: Node, + V: Weight, +{ + fn contains(&self, elem: &Edge) -> bool { + self.edges().contains(elem) + } +} + +impl Graph for DirectedGraph +where + N: Node, + V: Weight, +{ + fn store_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store + } + fn store(&self) -> &AdjacencyTable { + &self.store + } +} + +impl GraphExt for DirectedGraph +where + N: Node, + V: Weight, +{ fn new() -> Self { Self { - adjacency_table: AdjacencyTable::new(), + store: AdjacencyTable::new(), } } - fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable { - &mut self.adjacency_table - } - fn adjacency_table(&self) -> &AdjacencyTable { - &self.adjacency_table - } fn with_capacity(capacity: usize) -> Self { Self { - adjacency_table: AdjacencyTable::with_capacity(capacity), + store: AdjacencyTable::with_capacity(capacity), } } } -impl Subgraph for DirectedGraph {} +impl Subgraph for DirectedGraph +where + N: Node, + V: Weight, +{ +} + +impl From> for DirectedGraph +where + N: Node, + V: Weight, +{ + fn from(store: AdjacencyTable) -> Self { + Self { store } + } +} + +impl std::ops::Index for DirectedGraph +where + N: Node, + V: Weight, +{ + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + self.store.get(&index).unwrap() + } +} -impl From> for DirectedGraph { - fn from(adjacency_table: AdjacencyTable) -> Self { - Self { adjacency_table } +impl std::ops::IndexMut for DirectedGraph +where + N: Node, + V: Weight, +{ + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.store.get_mut(&index).unwrap() } } @@ -65,7 +149,7 @@ mod tests { graph.add_edge(i.into()); } for edge in TEST_EDGES { - assert!(graph.contains_edge(&Edge::from(edge))); + assert!(graph.contains(&Edge::from(edge))); } } @@ -77,7 +161,7 @@ mod tests { graph.add_edge(i.into()); } - assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5)]); + assert_eq!(graph.neighbors("a").unwrap(), &vec![("b", 5)]); } #[test] @@ -86,9 +170,7 @@ mod tests { graph.add_node("a"); graph.add_node("b"); graph.add_node("c"); - assert!(graph.contains_node(&"a")); - assert!(graph.contains_node(&"b")); - assert!(graph.contains_node(&"c")); - assert!(!graph.contains_node(&"d")); + assert!(graph.contains_all(["a", "b", "c"])); + assert!(graph.contains_some(["a", "b", "c", "d"])); } } diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 5878405..951e2f3 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -3,81 +3,169 @@ Contrib: FL03 Description: This library is dedicated to graphs, explicitly implementing generic directed and undirected data-structures while providing the tools to create new ones. */ -pub use self::{directed::*, undirected::*}; +pub use self::{cmp::*, directed::*, errors::*, specs::*, undirected::*}; +pub(crate) mod cmp; pub(crate) mod directed; +mod errors; +pub(crate) mod specs; pub(crate) mod undirected; -pub mod cmp; -pub mod errors; +pub mod search; +pub mod store; -use cmp::{AdjacencyTable, Edge, Node}; +use cmp::Edge; use errors::GraphError; -use std::collections::HashSet; +use std::{collections::HashSet, ops::IndexMut}; +use store::AdjacencyTable; + +pub enum GraphType { + Directed, + Undirected, +} /// [Graph] describes the basic operations of a graph data-structure -pub trait Graph: Clone { - fn new() -> Self; +pub trait Graph: + Clone + Contain + Contain> + IndexMut> +where + N: Node, + V: Weight, +{ /// [Graph::add_edge] inserts a new [Edge] into the graph fn add_edge(&mut self, edge: Edge) { let pair = edge.pair(); self.add_node(pair.0.clone()); self.add_node(pair.1.clone()); - self.adjacency_table_mut() - .entry(pair.0.clone()) - .and_modify(|e| { - e.push((pair.1, edge.value())); - }); + self.store_mut().entry(pair.0.clone()).and_modify(|e| { + e.push((pair.1, edge.value().clone())); + }); + } + /// [Graph::add_edges] insert several edges into the graph + fn add_edges(&mut self, iter: impl IntoIterator>) { + for i in iter { + self.add_edge(i) + } } /// [Graph::add_node] if the given [Node] is not already in the [Graph], insert the [Node] and return true; else return false fn add_node(&mut self, node: N) -> bool { - match self.adjacency_table().get(&node) { + match self.store().get(&node) { None => { - self.adjacency_table_mut().insert(node, Vec::new()); + self.store_mut().insert(node, Vec::new()); true } _ => false, } } - /// [Graph::adjacency_table_mut] returns an owned, mutable instance of the [AdjacencyTable] - fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable; - /// [Graph::adjacency_table] returns an owned instance of the [AdjacencyTable] - fn adjacency_table(&self) -> &AdjacencyTable; - /// [Graph::contains_edge] checks to see if a given [Edge] is found in the [Graph] + /// [Graph::add_nodes] insert several nodes into the graph + fn add_nodes(&mut self, iter: impl IntoIterator) { + for i in iter { + self.add_node(i); + } + } + /// [Graph::contains_edge] returns true if the given [Edge] is in the graph fn contains_edge(&self, edge: &Edge) -> bool { - self.edges().contains(edge) + match self.store().get(&edge.pair().0) { + Some(edges) => edges.contains(&(edge.pair().1, edge.value().clone())), + None => false, + } } - /// [Graph::contains_node] checks to see if a given [Node] is found in the [Graph] + /// [Graph::contains_node] returns true if the given [Node] is in the graph fn contains_node(&self, node: &N) -> bool { - self.adjacency_table().get(node).is_some() + self.store().contains_key(node) + } + /// [Graph::degree] returns the degree of the given [Node] + fn degree(&self, node: &N) -> Result { + match self.store().get(node) { + Some(edges) => Ok(edges.len()), + None => Err(GraphError::NodeNotInGraph), + } } /// [Graph::edges] returns all of the edges persisting within the graph fn edges(&self) -> Vec> { let mut edges = Vec::new(); - for (from_node, from_node_neighbours) in self.adjacency_table().clone() { + for (from_node, from_node_neighbours) in self.store().clone() { for (to_node, weight) in from_node_neighbours { edges.push((from_node.clone(), to_node, weight).into()); } } edges } - /// [Graph::neighbours] attempts to return a [Vec] that contains all of the connected [Node] and their values - fn neighbours(&self, node: N) -> Result<&Vec<(N, V)>, GraphError> { - match self.adjacency_table().get(&node) { + /// [Graph::edges_from] returns all of the edges originating from the given [Node] + fn edges_from(&self, node: &N) -> Result>, GraphError> { + match self.store().get(node) { + Some(edges) => Ok(edges + .iter() + .map(|(n, v)| Edge::from((node.clone(), n.clone(), v.clone()))) + .collect()), None => Err(GraphError::NodeNotInGraph), - Some(i) => Ok(i), + } + } + /// [Graph::edges_to] returns all of the edges terminating at the given [Node] + fn edges_to(&self, node: &N) -> Result>, GraphError> { + let mut edges = Vec::new(); + for (from_node, from_node_neighbours) in self.store().clone() { + for (to_node, weight) in from_node_neighbours { + if to_node == node.clone() { + edges.push((from_node.clone(), to_node, weight).into()); + } + } + } + Ok(edges) + } + /// [Graph::is_connected] returns true if the graph is connected + fn is_connected(&self) -> bool { + let mut visited = HashSet::new(); + let mut stack = vec![self.nodes().iter().next().unwrap().clone()]; + + while let Some(node) = stack.pop() { + if !visited.contains(&node) { + visited.insert(node.clone()); + stack.extend( + self.neighbors(node) + .unwrap() + .iter() + .cloned() + .map(|(n, _)| n), + ); + } + } + + visited.len() == self.nodes().len() + } + /// [Graph::store_mut] returns an owned, mutable instance of the [AdjacencyTable] + fn store_mut(&mut self) -> &mut AdjacencyTable; + /// [Graph::store] returns an owned instance of the [AdjacencyTable] + fn store(&self) -> &AdjacencyTable; + /// [Graph::neighbors] attempts to return a [Vec] that contains all of the connected [Node] and their values + fn neighbors(&self, node: N) -> Result<&Vec<(N, V)>, GraphError> { + if self.nodes().contains(&node) { + Ok(&self[node]) + } else { + Err(GraphError::NodeNotInGraph) } } /// [Graph::nodes] returns a cloned [HashSet] of the graph's current [Node]s fn nodes(&self) -> HashSet { - self.adjacency_table().keys().cloned().collect() + self.store().keys().cloned().collect() } - /// [Graph::with_capacity] is a method for creating a graph with a set number of nodes +} + +pub trait GraphExt: Graph +where + N: Node, + V: Weight, +{ + fn new() -> Self; + /// [GraphExt::with_capacity] is a method for creating a graph with a set number of nodes fn with_capacity(capacity: usize) -> Self; } -pub trait Subgraph: Graph { +pub trait Subgraph: Graph +where + N: Node, + V: Weight, +{ fn is_subgraph(&self, graph: impl Graph) -> bool { self.nodes().is_subset(&graph.nodes()) } diff --git a/graph/src/search/bfs.rs b/graph/src/search/bfs.rs new file mode 100644 index 0000000..de9e81f --- /dev/null +++ b/graph/src/search/bfs.rs @@ -0,0 +1,82 @@ +/* + Appellation: bfs + Contrib: FL03 + Description: ... Summary ... +*/ +use super::Searcher; +use crate::{Contain, Graph, Node, Weight}; +use std::collections::{HashSet, VecDeque}; + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct BreadthFirstSearch { + queue: VecDeque, + visited: HashSet, +} + +impl BreadthFirstSearch { + pub fn new() -> Self { + Self { + queue: VecDeque::new(), + visited: HashSet::new(), + } + } +} + +impl Contain for BreadthFirstSearch { + fn contains(&self, elem: &N) -> bool { + self.visited.contains(elem) + } +} + +impl Searcher for BreadthFirstSearch +where + N: Node, + V: Weight, +{ + fn reset(&mut self) { + self.visited.clear(); + self.queue.clear(); + } + fn search(&mut self, graph: impl Graph, start: N) -> Vec { + self.queue.push_back(start); + + while let Some(node) = self.queue.pop_front() { + if !self.visited.contains(&node) { + self.visited.insert(node.clone()); + + if let Some(edges) = graph.store().get(&node) { + for (n, _) in edges { + self.queue.push_back(n.clone()); + } + } + } + } + + self.visited.iter().cloned().collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{DirectedGraph, Edge, GraphExt}; + + const TEST_EDGES: [(&str, &str, usize); 5] = [ + ("a", "b", 5), + ("c", "a", 7), + ("b", "c", 10), + ("d", "c", 10), + ("e", "f", 10), + ]; + + #[test] + fn test_bfs_directed() { + let mut graph = DirectedGraph::<&str, usize>::new(); + for i in TEST_EDGES { + graph.add_edge(Edge::from(i)); + } + let mut bfs = BreadthFirstSearch::new(); + bfs.search(graph, "a"); + assert!(bfs.contains_all(["b", "c", "a"])); + } +} diff --git a/graph/src/search/dfs.rs b/graph/src/search/dfs.rs new file mode 100644 index 0000000..df6440f --- /dev/null +++ b/graph/src/search/dfs.rs @@ -0,0 +1,82 @@ +/* + Appellation: dfs + Contrib: FL03 + Description: ... Summary ... +*/ +use super::Searcher; +use crate::{Contain, Graph, Node, Weight}; + +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct DepthFirstSearch { + stack: Vec, + visited: Vec, +} + +impl DepthFirstSearch { + pub fn new() -> Self { + Self { + stack: Vec::new(), + visited: Vec::new(), + } + } +} + +impl Contain for DepthFirstSearch { + fn contains(&self, elem: &N) -> bool { + self.visited.contains(elem) + } +} + +impl Searcher for DepthFirstSearch +where + N: Node, + V: Weight, +{ + fn reset(&mut self) { + self.stack.clear(); + self.visited.clear(); + } + + fn search(&mut self, graph: impl Graph, start: N) -> Vec { + self.stack.push(start); + while let Some(node) = self.stack.pop() { + if !self.visited.contains(&node) { + self.visited.push(node.clone()); + if let Ok(neighbor) = graph.neighbors(node) { + for (node, _) in neighbor { + self.stack.push(node.clone()); + } + } + } + } + self.visited.clone() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{DirectedGraph, Edge, GraphExt}; + + const TEST_EDGES: [(&str, &str, usize); 5] = [ + ("a", "b", 5), + ("c", "a", 7), + ("b", "c", 10), + ("d", "c", 10), + ("e", "f", 10), + ]; + + #[test] + fn test_dfs_directed() { + let mut graph = DirectedGraph::<&str, usize>::new(); + for i in TEST_EDGES { + graph.add_edge(Edge::from(i)); + } + // + let mut dfs = DepthFirstSearch::new(); + // + dfs.search(graph, "a"); + // + assert!(dfs.contains_all(["b", "c", "a"])); + } +} diff --git a/graph/src/search/mod.rs b/graph/src/search/mod.rs new file mode 100644 index 0000000..b46b5c2 --- /dev/null +++ b/graph/src/search/mod.rs @@ -0,0 +1,23 @@ +/* + Appellation: search + Contrib: FL03 + Description: ... Summary ... +*/ +pub use self::{bfs::BreadthFirstSearch, dfs::DepthFirstSearch}; + +mod bfs; +mod dfs; + +use crate::{Contain, Graph, Node, Weight}; + +/// [Searcher] is a trait that defines the behavior of a graph search algorithm. +pub trait Searcher: Contain +where + N: Node, + V: Weight, +{ + /// Search the graph for nodes. + fn search(&mut self, graph: impl Graph, start: N) -> Vec; + /// Reset the search state. + fn reset(&mut self); +} diff --git a/graph/src/specs.rs b/graph/src/specs.rs new file mode 100644 index 0000000..6fcb5b4 --- /dev/null +++ b/graph/src/specs.rs @@ -0,0 +1,83 @@ +/* + Appellation: specs + Contrib: FL03 + Description: ... Summary ... +*/ + +pub trait Contain { + /// [Contain::contains] returns true if the given element is in the [Contain] instance + fn contains(&self, elem: &T) -> bool; + /// [Contain::contains_all] returns true if all elements in the given iterator are in the [Contain] instance + fn contains_all(&self, iter: impl IntoIterator) -> bool { + iter.into_iter().all(|i| self.contains(&i)) + } + /// [Contain::contains_some] returns true if any element in the given iterator is in the [Contain] instance + fn contains_some(&self, iter: impl IntoIterator) -> bool { + iter.into_iter().any(|i| self.contains(&i)) + } +} + +/// [Node] describes compatible vertices of the [crate::Graph] +pub trait Node: Clone + Default + Eq + std::hash::Hash {} + +impl Node for char {} + +impl Node for &str {} + +impl Node for String {} + +impl Node for usize {} + +impl Node for u8 {} + +impl Node for u16 {} + +impl Node for u32 {} + +impl Node for u64 {} + +impl Node for u128 {} + +impl Node for isize {} + +impl Node for i8 {} + +impl Node for i16 {} + +impl Node for i32 {} + +impl Node for i64 {} + +impl Node for i128 {} + +pub trait Weight: Clone + PartialEq {} + +impl Weight for char {} + +impl Weight for &str {} + +impl Weight for String {} + +impl Weight for usize {} + +impl Weight for u8 {} + +impl Weight for u16 {} + +impl Weight for u32 {} + +impl Weight for u64 {} + +impl Weight for u128 {} + +impl Weight for isize {} + +impl Weight for i8 {} + +impl Weight for i16 {} + +impl Weight for i32 {} + +impl Weight for i64 {} + +impl Weight for i128 {} diff --git a/graph/src/store/matrix.rs b/graph/src/store/matrix.rs new file mode 100644 index 0000000..74825c5 --- /dev/null +++ b/graph/src/store/matrix.rs @@ -0,0 +1,99 @@ +/* + Appellation: atable + Contrib: FL03 + Description: an adjacency table +*/ +use crate::Node; +use serde::{Deserialize, Serialize}; +use std::iter::Extend; + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct AdjacencyMatrix +where + N: Node, + V: Clone + PartialEq, +{ + store: Vec<(N, Vec<(N, V)>)>, +} + +impl AdjacencyMatrix { + pub fn new() -> Self { + Self { store: Vec::new() } + } + /// Clears the matrix, removing all elements. + pub fn clear(&mut self) { + self.store.clear() + } + /// Returns the number of elements the matrix can hold without reallocating. + pub fn capacity(&self) -> usize { + self.store.capacity() + } + /// Returns a reference to the value corresponding to the key. + pub fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { + self.store + .iter() + .find_map(|(k, v)| if k == key { Some(v) } else { None }) + } + /// Returns a reference to the key and its value(s). + pub fn get_key_value(&self, key: &N) -> Option<(&N, &Vec<(N, V)>)> { + self.store + .iter() + .find_map(|(k, v)| if k == key { Some((k, v)) } else { None }) + } + /// Returns a mutable reference to the value corresponding to the key. + pub fn get_mut(&mut self, key: &N) -> Option<&mut Vec<(N, V)>> { + self.store + .iter_mut() + .find_map(|(k, v)| if k == key { Some(v) } else { None }) + } + /// Returns an iterator over the elements of the matrix. + pub fn keys(&self) -> impl Iterator { + self.store.iter().map(|(k, _)| k) + } + /// Returns the number of elements in the matrix. + pub fn len(&self) -> usize { + self.store.len() + } + /// Pushes a new element to the matrix. + pub fn push(&mut self, elem: N, value: Vec<(N, V)>) { + self.store.push((elem, value)); + } + /// Shrinks the capacity of the matrix as much as possible. + pub fn shrink_to_fit(&mut self) { + self.store.shrink_to_fit() + } + /// Reserves capacity for at least `additional` more elements to be inserted in the matrix. + pub fn with_capacity(capacity: usize) -> Self { + Self { + store: Vec::with_capacity(capacity), + } + } +} + +impl Extend<(N, Vec<(N, V)>)> for AdjacencyMatrix { + fn extend)>>(&mut self, iter: T) { + self.store.extend(iter); + } +} + +impl std::ops::Index for AdjacencyMatrix +where + N: Node, + V: Clone + PartialEq, +{ + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + self.get(&index).unwrap() + } +} + +impl std::ops::IndexMut for AdjacencyMatrix +where + N: Node, + V: Clone + PartialEq, +{ + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.get_mut(&index).unwrap() + } +} diff --git a/graph/src/store/mod.rs b/graph/src/store/mod.rs new file mode 100644 index 0000000..b4ea3ef --- /dev/null +++ b/graph/src/store/mod.rs @@ -0,0 +1,79 @@ +/* + Appellation: store + Contrib: FL03 + Description: +*/ +pub use self::{matrix::*, table::*}; + +pub(crate) mod matrix; +pub(crate) mod table; + +use crate::{cmp::Edge, Contain, Node, Weight}; +use serde::{Deserialize, Serialize}; +use std::ops::IndexMut; + +pub struct Entry +where + N: Node, + V: Weight, +{ + key: N, + value: Vec<(N, V)>, +} + +impl Entry +where + N: Node, + V: Weight, +{ + pub fn new(key: N, value: Vec<(N, V)>) -> Self { + Self { key, value } + } + pub fn key(&self) -> &N { + &self.key + } + pub fn value(&self) -> &Vec<(N, V)> { + &self.value + } + pub fn value_mut(&mut self) -> &mut Vec<(N, V)> { + &mut self.value + } +} + +impl Contain<(N, V)> for Entry +where + N: Node, + V: Weight, +{ + fn contains(&self, elem: &(N, V)) -> bool { + self.value.contains(elem) + } +} + +pub trait Store: Extend> + IndexMut> +where + N: Node, + V: Weight, +{ + fn clear(&mut self); + fn contains_key(&self, key: &N) -> bool; + fn drain(&mut self); + fn entry(&mut self, key: N); + fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { + if self.contains_key(key) { + Some(&self[key.clone()]) + } else { + None + } + } +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub enum Stores +where + N: Node, + V: Weight, +{ + Matrix(AdjacencyMatrix), + Table(AdjacencyTable), +} diff --git a/graph/src/cmp/atable.rs b/graph/src/store/table.rs similarity index 58% rename from graph/src/cmp/atable.rs rename to graph/src/store/table.rs index e356af0..f28b887 100644 --- a/graph/src/cmp/atable.rs +++ b/graph/src/store/table.rs @@ -3,100 +3,83 @@ Contrib: FL03 Description: an adjacency table */ -use super::Node; +use crate::Node; use serde::{Deserialize, Serialize}; use std::collections::{hash_map, HashMap}; use std::iter::Extend; -pub trait HashMapLike: - Extend<(K, V)> - + FromIterator<(K, V)> - + IntoIterator> +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +pub struct AdjacencyTable +where + N: Node, { - fn new() -> Self; - fn capacity(&self) -> usize; - fn clear(&mut self) { - self.table_mut().clear() - } - fn drain(&mut self) -> hash_map::Drain<'_, K, V>; - fn entry(&mut self, key: K) -> hash_map::Entry<'_, K, V> { - self.table_mut().entry(key) - } - fn insert(&mut self, key: K, val: V) -> Option; - fn get(&self, key: &K) -> Option<&V>; - fn keys(&self) -> hash_map::Keys { - self.table().keys() - } - fn table(&self) -> &HashMap; - fn table_mut(&mut self) -> &mut HashMap; -} - -pub struct KeyValue(Vec<(K, Vec)>); - -impl KeyValue { - pub fn new() -> Self { - Self(Vec::new()) - } + store: HashMap>, } -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct AdjacencyTable(HashMap>); - impl AdjacencyTable { pub fn new() -> Self { - Self(HashMap::new()) - } - pub fn with_capacity(capacity: usize) -> Self { - Self(HashMap::with_capacity(capacity)) + Self { + store: HashMap::new(), + } } pub fn capacity(&self) -> usize { - self.0.capacity() + self.store.capacity() } pub fn clear(&mut self) { - self.0.clear() + self.store.clear() } pub fn contains_key(&self, key: &N) -> bool { - self.0.contains_key(key) + self.store.contains_key(key) } pub fn drain(&mut self) -> hash_map::Drain<'_, N, Vec<(N, V)>> { - self.0.drain() + self.store.drain() } pub fn entry(&mut self, key: N) -> hash_map::Entry<'_, N, Vec<(N, V)>> { - self.0.entry(key) + self.store.entry(key) } pub fn insert(&mut self, key: N, val: Vec<(N, V)>) -> Option> { - self.0.insert(key, val) + self.store.insert(key, val) } pub fn get(&self, key: &N) -> Option<&Vec<(N, V)>> { - self.0.get(key) + self.store.get(key) } pub fn get_key_value(&self, key: &N) -> Option<(&N, &Vec<(N, V)>)> { - self.0.get_key_value(key) + self.store.get_key_value(key) } pub fn get_mut(&mut self, key: &N) -> Option<&mut Vec<(N, V)>> { - self.0.get_mut(key) + self.store.get_mut(key) } pub fn keys(&self) -> hash_map::Keys> { - self.0.keys() + self.store.keys() } pub fn len(&self) -> usize { - self.0.len() + self.store.len() } - pub fn table(self) -> HashMap> { - self.0 + self.store } pub fn values(&self) -> hash_map::Values> { - self.0.values() + self.store.values() } pub fn values_mut(&mut self) -> hash_map::ValuesMut> { - self.0.values_mut() + self.store.values_mut() + } + pub fn with_capacity(capacity: usize) -> Self { + Self { + store: HashMap::with_capacity(capacity), + } } } impl Extend<(N, Vec<(N, V)>)> for AdjacencyTable { fn extend)>>(&mut self, iter: T) { - self.0.extend(iter) + self.store.extend(iter) + } +} + +impl From>> for AdjacencyTable { + fn from(store: HashMap>) -> Self { + Self { store } } } @@ -104,7 +87,7 @@ impl FromIterator<(N, Vec<(N, V)>)> for AdjacencyTable { fn from_iter)>>(iter: T) -> Self { let mut map = HashMap::with_hasher(Default::default()); map.extend(iter); - AdjacencyTable(map) + AdjacencyTable::from(map) } } @@ -114,6 +97,20 @@ impl IntoIterator for AdjacencyTable { type IntoIter = hash_map::IntoIter>; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + self.store.into_iter() + } +} + +impl std::ops::Index for AdjacencyTable { + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + &self.store[&index] + } +} + +impl std::ops::IndexMut for AdjacencyTable { + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.store.get_mut(&index).unwrap() } } diff --git a/graph/src/undirected.rs b/graph/src/undirected.rs index c063054..4159f20 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -3,61 +3,144 @@ Contrib: FL03 Description: ... Summary ... */ -use super::{ - cmp::{AdjacencyTable, Edge, Node}, - Graph, Subgraph, -}; +use crate::{cmp::Edge, store::AdjacencyTable}; +use crate::{Contain, Graph, GraphExt, Node, Subgraph, Weight}; + use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct UndirectedGraph { - adjacency_table: AdjacencyTable, +pub struct UndirectedGraph +where + N: Node, + V: Weight, +{ + store: AdjacencyTable, } -impl Graph for UndirectedGraph { - fn new() -> Self { - Self { - adjacency_table: AdjacencyTable::new(), - } +impl AsMut> for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn as_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store + } +} + +impl AsRef> for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn as_ref(&self) -> &AdjacencyTable { + &self.store + } +} + +impl Contain for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn contains(&self, elem: &N) -> bool { + self.store.contains_key(elem) + } +} + +impl Contain> for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn contains(&self, elem: &Edge) -> bool { + self.edges().contains(elem) } +} + +impl Graph for UndirectedGraph +where + N: Node, + V: Weight, +{ fn add_edge(&mut self, edge: Edge) { let pair = edge.pair(); self.add_node(pair.0.clone()); self.add_node(pair.1.clone()); - self.adjacency_table.entry(pair.0.clone()).and_modify(|e| { - e.push((pair.1.clone(), edge.value())); + self.store.entry(pair.0.clone()).and_modify(|e| { + e.push((pair.1.clone(), edge.value().clone())); }); - self.adjacency_table.entry(pair.1).and_modify(|e| { - e.push((pair.0, edge.value())); + self.store.entry(pair.1).and_modify(|e| { + e.push((pair.0, edge.value().clone())); }); } - fn adjacency_table_mut(&mut self) -> &mut AdjacencyTable { - &mut self.adjacency_table + fn store_mut(&mut self) -> &mut AdjacencyTable { + &mut self.store } - fn adjacency_table(&self) -> &AdjacencyTable { - &self.adjacency_table + fn store(&self) -> &AdjacencyTable { + &self.store + } +} + +impl GraphExt for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn new() -> Self { + Self { + store: AdjacencyTable::new(), + } } fn with_capacity(capacity: usize) -> Self { Self { - adjacency_table: AdjacencyTable::with_capacity(capacity), + store: AdjacencyTable::with_capacity(capacity), } } } -impl Subgraph for UndirectedGraph {} +impl Subgraph for UndirectedGraph +where + N: Node, + V: Weight, +{ +} -impl From> for UndirectedGraph { - fn from(adjacency_table: AdjacencyTable) -> Self { - Self { adjacency_table } +impl From> for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn from(store: AdjacencyTable) -> Self { + Self { store } + } +} + +impl std::ops::Index for UndirectedGraph +where + N: Node, + V: Weight, +{ + type Output = Vec<(N, V)>; + + fn index(&self, index: N) -> &Self::Output { + &self.store[index] + } +} + +impl std::ops::IndexMut for UndirectedGraph +where + N: Node, + V: Weight, +{ + fn index_mut(&mut self, index: N) -> &mut Self::Output { + self.store.index_mut(index) } } #[cfg(test)] mod tests { - use super::Graph; - use super::UndirectedGraph; - use crate::cmp::Edge; + use super::*; const TEST_EDGES: [(&str, &str, usize); 3] = [("a", "b", 5), ("c", "a", 7), ("b", "c", 10)]; @@ -77,10 +160,10 @@ mod tests { for i in TEST_EDGES { graph.add_edge(i.into()); } - - for edge in EXPECTED { - assert!(graph.edges().contains(&Edge::from(edge))); - } + // assert that the graph contains all the edges + assert!(graph.contains_all(EXPECTED.into_iter().map(Edge::from).collect::>())); + // assert that the graph can be indexed + assert_eq!(graph["a"], vec![("b", 5), ("c", 7)]); } #[test] @@ -91,6 +174,6 @@ mod tests { graph.add_edge(i.into()); } - assert_eq!(graph.neighbours("a").unwrap(), &vec![("b", 5), ("c", 7)]); + assert_eq!(graph["a"], vec![("b", 5), ("c", 7)]); } } diff --git a/merkle/Cargo.toml b/merkle/Cargo.toml index 9c8af81..0e5c0e2 100644 --- a/merkle/Cargo.toml +++ b/merkle/Cargo.toml @@ -25,14 +25,11 @@ crate-type = ["cdylib", "rlib"] test = true [dependencies] -anyhow = "1" -hex = "0.4" -log = "0.4" -ring = { features = ["wasm32_c"], version = "0.16" } - +anyhow.workspace = true decanter.workspace = true +hex = "0.4" itertools.workspace = true -# scsys.workspace = true +ring = { features = ["wasm32_c"], version = "0.16" } serde.workspace = true serde_json.workspace = true smart-default.workspace = true @@ -40,6 +37,7 @@ strum.workspace = true [dev-dependencies] hex-literal = "0.3" +log = "0.4" vrf = "0.2" [package.metadata.docs.rs] diff --git a/merkle/src/layers.rs b/merkle/src/layers.rs index f5b090e..140be48 100644 --- a/merkle/src/layers.rs +++ b/merkle/src/layers.rs @@ -11,9 +11,14 @@ use std::string::ToString; // pub fn build_new_merkle_layer(left: MerkleNode, right: MerkleNode) #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Layer(Vec>); +pub struct Layer(Vec>) +where + T: Default + ToString; -impl Layer { +impl Layer +where + T: Default + ToString, +{ pub fn new(data: Vec>) -> Self { let layer = data.into_iter().batching(|it| match it.next() { Some(l) => match it.next() { @@ -30,13 +35,19 @@ impl Layer { } } -impl std::convert::From>> for Layer { +impl From>> for Layer +where + T: Default + ToString, +{ fn from(data: Vec>) -> Self { Self::new(data) } } -impl std::convert::From<(Node, Node)> for Layer { +impl From<(Node, Node)> for Layer +where + T: Default + ToString, +{ fn from(data: (Node, Node)) -> Self { Self::new(vec![data.0, data.1]) } diff --git a/merkle/src/lib.rs b/merkle/src/lib.rs index 87b83a5..91d8661 100644 --- a/merkle/src/lib.rs +++ b/merkle/src/lib.rs @@ -6,13 +6,38 @@ #[cfg(test)] extern crate hex_literal; #[doc(inline)] -pub use self::{layers::*, nodes::*, payloads::*, shape::*, trees::*, utils::*}; +pub use self::{layers::*, nodes::*, payloads::*, shape::*, tree::*, utils::*}; pub(crate) mod layers; pub(crate) mod nodes; pub(crate) mod payloads; pub(crate) mod shape; -pub(crate) mod trees; -pub(crate) mod utils; +pub(crate) mod tree; +mod utils; pub mod proofs; + +use decanter::prelude::H256; +use proofs::merkle_proof; + +pub trait MerkleTreeSpec: Clone { + // Returns the dimension of the given tree + fn dim(&self) -> MerkleDimension; + // Returns the nodes of the given tree + fn nodes(&self) -> Vec; + // Returns the proof for the given index + fn proof(&self, index: usize) -> Vec { + merkle_proof(self.dim(), self.nodes(), index) + } + // Writes the injected nodes to the console for viewing purposes + fn print(&self) -> &Self { + for i in 0..self.dim().size { + println!("{:?}", self.nodes()[i]); + } + self + } + // Returns the root hash of the merkle tree + fn root(&self) -> H256 { + self.nodes()[self.dim().size() - 1] + } +} diff --git a/merkle/src/nodes.rs b/merkle/src/nodes.rs index 7ae9ef9..ab5c7a0 100644 --- a/merkle/src/nodes.rs +++ b/merkle/src/nodes.rs @@ -8,25 +8,37 @@ use decanter::prelude::{Hashable, H256}; use serde::{Deserialize, Serialize}; use std::string::ToString; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Node { +#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Node +where + T: Default + ToString, +{ pub data: Payload, pub hash: H256, } -impl Node { +impl Node +where + T: Default + ToString, +{ pub fn new(data: Payload, hash: H256) -> Self { Self { data, hash } } } -impl Hashable for Node { +impl Hashable for Node +where + T: Default + ToString, +{ fn hash(&self) -> H256 { - merkle_hash(&self.data) + merkle_hash(self.data.to_string()) } } -impl std::convert::From<(Node, Node)> for Node { +impl From<(Node, Node)> for Node +where + T: Default + ToString, +{ fn from(data: (Node, Node)) -> Self { let hash = merkle_hash(combine(&data.0.hash, &data.1.hash)); let data = Payload::Node(Box::new(data.0), Box::new(data.1)); @@ -34,17 +46,15 @@ impl std::convert::From<(Node, Node)> for Node { } } -impl std::convert::From for Node { - fn from(data: T) -> Self { - Self::new(Payload::from(data.clone()), merkle_hash(data)) - } -} - impl std::fmt::Display for Node where - T: Serialize + ToString, + T: Default + ToString, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) + let msg = serde_json::json!({ + "data": self.data.to_string(), + "hash": self.hash, + }); + write!(f, "{}", msg) } } diff --git a/merkle/src/payloads.rs b/merkle/src/payloads.rs index c59ef45..ff5f254 100644 --- a/merkle/src/payloads.rs +++ b/merkle/src/payloads.rs @@ -4,47 +4,73 @@ Description: ... Summary ... */ use crate::Node; +use decanter::prelude::Hashable; use serde::{Deserialize, Serialize}; +use smart_default::SmartDefault; use std::string::ToString; +use strum::Display; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub enum Payload { +#[derive( + Clone, + Debug, + Deserialize, + Display, + Eq, + Hash, + Hashable, + Ord, + PartialEq, + PartialOrd, + Serialize, + SmartDefault, +)] +pub enum Payload +where + T: Default + ToString, +{ + #[default] Leaf(T), Node(Box>, Box>), } impl Payload where - T: ToString, + T: Default + ToString, { - pub fn new_leaf(data: T) -> Self { + pub fn leaf(data: T) -> Self { Self::Leaf(data) } - pub fn new_node(left: Box>, right: Box>) -> Self { + pub fn node(left: Box>, right: Box>) -> Self { Self::Node(left, right) } + pub fn is_leaf(&self) -> bool { + match self { + Self::Leaf(_) => true, + _ => false, + } + } + pub fn is_node(&self) -> bool { + match self { + Self::Node(_, _) => true, + _ => false, + } + } } -impl std::fmt::Display for Payload +impl From for Payload where - T: Serialize + ToString, + T: Default + ToString, { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} - -impl std::convert::From for Payload { fn from(data: T) -> Self { Self::Leaf(data) } } -impl std::convert::From<(Box>, Box>)> for Payload +impl From<(Box>, Box>)> for Payload where - T: ToString, + T: Default + ToString, { fn from(data: (Box>, Box>)) -> Self { - Self::new_node(data.0, data.1) + Self::node(data.0, data.1) } } diff --git a/merkle/src/proofs/proof.rs b/merkle/src/proofs/proof.rs index 037d0d3..d175472 100644 --- a/merkle/src/proofs/proof.rs +++ b/merkle/src/proofs/proof.rs @@ -4,9 +4,44 @@ Description: ... Summary ... */ use super::path::proof_path; -use crate::MerkleDimension; +use crate::{MerkleDimension, MerkleTree, MerkleTreeSpec}; use decanter::prelude::H256; +pub struct Prover { + cursor: usize, + proof: Vec, + tree: MerkleTree, +} + +impl Prover { + pub fn new(tree: MerkleTree) -> Self { + Self { + cursor: 0, + proof: Vec::new(), + tree, + } + } + /// Returns the proof for the given index + pub fn prove(&mut self, index: usize) -> Vec { + self.proof = self.tree.proof(index); + self.proof.clone() + } +} + +impl Iterator for Prover { + type Item = Vec; + + fn next(&mut self) -> Option { + if self.cursor < self.proof.len() { + let item = self.prove(self.cursor); + self.cursor += 1; + Some(item) + } else { + None + } + } +} + // Returns the proof for the given index pub fn merkle_proof(dim: MerkleDimension, nodes: Vec, index: usize) -> Vec { let mut proof: Vec = Vec::new(); diff --git a/merkle/src/shape.rs b/merkle/src/shape.rs index c1d5810..e8351a9 100644 --- a/merkle/src/shape.rs +++ b/merkle/src/shape.rs @@ -14,7 +14,9 @@ pub trait MerkleShape { fn size(&self) -> usize; } -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] +#[derive( + Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, +)] pub struct MerkleDimension { pub depth: usize, pub leafs: usize, @@ -40,20 +42,20 @@ impl MerkleShape for MerkleDimension { } } -impl std::convert::From> for MerkleDimension { +impl std::fmt::Display for MerkleDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {}, {})", self.depth, self.leafs, self.size) + } +} + +impl From> for MerkleDimension { fn from(data: Box) -> Self { Self::from(data.shape()) } } -impl std::convert::From<(usize, usize, usize)> for MerkleDimension { +impl From<(usize, usize, usize)> for MerkleDimension { fn from(data: (usize, usize, usize)) -> Self { Self::new(data.0, data.1, data.2) } } - -impl std::fmt::Display for MerkleDimension { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({}, {}, {})", self.depth, self.leafs, self.size) - } -} diff --git a/merkle/src/trees/tree.rs b/merkle/src/tree.rs similarity index 55% rename from merkle/src/trees/tree.rs rename to merkle/src/tree.rs index ed0e573..ced672f 100644 --- a/merkle/src/trees/tree.rs +++ b/merkle/src/tree.rs @@ -3,13 +3,11 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{ - create_merkle_tree, proofs::merkle_proof, MerkleDimension, MerkleShape, MerkleTreeWrapper, - MerkleTreeWrapperExt, -}; +use crate::{create_merkle_tree, MerkleDimension, MerkleShape, MerkleTreeSpec}; use decanter::prelude::{Hashable, H256}; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct MerkleTree { pub dim: MerkleDimension, pub nodes: Vec, @@ -19,42 +17,37 @@ impl MerkleTree { pub fn new(dim: MerkleDimension, nodes: Vec) -> Self { Self { dim, nodes } } - // Returns the proof for the given index - pub fn proof(&self, index: usize) -> Vec { - merkle_proof(self.dim.clone(), self.nodes.clone(), index) - } } -impl MerkleTreeWrapper for MerkleTree { - fn new(dim: MerkleDimension, nodes: Vec) -> Self { - Self { dim, nodes } - } - +impl MerkleTreeSpec for MerkleTree { fn dim(&self) -> MerkleDimension { self.dim.clone() } - fn nodes(&self) -> Vec { self.nodes.clone() } } -impl MerkleTreeWrapperExt for MerkleTree {} +impl std::fmt::Display for MerkleTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.nodes) + } +} -impl std::convert::From<&[T]> for MerkleTree { +impl From<&[T]> for MerkleTree { fn from(data: &[T]) -> Self { let (dim, nodes) = create_merkle_tree::(data); Self::new(MerkleDimension::from(dim), nodes) } } -impl std::convert::From<(Box, Vec)> for MerkleTree { +impl From<(Box, Vec)> for MerkleTree { fn from(data: (Box, Vec)) -> Self { Self::new(MerkleDimension::from(data.0), data.1) } } -impl std::convert::From> for MerkleTree { +impl From> for MerkleTree { fn from(data: Vec) -> Self { Self::from(create_merkle_tree(&data)) } diff --git a/merkle/src/trees/interface.rs b/merkle/src/trees/interface.rs deleted file mode 100644 index ece2c4f..0000000 --- a/merkle/src/trees/interface.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* - Appellation: tree - Contrib: FL03 - Description: - Merkle Tree def... -*/ -use crate::{Layer, Node}; -use decanter::prelude::{Hashable, H256}; -use serde::{Deserialize, Serialize}; -use std::string::ToString; - -/// Implements a complete merkle tree -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct Tree { - pub leaves: Vec, - pub root: Node, -} - -impl Tree { - pub fn new(leaves: Vec, root: Node) -> Self { - Self { leaves, root } - } - pub fn root_hash(&self) -> H256 { - self.root.hash() - } -} - -impl std::convert::From for Tree -where - ::Item: Clone + Serialize + ToString, -{ - fn from(data: II) -> Self { - let leaves = data.into_iter().collect::>(); - - let mut layer: Vec<_> = leaves.iter().cloned().map(Node::from).collect(); - - while layer.len() != 1 { - layer = Layer::from(layer).layer().clone(); - } - - match layer.pop() { - Some(root) => Self::new(leaves, root), - None => panic!("You should not have built an empty tree"), - } - } -} diff --git a/merkle/src/trees/mod.rs b/merkle/src/trees/mod.rs deleted file mode 100644 index d13c92e..0000000 --- a/merkle/src/trees/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -/* - Appellation: interfaces - Contrib: FL03 - Description: ... Summary ... -*/ -pub use self::{interface::*, tree::*, utils::*}; - -pub(crate) mod interface; -pub(crate) mod tree; - -use crate::{proofs::merkle_proof, MerkleDimension, MerkleShape}; -use decanter::prelude::{Hashable, H256}; - -pub trait MerkleTreeWrapper { - fn new(dim: MerkleDimension, nodes: Vec) -> Self; - fn create(data: &[T]) -> Self - where - Self: Sized, - T: Hashable, - { - let (dim, nodes) = create_merkle_tree(data); - - Self::new(dim.into(), nodes) - } - fn dim(&self) -> MerkleDimension; - fn nodes(&self) -> Vec; - // Returns the proof for the given index - fn proof(&self, index: usize) -> Vec { - merkle_proof(self.dim(), self.nodes(), index) - } - // Returns the root hash of the merkle tree - fn root(&self) -> H256 { - self.nodes()[self.dim().size() - 1] - } -} - -pub trait MerkleTreeWrapperExt: MerkleTreeWrapper { - // Writes the injected nodes to the console for viewing purposes - fn print(&self) -> &Self { - for i in 0..self.dim().size { - println!("{:?}", self.nodes()[i]); - } - self - } -} -pub(crate) mod utils { - use crate::{add_hash, MerkleDimension, MerkleShape}; - use decanter::prelude::{Hashable, H256}; - - pub fn create_merkle_tree(data: &[T]) -> (Box, Vec) - where - T: Hashable, - { - // unimplemented!() - let mut length = data.len(); - let mut nodes = Vec::new(); - let mut last_level = Vec::new(); - for i in data { - let h: H256 = i.hash(); - last_level.push(h); - nodes.push(h); - } - let mut depth = 1; - while length > 1 { - if length % 2 != 0 { - last_level.push(data[length - 1].hash()); - nodes.push(data[length - 1].hash()); - length += 1; - } - let mut temp = Vec::new(); - for i in 0..length / 2 { - let h: H256 = add_hash(&last_level[2 * i], &last_level[2 * i + 1]); - temp.push(h); - nodes.push(h); - } - last_level = temp.clone(); - length /= 2; - depth += 1; - } - let dim = MerkleDimension::new(depth, data.len(), nodes.len()); - (Box::new(dim), nodes) - } -} diff --git a/merkle/src/utils.rs b/merkle/src/utils.rs index 78dc159..e00de39 100644 --- a/merkle/src/utils.rs +++ b/merkle/src/utils.rs @@ -4,28 +4,59 @@ Description: */ use crate::proofs::proof_path; -use decanter::prelude::{hasher, H256}; +use crate::{MerkleDimension, MerkleShape}; +use decanter::prelude::{hasher, Hashable, H256}; use serde::Serialize; /// pub fn add_hash(a: &H256, b: &H256) -> H256 { let c = [a.as_ref(), b.as_ref()].concat(); let combined = ring::digest::digest(&ring::digest::SHA256, &c); - ::from(combined) + hasher(combined).into() } /// Merges two hashes into a string pub fn combine(a: &T, b: &T) -> String { format!("{}{}", a.to_string(), b.to_string()) } +/// Creates a Merkle tree from a slice of data +pub fn create_merkle_tree(data: &[T]) -> (Box, Vec) +where + T: Hashable, +{ + let mut length = data.len(); + let mut nodes = Vec::new(); + let mut last_level = Vec::new(); + for i in data { + let h: H256 = i.hash(); + last_level.push(h); + nodes.push(h); + } + let mut depth = 1; + while length > 1 { + if length % 2 != 0 { + last_level.push(data[length - 1].hash()); + nodes.push(data[length - 1].hash()); + length += 1; + } + let mut temp = Vec::new(); + for i in 0..length / 2 { + let h: H256 = add_hash(&last_level[2 * i], &last_level[2 * i + 1]); + temp.push(h); + nodes.push(h); + } + last_level = temp.clone(); + length /= 2; + depth += 1; + } + let dim = MerkleDimension::new(depth, data.len(), nodes.len()); + (Box::new(dim), nodes) +} /// Takes the hash of the given information to the second degree -pub fn merkle_hash(data: T) -> H256 { - let res: H256 = { - let tmp: H256 = hasher(&data).as_slice().to_owned().into(); - hasher(&tmp).as_slice().to_owned().into() - }; - res +pub fn merkle_hash(data: impl AsRef<[u8]>) -> H256 { + let tmp: H256 = hasher(data).into(); + hasher(&tmp).into() } /// Verify that the datum hash with a vector of proofs will produce the Merkle root. Also need the diff --git a/merkle/tests/merkle.rs b/merkle/tests/merkle.rs index 732c8bd..7b251a6 100644 --- a/merkle/tests/merkle.rs +++ b/merkle/tests/merkle.rs @@ -7,7 +7,7 @@ def. This notation abbreviates a is the hash of A; more formally, (A) maps to the hash (a) by the hashing function H */ -use algae_merkle::{is_merkle_valid, MerkleTree, MerkleTreeWrapper}; +use algae_merkle::{is_merkle_valid, MerkleTree, MerkleTreeSpec}; use decanter::prelude::{Hashable, H256}; use hex_literal::hex; diff --git a/mmr/Cargo.toml b/mmr/Cargo.toml index 2d1be0a..5796c33 100644 --- a/mmr/Cargo.toml +++ b/mmr/Cargo.toml @@ -24,19 +24,17 @@ crate-type = ["cdylib", "rlib"] test = true [dependencies] +anyhow.workspace = true decanter.workspace = true +digest = "0.10" +hex = "0.4" itertools.workspace = true serde.workspace = true serde_json.workspace = true smart-default.workspace = true strum.workspace = true -anyhow = "1" -digest = "0.10" -hex = "0.4" - [dev-dependencies] -scsys.workspace = true hex-literal = "0.3.4" vrf = "0.2.4" diff --git a/mmr/src/actors/builders/mod.rs b/mmr/src/actors/builders/mod.rs deleted file mode 100644 index df7d0fb..0000000 --- a/mmr/src/actors/builders/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: builders - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::builder::*; - -pub(crate) mod builder; diff --git a/mmr/src/actors/builders/builder.rs b/mmr/src/builder.rs similarity index 76% rename from mmr/src/actors/builders/builder.rs rename to mmr/src/builder.rs index 0bc3970..c5da5c5 100644 --- a/mmr/src/actors/builders/builder.rs +++ b/mmr/src/builder.rs @@ -6,6 +6,9 @@ use crate::MerkleMountainRange; use decanter::prelude::Hashable; -pub struct MerkleMountainRangeBuilder { +pub struct MerkleMountainRangeBuilder +where + T: ToString, +{ pub mmr: MerkleMountainRange, } diff --git a/mmr/src/cmp/mod.rs b/mmr/src/cmp/mod.rs new file mode 100644 index 0000000..539a0b1 --- /dev/null +++ b/mmr/src/cmp/mod.rs @@ -0,0 +1,10 @@ +/* + Appellation: cmp + Contrib: FL03 + Description: This module implements a host of components for building a Merkle Mountain Range. +*/ +pub use self::{nodes::*, payloads::*, position::*}; + +mod nodes; +mod payloads; +mod position; diff --git a/mmr/src/cmps/nodes/node.rs b/mmr/src/cmp/nodes.rs similarity index 100% rename from mmr/src/cmps/nodes/node.rs rename to mmr/src/cmp/nodes.rs diff --git a/mmr/src/cmp/payloads.rs b/mmr/src/cmp/payloads.rs new file mode 100644 index 0000000..5870b0c --- /dev/null +++ b/mmr/src/cmp/payloads.rs @@ -0,0 +1,53 @@ +/* + Appellation: rangemap + Contrib: FL03 + Description: ... summary ... +*/ +use decanter::prelude::{Hashable, H256}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hashable, PartialEq, Serialize)] +pub struct Payload +where + T: ToString, +{ + id: H256, + data: HashMap, +} + +impl Payload +where + T: ToString, +{ + pub fn new() -> Self { + Self { + id: H256::generate(), + data: HashMap::new(), + } + } + pub fn data(&self) -> &HashMap { + &self.data + } +} + +impl From> for Payload +where + T: ToString, +{ + fn from(data: HashMap) -> Self { + Self { + id: H256::generate(), + data, + } + } +} + +impl std::fmt::Display for Payload +where + T: ToString, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.id) + } +} diff --git a/mmr/src/cmps/positions/position.rs b/mmr/src/cmp/position.rs similarity index 71% rename from mmr/src/cmps/positions/position.rs rename to mmr/src/cmp/position.rs index 9f629b0..dc34777 100644 --- a/mmr/src/cmps/positions/position.rs +++ b/mmr/src/cmp/position.rs @@ -3,10 +3,12 @@ Contrib: FL03 Description: ... summary ... */ -use decanter::prelude::{hasher, Hashable, H256}; +use decanter::prelude::Hashable; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] +#[derive( + Clone, Debug, Default, Deserialize, Eq, Hash, Hashable, Ord, PartialEq, PartialOrd, Serialize, +)] pub struct Position { pub height: usize, pub index: usize, @@ -16,6 +18,12 @@ impl Position { pub fn new(height: usize, index: usize) -> Self { Self { height, index } } + pub fn height(&self) -> usize { + self.height + } + pub fn index(&self) -> usize { + self.index + } } impl From<(usize, usize)> for Position { @@ -24,12 +32,6 @@ impl From<(usize, usize)> for Position { } } -impl Hashable for Position { - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - impl std::fmt::Display for Position { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", serde_json::to_string(&self).unwrap()) diff --git a/mmr/src/cmps/mod.rs b/mmr/src/cmps/mod.rs deleted file mode 100644 index 9d07b0f..0000000 --- a/mmr/src/cmps/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* - Appellation: cmps - Contrib: FL03 - Description: -*/ - -pub mod nodes; -pub mod payloads; -pub mod positions; diff --git a/mmr/src/cmps/nodes/mod.rs b/mmr/src/cmps/nodes/mod.rs deleted file mode 100644 index 8cc5492..0000000 --- a/mmr/src/cmps/nodes/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: nodes - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::node::*; - -pub(crate) mod node; diff --git a/mmr/src/cmps/payloads/mod.rs b/mmr/src/cmps/payloads/mod.rs deleted file mode 100644 index ee44695..0000000 --- a/mmr/src/cmps/payloads/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* -Appellation: stores -Contrib: FL03 -Description: ... summary ... -*/ -pub use self::payload::*; - -pub(crate) mod payload; diff --git a/mmr/src/cmps/payloads/payload.rs b/mmr/src/cmps/payloads/payload.rs deleted file mode 100644 index 729a2f9..0000000 --- a/mmr/src/cmps/payloads/payload.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - Appellation: rangemap - Contrib: FL03 - Description: ... summary ... -*/ -use decanter::prelude::{hasher, Hashable, H256}; -use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, convert::From}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct RangeMap(pub HashMap); - -impl RangeMap { - pub fn data(&self) -> &HashMap { - &self.0 - } -} - -impl From> for RangeMap { - fn from(data: HashMap) -> Self { - Self(data) - } -} - -impl Hashable for RangeMap { - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - -impl std::fmt::Display for RangeMap { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} diff --git a/mmr/src/cmps/positions/mod.rs b/mmr/src/cmps/positions/mod.rs deleted file mode 100644 index df3172c..0000000 --- a/mmr/src/cmps/positions/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: positions - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::position::*; - -pub(crate) mod position; diff --git a/mmr/src/core/mod.rs b/mmr/src/core/mod.rs deleted file mode 100644 index 10b1442..0000000 --- a/mmr/src/core/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* - Appellation: core - Contrib: FL03 - Description: -*/ -pub use self::{mmr::*, primitives::*, utils::*}; - -pub(crate) mod mmr; -pub(crate) mod utils; - -pub(crate) mod primitives {} diff --git a/mmr/src/data/mod.rs b/mmr/src/data/mod.rs deleted file mode 100644 index 2281558..0000000 --- a/mmr/src/data/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - Appellation: data - Contrib: FL03 - Description: ... summary ... -*/ - -pub mod proofs; -pub mod stores; diff --git a/mmr/src/data/stores/store.rs b/mmr/src/data/stores/store.rs deleted file mode 100644 index c85658a..0000000 --- a/mmr/src/data/stores/store.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* - Appellation: store - Contrib: FL03 - Description: ... summary ... -*/ -use decanter::prelude::{hasher, Hashable, H256}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct MMRStore(pub T); - -impl MMRStore { - pub fn new(data: T) -> Self { - Self(data) - } - pub fn data(&self) -> &T { - &self.0 - } -} - -impl Hashable for MMRStore -where - T: Serialize, -{ - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - -impl std::convert::From<&MMRStore> for MMRStore -where - T: Clone, -{ - fn from(data: &MMRStore) -> Self { - Self(data.clone().0) - } -} - -impl std::convert::From for MMRStore { - fn from(data: T) -> Self { - Self(data) - } -} - -impl std::fmt::Display for MMRStore -where - T: Serialize, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use scsys::gen::generate_random_string; - use std::ops::Range; - #[test] - fn test_store_default() { - let a = MMRStore::::default(); - let b = MMRStore::::new(Default::default()); - assert_eq!(a, b) - } - - #[test] - fn test_store_hash() { - let data = Range { start: 0, end: 12 } - .map(|_| generate_random_string(12)) - .collect::>(); - - let a = MMRStore::>::from(data); - let b = MMRStore::from(&a); - assert_eq!(a, b) - } -} diff --git a/mmr/src/lib.rs b/mmr/src/lib.rs index 860c83c..ebdefe7 100644 --- a/mmr/src/lib.rs +++ b/mmr/src/lib.rs @@ -3,9 +3,13 @@ Contrib: FL03 Description: */ -pub use self::{actors::*, cmps::*, core::*, data::*}; +pub use self::{builder::*, mmr::*, primitives::*, utils::*}; -pub(crate) mod actors; -pub(crate) mod cmps; -pub(crate) mod core; -pub(crate) mod data; +mod builder; +mod mmr; +mod primitives; +mod utils; + +pub mod cmp; +pub mod proofs; +pub mod stores; diff --git a/mmr/src/core/mmr.rs b/mmr/src/mmr.rs similarity index 92% rename from mmr/src/core/mmr.rs rename to mmr/src/mmr.rs index bd3f71a..2fc27a2 100644 --- a/mmr/src/core/mmr.rs +++ b/mmr/src/mmr.rs @@ -3,22 +3,27 @@ Contrib: FL03 Description: ... summary ... */ -use crate::{ - is_node_right, nodes::MerkleNode, payloads::RangeMap, positions::Position, sibling_index, -}; +use crate::cmp::{MerkleNode, Position}; +use crate::{is_node_right, sibling_index, RangeMap}; use decanter::prelude::{hasher, Hashable, H256}; use digest::Digest; use serde::{Deserialize, Serialize}; use std::convert::From; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct MerkleMountainRange { +#[derive(Clone, Debug, Default, Deserialize, Eq, Hashable, PartialEq, Serialize)] +pub struct MerkleMountainRange +where + T: ToString, +{ data: RangeMap, mmr: Vec, // todo convert these to a bitmap position: Position, } -impl MerkleMountainRange { +impl MerkleMountainRange +where + T: ToString, +{ pub fn new(mmr: Vec, data: RangeMap, position: Position) -> Self { Self { mmr, @@ -26,17 +31,81 @@ impl MerkleMountainRange { position, } } + /// This function adds a vec of leaf nodes to the mmr. + pub fn add_vec(&mut self, objects: Vec) { + for object in objects { + self.add_single::(object); + } + } + /// This function adds a new leaf node to the mmr. + pub fn add_single(&mut self, object: T) { + let node_hash: H256 = hasher(object.to_string()).into(); + let node = MerkleNode::from(node_hash); + self.data.insert(node_hash, object); + self.mmr.push(node); + if is_node_right(self.get_last_added_index()) { + self.add_single_no_leaf::(self.get_last_added_index()) + } + } + // This function adds non leaf nodes, eg nodes that are not directly a hash of data + // This is iterative and will continue to up and till it hits the top, will be a future left child + fn add_single_no_leaf(&mut self, index: usize) { + let mut hasher = D::new(); + hasher.update(self.mmr[sibling_index(index)].hash); + hasher.update(self.mmr[index].hash); + let new_hash: H256 = hasher.finalize().to_vec().into(); + let new_node = MerkleNode::from(new_hash); + self.mmr.push(new_node); + if is_node_right(self.get_last_added_index()) { + self.add_single_no_leaf::(self.get_last_added_index()) + } else { + self.position = self.calc_peak_height().into(); // because we have now stopped adding right nodes, we need to update the height of the mmr + } + } + fn bag_mmr(&self) -> Vec { + // lets find all peaks of the mmr + let mut peaks = Vec::new(); + self.find_bagging_indexes(self.position.height as i64, self.position.index, &mut peaks); + peaks + } + // This function calculates the peak height of the mmr + fn calc_peak_height(&self) -> (usize, usize) { + let mut height_counter = 0; + let mmr_len = self.get_last_added_index(); + let mut index: usize = (1 << (height_counter + 2)) - 2; + let mut actual_height_index = 0; + while mmr_len >= index { + // find the height of the tree by finding if we can subtract the height +1 + height_counter += 1; + actual_height_index = index; + index = (1 << (height_counter + 2)) - 2; + } + (height_counter, actual_height_index) + } + fn find_bagging_indexes(&self, mut height: i64, index: usize, peaks: &mut Vec) { + let mut new_index = index + (1 << (height + 1)) - 1; // go the potential right sibling + while (new_index > self.get_last_added_index()) && (height > 0) { + // lets go down left child till we hit a valid node or we reach height 0 + new_index -= 1 << height; + height -= 1; + } + if (new_index <= self.get_last_added_index()) && (height >= 0) { + // is this a valid peak which needs to be bagged + peaks.push(self.mmr[new_index].hash); + self.find_bagging_indexes(height, new_index, peaks); // lets go look for more peaks + } + } /// This function returns a reference to the data stored in the mmr /// It will return none if the hash does not exist pub fn get_object(&self, hash: &H256) -> Option<&T> { - self.data.0.get(hash) + self.data.get(hash) } /// This function returns a mut reference to the data stored in the MMR /// It will return none if the hash does not exist pub fn get_mut_object(&mut self, hash: &H256) -> Option<&mut T> { - self.data.0.get_mut(hash) + self.data.get_mut(hash) } pub fn get_hash(&self, index: usize) -> Option { @@ -128,43 +197,10 @@ impl MerkleMountainRange { } self.get_ordered_hash_proof(next_index, results); } - - /// This function will verify the provided proof. Internally it uses the get_hash_proof function to construct a - /// similar proof. This function will return true if the proof is valid - /// If the order does not match Lchild-Rchild-parent(Lchild)-Rchild-parent-.. the validation will fail - /// This function will only succeed if the given hash is of height 0 - pub fn verify_proof(&self, hashes: &Vec) -> bool { - if hashes.is_empty() { - return false; - } - if self.get_object(&hashes[0]).is_none() && self.get_object(&hashes[1]).is_none() { - // we only want to search for valid object's proofs, either 0 or 1 must be a valid object - return false; - } - let proof = self.get_hash_proof::(&hashes[0]); - hashes.eq(&proof) - } - - // This function calculates the peak height of the mmr - fn calc_peak_height(&self) -> (usize, usize) { - let mut height_counter = 0; - let mmr_len = self.get_last_added_index(); - let mut index: usize = (1 << (height_counter + 2)) - 2; - let mut actual_height_index = 0; - while mmr_len >= index { - // find the height of the tree by finding if we can subtract the height +1 - height_counter += 1; - actual_height_index = index; - index = (1 << (height_counter + 2)) - 2; - } - (height_counter, actual_height_index) - } - /// This function returns the peak height of the mmr pub fn get_peak_height(&self) -> usize { self.position.height } - /// This function will return the single merkle root of the MMR. pub fn get_merkle_root(&self) -> H256 { let mut peaks = self.bag_mmr(); @@ -187,76 +223,32 @@ impl MerkleMountainRange { // there was no other peaks, return the highest peak self.mmr[self.position.index].hash } - - /// This function adds a vec of leaf nodes to the mmr. - pub fn add_vec(&mut self, objects: Vec) { - for object in objects { - self.add_single::(object); - } - } - - /// This function adds a new leaf node to the mmr. - pub fn add_single(&mut self, object: T) { - let node_hash = object.hash(); - let node = MerkleNode::from(node_hash); - self.data.0.insert(node_hash, object); - self.mmr.push(node); - if is_node_right(self.get_last_added_index()) { - self.add_single_no_leaf::(self.get_last_added_index()) - } - } - - // This function adds non leaf nodes, eg nodes that are not directly a hash of data - // This is iterative and will continue to up and till it hits the top, will be a future left child - fn add_single_no_leaf(&mut self, index: usize) { - let mut hasher = D::new(); - hasher.update(self.mmr[sibling_index(index)].hash); - hasher.update(self.mmr[index].hash); - let new_hash: H256 = hasher.finalize().to_vec().into(); - let new_node = MerkleNode::from(new_hash); - self.mmr.push(new_node); - if is_node_right(self.get_last_added_index()) { - self.add_single_no_leaf::(self.get_last_added_index()) - } else { - self.position = self.calc_peak_height().into(); // because we have now stopped adding right nodes, we need to update the height of the mmr - } - } - // This function is just a private function to return the index of the last added node fn get_last_added_index(&self) -> usize { self.mmr.len() - 1 } - - fn bag_mmr(&self) -> Vec { - // lets find all peaks of the mmr - let mut peaks = Vec::new(); - self.find_bagging_indexes(self.position.height as i64, self.position.index, &mut peaks); - peaks - } - - fn find_bagging_indexes(&self, mut height: i64, index: usize, peaks: &mut Vec) { - let mut new_index = index + (1 << (height + 1)) - 1; // go the potential right sibling - while (new_index > self.get_last_added_index()) && (height > 0) { - // lets go down left child till we hit a valid node or we reach height 0 - new_index -= 1 << height; - height -= 1; + /// This function will verify the provided proof. Internally it uses the get_hash_proof function to construct a + /// similar proof. This function will return true if the proof is valid + /// If the order does not match Lchild-Rchild-parent(Lchild)-Rchild-parent-.. the validation will fail + /// This function will only succeed if the given hash is of height 0 + pub fn verify_proof(&self, hashes: &Vec) -> bool { + if hashes.is_empty() { + return false; } - if (new_index <= self.get_last_added_index()) && (height >= 0) { - // is this a valid peak which needs to be bagged - peaks.push(self.mmr[new_index].hash); - self.find_bagging_indexes(height, new_index, peaks); // lets go look for more peaks + if self.get_object(&hashes[0]).is_none() && self.get_object(&hashes[1]).is_none() { + // we only want to search for valid object's proofs, either 0 or 1 must be a valid object + return false; } + let proof = self.get_hash_proof::(&hashes[0]); + hashes.eq(&proof) } } -impl Hashable for MerkleMountainRange { - fn hash(&self) -> H256 { - hasher(&self).as_slice().to_owned().into() - } -} - -impl std::fmt::Display for MerkleMountainRange { +impl std::fmt::Display for MerkleMountainRange +where + T: ToString, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) + write!(f, "{:?}", self.mmr) } } diff --git a/mmr/src/primitives.rs b/mmr/src/primitives.rs new file mode 100644 index 0000000..b73fd15 --- /dev/null +++ b/mmr/src/primitives.rs @@ -0,0 +1,17 @@ +/* + Appellation: primitives + Contrib: FL03 + Description: ... summary ... +*/ +pub use self::{constants::*, statics::*, types::*}; + +mod constants {} + +mod statics {} + +mod types { + use decanter::prelude::H256; + use std::collections::HashMap; + + pub type RangeMap = HashMap; +} diff --git a/mmr/src/data/proofs/mod.rs b/mmr/src/proofs/mod.rs similarity index 100% rename from mmr/src/data/proofs/mod.rs rename to mmr/src/proofs/mod.rs diff --git a/mmr/src/data/stores/mod.rs b/mmr/src/stores/mod.rs similarity index 100% rename from mmr/src/data/stores/mod.rs rename to mmr/src/stores/mod.rs diff --git a/mmr/src/stores/store.rs b/mmr/src/stores/store.rs new file mode 100644 index 0000000..0302ec9 --- /dev/null +++ b/mmr/src/stores/store.rs @@ -0,0 +1,65 @@ +/* + Appellation: store + Contrib: FL03 + Description: ... summary ... +*/ +use decanter::prelude::{Hashable, H256}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Hashable, PartialEq, Serialize)] +pub struct MMRStore +where + T: ToString, +{ + id: H256, + data: T, +} + +impl MMRStore +where + T: ToString, +{ + pub fn new(data: T) -> Self { + Self { + id: H256::generate(), + data, + } + } + pub fn data(&self) -> &T { + &self.data + } +} + +impl From for MMRStore +where + T: ToString, +{ + fn from(data: T) -> Self { + Self::new(data) + } +} + +impl std::fmt::Display for MMRStore +where + T: ToString, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let msg = serde_json::json!({ + "id": self.id, + "data": self.data.to_string(), + }); + write!(f, "{}", msg) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_store_default() { + let a = MMRStore::::default(); + let b = MMRStore::::new(Default::default()); + assert_eq!(a, b) + } +} diff --git a/mmr/src/core/utils.rs b/mmr/src/utils.rs similarity index 100% rename from mmr/src/core/utils.rs rename to mmr/src/utils.rs