diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 360a92c..9a0bec2 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -2,28 +2,25 @@ name: Clippy on: pull_request: - branches-ignore: [ "dev*" ] - tags: [ "v*.*.*" ] + branches: [ main, master, v*.*.*, ] push: - branches-ignore: [ "dev*" ] - tags: [ "v*.*.*" ] + branches-ignore: [ "beta*", "dev*", "next*" ] + tags: [ "nightly*", "v*.*.*" ] release: schedule: - - cron: "30 9 * * *" + - cron: "30 9 * * *" # 9:30am UTC workflow_dispatch: jobs: - rust-clippy-analyze: - name: Run rust-clippy analyzing - runs-on: ubuntu-latest + clippy: + name: Clippy permissions: + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status contents: read security-events: write - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v3 - + - uses: actions/checkout@v3 - name: Install Rust toolchain uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 with: @@ -31,17 +28,14 @@ jobs: toolchain: stable components: clippy override: true - - - name: Install required cargo + - name: Setup run: cargo install clippy-sarif sarif-fmt - - - name: Run rust-clippy + - name: clippy run: cargo clippy --all-features --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt continue-on-error: true - - name: Upload analysis results to GitHub uses: github/codeql-action/upload-sarif@v2 with: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 81a24db..0c3120b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -5,13 +5,14 @@ env: on: pull_request: - branches-ignore: [ "dev*" ] - tags: [ "v*.*.*" ] + branches: [ main, master, v*.*.*, ] push: - branches-ignore: [ "dev*" ] - tags: [ "v*.*.*" ] + branches-ignore: [ "beta*", "dev*", "next*" ] + tags: [ "nightly*", "v*.*.*" ] + release: + types: [created] schedule: - - cron: "30 9 * * *" + - cron: "30 9 * * *" # 9:30am UTC workflow_dispatch: inputs: publish: @@ -23,39 +24,52 @@ on: jobs: build: name: Build and Test - runs-on: ubuntu-latest strategy: matrix: - toolchain: - - stable - - nightly + platform: [ ubuntu-latest ] + toolchain: [ stable, nightly ] + runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v3 - - name: setup - run: rustup default ${{ matrix.toolchain }} - - run: cargo build --release -v --workspace - - run: cargo test --all --all-features --release -v + - name: setup (langspace) + run: | + rustup update + rustup default ${{ matrix.toolchain }} + - name: Build + run: cargo build --release -v --workspace + - name: Cache build + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target/release + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Test + run: cargo test --all --release -v + - name: Bench + if: matrix.toolchain == 'nightly' + run: cargo bench --all -v features: - if: ${{ github.event.inputs.publish }} - name: Features + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.event_name == 'release' && github.event.action == 'created' || github.event.inputs.publish == 'true' + name: Publish (features) needs: build runs-on: ubuntu-latest strategy: matrix: - package: - - algae-graph - - algae-merkle - - algae-mmr + feature: [ "graph", "merkle", "mmr" ] + env: + PACKAGE_NAME: ${{ github.event.repository.name }}-${{ matrix.feature }} steps: - uses: actions/checkout@v3 - - name: Publish (${{matrix.package}}) - run: cargo publish --all-features -v -p ${{ matrix.package }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + - name: Publish (${{ env.PACKAGE_NAME }}) + run: cargo publish --all-features -v -p ${{ env.PACKAGE_NAME }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} publish: - if: ${{ github.event.inputs.publish }} - name: Publish + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && github.event_name == 'release' && github.event.action == 'created' || github.event.inputs.publish == 'true' + name: Publish (sdk) needs: features runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Publish (algae) - run: cargo publish --all-features -v -p algae --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + - name: Publish (${{ github.event.repository.name }}) + run: cargo publish --all-features -v -p ${{ github.event.repository.name }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 51771f1..d55e01b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/scattered-systems/algae" version = "0.1.19" [workspace.dependencies] -decanter = { features = ["derive", "wasm"], git = "https://github.com/FL03/decanter", branch = "v0.1.5", version = "0.1.5" } +decanter = { features = ["derive", "wasm"], version = "0.1.5" } anyhow = "1" itertools = "0.10" diff --git a/README.md b/README.md index 58fd565..1d0f81a 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![crates.io](https://img.shields.io/crates/v/algae.svg)](https://crates.io/crates/algae) [![docs.rs](https://docs.rs/algae/badge.svg)](https://docs.rs/algae) -[![Clippy](https://github.com/FL03/algae/actions/workflows/rust-clippy.yml/badge.svg)](https://github.com/FL03/algae/actions/workflows/rust-clippy.yml) -[![Rust](https://github.comFL03/algae/actions/workflows/rust.yml/badge.svg)](https://github.com/FL03/algae/actions/workflows/rust.yml) +[![Clippy](https://github.com/FL03/algae/actions/workflows/clippy.yml/badge.svg)](https://github.com/FL03/algae/actions/workflows/clippy.yml) +[![Rust](https://github.com/FL03/algae/actions/workflows/rust.yml/badge.svg)](https://github.com/FL03/algae/actions/workflows/rust.yml) *** diff --git a/algae/Cargo.toml b/algae/Cargo.toml index be2b898..a60f035 100644 --- a/algae/Cargo.toml +++ b/algae/Cargo.toml @@ -1,10 +1,10 @@ [package] authors.workspace = true -categories = [] +categories.workspace = true description.workspace = true edition.workspace = true homepage.workspace = true -keywords = ["algorithms", "data-structures"] +keywords.workspace = true license.workspace = true name = "algae" readme.workspace = true @@ -12,14 +12,15 @@ repository.workspace = true version.workspace = true [features] -default = ["core"] - -core = [ +default = ["core", "graph", "merkle", "mmr"] +full = [ + "core", + "graph", "merkle", "mmr", - "graph" ] +core = [] graph = ["algae-graph"] merkle = ["algae-merkle"] mmr = ["algae-mmr"] diff --git a/algae/benches/default.rs b/algae/benches/default.rs new file mode 100644 index 0000000..937f238 --- /dev/null +++ b/algae/benches/default.rs @@ -0,0 +1,52 @@ +// bench.rs +#![feature(test)] + +extern crate test; + +use std::mem::replace; +use test::Bencher; + +// bench: find the `BENCH_SIZE` first terms of the fibonacci sequence +static BENCH_SIZE: usize = 20; + +// recursive fibonacci +fn fibonacci(n: usize) -> u32 { + if n < 2 { + 1 + } else { + fibonacci(n - 1) + fibonacci(n - 2) + } +} + +// iterative fibonacci +struct Fibonacci { + curr: u32, + next: u32, +} + +impl Iterator for Fibonacci { + type Item = u32; + fn next(&mut self) -> Option { + let new_next = self.curr + self.next; + let new_curr = replace(&mut self.next, new_next); + + Some(replace(&mut self.curr, new_curr)) + } +} + +fn fibonacci_sequence() -> Fibonacci { + Fibonacci { curr: 1, next: 1 } +} + +// function to benchmark must be annotated with `#[bench]` +#[bench] +fn recursive_fibonacci(b: &mut Bencher) { + // exact code to benchmark must be passed as a closure to the iter + // method of Bencher + b.iter(|| (0..BENCH_SIZE).map(fibonacci).collect::>()) +} + +#[bench] +fn iterative_fibonacci(b: &mut Bencher) { + b.iter(|| fibonacci_sequence().take(BENCH_SIZE).collect::>()) +} diff --git a/algae/benches/graphs.rs b/algae/benches/graphs.rs new file mode 100644 index 0000000..8e4d12c --- /dev/null +++ b/algae/benches/graphs.rs @@ -0,0 +1,25 @@ +// bench.rs +#![feature(test)] + +extern crate test; +use algae::graph::{DirectedGraph, Edge, Graph,}; +use test::Bencher; + +const TEST_EDGES: [(&str, &str, usize); 5] = [ + ("a", "b", 5), + ("c", "a", 7), + ("b", "c", 10), + ("d", "c", 10), + ("e", "f", 10), +]; + +#[bench] +fn bench_directed(b: &mut Bencher) { + let mut graph = DirectedGraph::<&str, usize>::new(); + b.iter(|| { + TEST_EDGES + .into_iter() + .map(|i| Edge::from(i)) + .for_each(|i| graph.add_edge(i)); + }); +} diff --git a/graph/src/cmp/edge.rs b/graph/src/cmp/edge.rs index 0c152e9..ac797f5 100644 --- a/graph/src/cmp/edge.rs +++ b/graph/src/cmp/edge.rs @@ -4,7 +4,7 @@ Description: an edge consists of two nodes and an optional edge value */ use super::Pair; -use crate::{Node, Weight}; +use crate::Node; use serde::{Deserialize, Serialize}; pub trait Related {} @@ -13,7 +13,6 @@ pub trait Related {} pub struct Edge where N: Node, - V: Weight, { pair: Pair, weight: V, @@ -22,10 +21,12 @@ where impl Edge where N: Node, - V: Weight, { - pub fn new(pair: Pair, weight: V) -> Self { - Self { pair, weight } + pub fn new(a: N, b: N, weight: V) -> Self { + Self { + pair: Pair::new(a, b), + weight, + } } pub fn pair(&self) -> Pair { self.pair.clone() @@ -38,20 +39,15 @@ where impl From<(N, N, V)> for Edge where N: Node, - V: Weight, { fn from(data: (N, N, V)) -> Self { - Self { - pair: Pair::new(data.0, data.1), - weight: data.2, - } + Self::new(data.0, data.1, data.2) } } impl From<(Pair, V)> for Edge where N: Node, - V: Weight, { fn from(data: (Pair, V)) -> Self { Self { diff --git a/graph/src/directed.rs b/graph/src/directed.rs index 40973cf..1f21105 100644 --- a/graph/src/directed.rs +++ b/graph/src/directed.rs @@ -3,8 +3,8 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{cmp::Edge, store::AdjacencyTable}; -use crate::{Contain, Graph, GraphExt, Node, Subgraph, Weight}; +use crate::{store::AdjacencyTable, Edge, Node, Weight}; +use crate::{Contain, Graph, GraphExt, Subgraph}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] @@ -16,6 +16,23 @@ where store: AdjacencyTable, } +impl DirectedGraph +where + N: Node, + V: Weight, +{ + pub fn new() -> Self { + Self { + store: AdjacencyTable::new(), + } + } + pub fn with_capacity(capacity: usize) -> Self { + Self { + store: AdjacencyTable::with_capacity(capacity), + } + } +} + impl AsMut> for DirectedGraph where N: Node, @@ -74,16 +91,6 @@ where N: Node, V: Weight, { - fn new() -> Self { - Self { - store: AdjacencyTable::new(), - } - } - fn with_capacity(capacity: usize) -> Self { - Self { - store: AdjacencyTable::with_capacity(capacity), - } - } } impl Subgraph for DirectedGraph diff --git a/graph/src/graph/mod.rs b/graph/src/graph/mod.rs new file mode 100644 index 0000000..05d9b74 --- /dev/null +++ b/graph/src/graph/mod.rs @@ -0,0 +1,39 @@ +/* + Appellation: graph + Contrib: FL03 + Description: This module implements an abstract graph data structure +*/ +use crate::store::Entry; +use serde::{Deserialize, Serialize}; +use strum::{Display, EnumString, EnumVariantNames}; + +pub trait GraphStore: Default { + fn capacity(&self) -> usize; + fn clear(&mut self); + fn dim(&self) -> (usize, usize); + fn get_entries(&self, key: &N) -> Option<&Entry>; +} + +#[derive( + Clone, + Copy, + Debug, + Default, + Deserialize, + Display, + EnumString, + EnumVariantNames, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Serialize, +)] +#[repr(u8)] +#[strum(serialize_all = "snake_case")] +pub enum GraphType { + Directed, + #[default] + Undirected, +} diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 951e2f3..d90955f 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -8,22 +8,17 @@ pub use self::{cmp::*, directed::*, errors::*, specs::*, undirected::*}; pub(crate) mod cmp; pub(crate) mod directed; mod errors; -pub(crate) mod specs; +mod specs; pub(crate) mod undirected; +pub mod graph; pub mod search; pub mod store; -use cmp::Edge; use errors::GraphError; 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 + Contain + Contain> + IndexMut> @@ -96,7 +91,8 @@ where match self.store().get(node) { Some(edges) => Ok(edges .iter() - .map(|(n, v)| Edge::from((node.clone(), n.clone(), v.clone()))) + .cloned() + .map(|(n, v)| Edge::new(node.clone(), n, v)) .collect()), None => Err(GraphError::NodeNotInGraph), } @@ -104,10 +100,10 @@ where /// [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()); + for (origin, neighbours) in self.store().clone() { + for (dest, weight) in neighbours { + if &dest == node { + edges.push((origin.clone(), dest, weight).into()); } } } @@ -116,7 +112,7 @@ where /// [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()]; + let mut stack = self.nodes().iter().cloned().collect::>(); while let Some(node) = stack.pop() { if !visited.contains(&node) { @@ -125,8 +121,8 @@ where self.neighbors(node) .unwrap() .iter() - .cloned() - .map(|(n, _)| n), + .map(|(n, _)| n) + .cloned(), ); } } @@ -156,9 +152,6 @@ 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 diff --git a/graph/src/search/bfs.rs b/graph/src/search/bfs.rs index de9e81f..717ad1e 100644 --- a/graph/src/search/bfs.rs +++ b/graph/src/search/bfs.rs @@ -59,7 +59,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{DirectedGraph, Edge, GraphExt}; + use crate::{DirectedGraph, Edge}; const TEST_EDGES: [(&str, &str, usize); 5] = [ ("a", "b", 5), diff --git a/graph/src/search/dfs.rs b/graph/src/search/dfs.rs index df6440f..975551a 100644 --- a/graph/src/search/dfs.rs +++ b/graph/src/search/dfs.rs @@ -56,7 +56,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{DirectedGraph, Edge, GraphExt}; + use crate::{DirectedGraph, Edge}; const TEST_EDGES: [(&str, &str, usize); 5] = [ ("a", "b", 5), diff --git a/graph/src/specs.rs b/graph/src/specs.rs index 6fcb5b4..35c8a85 100644 --- a/graph/src/specs.rs +++ b/graph/src/specs.rs @@ -4,7 +4,10 @@ Description: ... Summary ... */ -pub trait Contain { +pub trait Contain +where + T: PartialEq, +{ /// [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 @@ -20,64 +23,8 @@ pub trait Contain { /// [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 {} +impl Node for T where T: Clone + Default + Eq + std::hash::Hash {} 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 {} +impl Weight for T where T: Clone + PartialEq {} diff --git a/graph/src/store/mod.rs b/graph/src/store/mod.rs index b4ea3ef..4c9173f 100644 --- a/graph/src/store/mod.rs +++ b/graph/src/store/mod.rs @@ -5,27 +5,20 @@ */ pub use self::{matrix::*, table::*}; -pub(crate) mod matrix; -pub(crate) mod table; +mod matrix; +mod table; -use crate::{cmp::Edge, Contain, Node, Weight}; +use crate::{Contain, Edge, Node}; use serde::{Deserialize, Serialize}; use std::ops::IndexMut; -pub struct Entry -where - N: Node, - V: Weight, -{ +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Entry { key: N, value: Vec<(N, V)>, } -impl Entry -where - N: Node, - V: Weight, -{ +impl Entry { pub fn new(key: N, value: Vec<(N, V)>) -> Self { Self { key, value } } @@ -42,8 +35,8 @@ where impl Contain<(N, V)> for Entry where - N: Node, - V: Weight, + N: PartialEq, + V: PartialEq, { fn contains(&self, elem: &(N, V)) -> bool { self.value.contains(elem) @@ -53,7 +46,6 @@ where pub trait Store: Extend> + IndexMut> where N: Node, - V: Weight, { fn clear(&mut self); fn contains_key(&self, key: &N) -> bool; @@ -67,13 +59,3 @@ where } } } - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] -pub enum Stores -where - N: Node, - V: Weight, -{ - Matrix(AdjacencyMatrix), - Table(AdjacencyTable), -} diff --git a/graph/src/store/table.rs b/graph/src/store/table.rs index f28b887..627ad08 100644 --- a/graph/src/store/table.rs +++ b/graph/src/store/table.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: an adjacency table */ -use crate::Node; +use crate::{Node, Weight}; use serde::{Deserialize, Serialize}; use std::collections::{hash_map, HashMap}; use std::iter::Extend; @@ -12,11 +12,16 @@ use std::iter::Extend; pub struct AdjacencyTable where N: Node, + V: Weight, { store: HashMap>, } -impl AdjacencyTable { +impl AdjacencyTable +where + N: Node, + V: Weight, +{ pub fn new() -> Self { Self { store: HashMap::new(), @@ -71,19 +76,31 @@ impl AdjacencyTable { } } -impl Extend<(N, Vec<(N, V)>)> for AdjacencyTable { +impl Extend<(N, Vec<(N, V)>)> for AdjacencyTable +where + N: Node, + V: Weight, +{ fn extend)>>(&mut self, iter: T) { self.store.extend(iter) } } -impl From>> for AdjacencyTable { +impl From>> for AdjacencyTable +where + N: Node, + V: Weight, +{ fn from(store: HashMap>) -> Self { Self { store } } } -impl FromIterator<(N, Vec<(N, V)>)> for AdjacencyTable { +impl FromIterator<(N, Vec<(N, V)>)> for AdjacencyTable +where + N: Node, + V: Weight, +{ fn from_iter)>>(iter: T) -> Self { let mut map = HashMap::with_hasher(Default::default()); map.extend(iter); @@ -91,7 +108,11 @@ impl FromIterator<(N, Vec<(N, V)>)> for AdjacencyTable { } } -impl IntoIterator for AdjacencyTable { +impl IntoIterator for AdjacencyTable +where + N: Node, + V: Weight, +{ type Item = (N, Vec<(N, V)>); type IntoIter = hash_map::IntoIter>; @@ -101,7 +122,11 @@ impl IntoIterator for AdjacencyTable { } } -impl std::ops::Index for AdjacencyTable { +impl std::ops::Index for AdjacencyTable +where + N: Node, + V: Weight, +{ type Output = Vec<(N, V)>; fn index(&self, index: N) -> &Self::Output { @@ -109,7 +134,11 @@ impl std::ops::Index for AdjacencyTable { } } -impl std::ops::IndexMut for AdjacencyTable { +impl std::ops::IndexMut for AdjacencyTable +where + N: Node, + V: Weight, +{ 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 4159f20..5058d49 100644 --- a/graph/src/undirected.rs +++ b/graph/src/undirected.rs @@ -17,6 +17,23 @@ where store: AdjacencyTable, } +impl UndirectedGraph +where + N: Node, + V: Weight, +{ + pub fn new() -> Self { + Self { + store: AdjacencyTable::new(), + } + } + pub fn with_capacity(capacity: usize) -> Self { + Self { + store: AdjacencyTable::with_capacity(capacity), + } + } +} + impl AsMut> for UndirectedGraph where N: Node, @@ -87,16 +104,6 @@ where N: Node, V: Weight, { - fn new() -> Self { - Self { - store: AdjacencyTable::new(), - } - } - fn with_capacity(capacity: usize) -> Self { - Self { - store: AdjacencyTable::with_capacity(capacity), - } - } } impl Subgraph for UndirectedGraph diff --git a/merkle/Cargo.toml b/merkle/Cargo.toml index 0e5c0e2..9bd2259 100644 --- a/merkle/Cargo.toml +++ b/merkle/Cargo.toml @@ -26,6 +26,7 @@ test = true [dependencies] anyhow.workspace = true +blake3 ={ features = [], version = "1.3" } decanter.workspace = true hex = "0.4" itertools.workspace = true diff --git a/merkle/src/lib.rs b/merkle/src/lib.rs index 91d8661..11a21fd 100644 --- a/merkle/src/lib.rs +++ b/merkle/src/lib.rs @@ -16,28 +16,3 @@ 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 ab5c7a0..dc8c23b 100644 --- a/merkle/src/nodes.rs +++ b/merkle/src/nodes.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{combine, merkle_hash, Payload}; +use crate::{combine_hash_str, merkle_hash, Payload}; use decanter::prelude::{Hashable, H256}; use serde::{Deserialize, Serialize}; use std::string::ToString; @@ -40,7 +40,7 @@ where T: Default + ToString, { fn from(data: (Node, Node)) -> Self { - let hash = merkle_hash(combine(&data.0.hash, &data.1.hash)); + let hash = merkle_hash(combine_hash_str(&data.0.hash, &data.1.hash)); let data = Payload::Node(Box::new(data.0), Box::new(data.1)); Self::new(data, hash) } diff --git a/merkle/src/proofs/proof.rs b/merkle/src/proofs/proof.rs index d175472..d1d48f8 100644 --- a/merkle/src/proofs/proof.rs +++ b/merkle/src/proofs/proof.rs @@ -4,7 +4,7 @@ Description: ... Summary ... */ use super::path::proof_path; -use crate::{MerkleDimension, MerkleTree, MerkleTreeSpec}; +use crate::{MerkleDimension, MerkleTree}; use decanter::prelude::H256; pub struct Prover { diff --git a/merkle/src/tree.rs b/merkle/src/tree.rs index ced672f..941a3c4 100644 --- a/merkle/src/tree.rs +++ b/merkle/src/tree.rs @@ -3,7 +3,8 @@ Contrib: FL03 Description: ... Summary ... */ -use crate::{create_merkle_tree, MerkleDimension, MerkleShape, MerkleTreeSpec}; +use crate::proofs::merkle_proof; +use crate::{create_merkle_tree, MerkleDimension, MerkleShape}; use decanter::prelude::{Hashable, H256}; use serde::{Deserialize, Serialize}; @@ -14,17 +15,29 @@ pub struct MerkleTree { } impl MerkleTree { - pub fn new(dim: MerkleDimension, nodes: Vec) -> Self { + pub fn new(dim: MerkleDimension) -> Self { + Self { + dim, + nodes: Vec::new(), + } + } + pub fn new_with_nodes(dim: MerkleDimension, nodes: Vec) -> Self { Self { dim, nodes } } -} - -impl MerkleTreeSpec for MerkleTree { - fn dim(&self) -> MerkleDimension { - self.dim.clone() + pub fn dim(&self) -> MerkleDimension { + self.dim + } + pub fn proof(&self, index: usize) -> Vec { + merkle_proof(self.dim(), self.nodes().clone(), index) + } + pub fn root(&self) -> H256 { + self.nodes()[self.dim().size() - 1] } - fn nodes(&self) -> Vec { - self.nodes.clone() + pub fn nodes(&self) -> &Vec { + &self.nodes + } + pub fn nodes_mut(&mut self) -> &mut Vec { + &mut self.nodes } } @@ -37,18 +50,18 @@ impl std::fmt::Display 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) + Self { + dim: MerkleDimension::from(dim), + nodes, + } } } impl From<(Box, Vec)> for MerkleTree { fn from(data: (Box, Vec)) -> Self { - Self::new(MerkleDimension::from(data.0), data.1) - } -} - -impl From> for MerkleTree { - fn from(data: Vec) -> Self { - Self::from(create_merkle_tree(&data)) + Self { + dim: MerkleDimension::from(data.0), + nodes: data.1, + } } } diff --git a/merkle/src/utils.rs b/merkle/src/utils.rs index e00de39..b35fdbf 100644 --- a/merkle/src/utils.rs +++ b/merkle/src/utils.rs @@ -6,17 +6,15 @@ use crate::proofs::proof_path; use crate::{MerkleDimension, MerkleShape}; use decanter::prelude::{hasher, Hashable, H256}; -use serde::Serialize; -/// +/// Combines two hashes into a single hash 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); hasher(combined).into() } - /// Merges two hashes into a string -pub fn combine(a: &T, b: &T) -> String { +pub fn combine_hash_str(a: &T, b: &T) -> String { format!("{}{}", a.to_string(), b.to_string()) } /// Creates a Merkle tree from a slice of data diff --git a/merkle/tests/merkle.rs b/merkle/tests/merkle.rs index 7b251a6..f60daf0 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, MerkleTreeSpec}; +use algae_merkle::{is_merkle_valid, MerkleTree}; use decanter::prelude::{Hashable, H256}; use hex_literal::hex; @@ -42,7 +42,7 @@ fn test_merkle_root() { let data: Vec = gen_merkle_tree_data!(); let expected = (hex!("6b787718210e0b3b608814e04e61fde06d0df794319a12162f287412df3ec920")).into(); - let a = MerkleTree::from(data); + let a = MerkleTree::from(data.as_slice()); assert_ne!(&a.root(), &expected); } @@ -53,8 +53,8 @@ fn test_merkle_root() { fn test_merkle_proof() { let expected = vec![hex!("965b093a75a75895a351786dd7a188515173f6928a8af8c9baa4dcff268a4f0f").into()]; - - let a = MerkleTree::from(gen_merkle_tree_data!()); + let data: Vec = gen_merkle_tree_data!(); + let a = MerkleTree::from(data.as_slice()); assert_ne!(a.proof(0), expected) } @@ -65,7 +65,7 @@ fn test_merkle_proof() { #[test] fn test_is_merkle_valid() { let data: Vec = gen_merkle_tree_data2!(); - let merkle_tree = MerkleTree::from(data.clone()); + let merkle_tree = MerkleTree::from(data.as_slice()); let index = 3; let proof = merkle_tree.proof(index); log::info!("{:?}", proof); @@ -81,7 +81,7 @@ fn test_is_merkle_valid() { #[test] fn test_vrf_tree() { let data: Vec = gen_merkle_tree_data!(); - let merkle_tree = MerkleTree::from(data.clone()); + let merkle_tree = MerkleTree::from(data.as_slice()); let proof = merkle_tree.proof(0); assert!(is_merkle_valid( &merkle_tree.root(), diff --git a/mmr/src/builder.rs b/mmr/src/builder.rs index c5da5c5..6b878df 100644 --- a/mmr/src/builder.rs +++ b/mmr/src/builder.rs @@ -4,7 +4,6 @@ Description: ... summary ... */ use crate::MerkleMountainRange; -use decanter::prelude::Hashable; pub struct MerkleMountainRangeBuilder where diff --git a/mmr/src/cmp/nodes.rs b/mmr/src/cmp/nodes.rs index b7e988d..ce531fc 100644 --- a/mmr/src/cmp/nodes.rs +++ b/mmr/src/cmp/nodes.rs @@ -4,23 +4,41 @@ Description: ... summary ... */ -use decanter::prelude::H256; +use decanter::prelude::{Hashable, H256}; use serde::{Deserialize, Serialize}; use std::convert::From; -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub struct MerkleNode { +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Node { pub hash: H256, pub pruned: bool, } -impl MerkleNode { - pub fn new(hash: H256, pruned: bool) -> MerkleNode { - MerkleNode { hash, pruned } +impl Node { + pub fn new(hash: H256, pruned: bool) -> Node { + Node { hash, pruned } + } + pub fn prune(&mut self) { + self.pruned = true; + } + pub fn is_pruned(&self) -> bool { + self.pruned + } +} + +impl Hashable for Node { + fn hash(&self) -> H256 { + self.hash + } +} + +impl std::fmt::Display for Node { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.hash) } } -impl From for MerkleNode { +impl From for Node { fn from(data: H256) -> Self { Self::new(data, false) } diff --git a/mmr/src/cmp/payloads.rs b/mmr/src/cmp/payloads.rs index 5870b0c..10b0bf0 100644 --- a/mmr/src/cmp/payloads.rs +++ b/mmr/src/cmp/payloads.rs @@ -29,6 +29,16 @@ where pub fn data(&self) -> &HashMap { &self.data } + pub fn set_id(mut self, id: H256) -> Self { + self.id = id; + self + } + pub fn with_capacity(capacity: usize) -> Self { + Self { + id: H256::generate(), + data: HashMap::with_capacity(capacity), + } + } } impl From> for Payload @@ -48,6 +58,65 @@ where T: ToString, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.id) + let data = self + .data + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect::>(); + write!(f, "{}", serde_json::to_value(data).unwrap()) + } +} + +impl Extend for Payload +where + T: ToString, +{ + fn extend>(&mut self, iter: I) { + for v in iter { + self.data.insert(H256::generate(), v); + } + } +} + +impl Extend<(H256, T)> for Payload +where + T: ToString, +{ + fn extend>(&mut self, iter: I) { + for (k, v) in iter { + self.data.insert(k, v); + } + } +} + +impl std::ops::Index for Payload +where + T: ToString, +{ + type Output = T; + + fn index(&self, index: H256) -> &Self::Output { + &self.data[&index] + } +} + +impl std::ops::IndexMut for Payload +where + T: ToString, +{ + fn index_mut(&mut self, index: H256) -> &mut Self::Output { + self.data.get_mut(&index).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_payload() { + let mut payload = Payload::new(); + payload.extend(vec!["a", "b"]); + assert_eq!(payload.data().len(), 2); } } diff --git a/mmr/src/mmr.rs b/mmr/src/mmr.rs index 2fc27a2..b39fa6d 100644 --- a/mmr/src/mmr.rs +++ b/mmr/src/mmr.rs @@ -3,7 +3,7 @@ Contrib: FL03 Description: ... summary ... */ -use crate::cmp::{MerkleNode, Position}; +use crate::cmp::{Node, Position}; use crate::{is_node_right, sibling_index, RangeMap}; use decanter::prelude::{hasher, Hashable, H256}; use digest::Digest; @@ -16,7 +16,7 @@ where T: ToString, { data: RangeMap, - mmr: Vec, // todo convert these to a bitmap + mmr: Vec, // todo convert these to a bitmap position: Position, } @@ -24,7 +24,7 @@ impl MerkleMountainRange where T: ToString, { - pub fn new(mmr: Vec, data: RangeMap, position: Position) -> Self { + pub fn new(mmr: Vec, data: RangeMap, position: Position) -> Self { Self { mmr, data, @@ -40,7 +40,7 @@ where /// 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); + let node = Node::from(node_hash); self.data.insert(node_hash, object); self.mmr.push(node); if is_node_right(self.get_last_added_index()) { @@ -54,7 +54,7 @@ where 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); + let new_node = Node::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()) diff --git a/mmr/src/stores/mod.rs b/mmr/src/stores/mod.rs index e098b64..e510b9f 100644 --- a/mmr/src/stores/mod.rs +++ b/mmr/src/stores/mod.rs @@ -5,4 +5,6 @@ */ pub use self::store::*; -pub(crate) mod store; +mod store; + +pub trait Store {} diff --git a/mmr/src/stores/store.rs b/mmr/src/stores/store.rs index 0302ec9..abdfed2 100644 --- a/mmr/src/stores/store.rs +++ b/mmr/src/stores/store.rs @@ -57,9 +57,9 @@ mod tests { use super::*; #[test] - fn test_store_default() { + fn test_store() { let a = MMRStore::::default(); let b = MMRStore::::new(Default::default()); - assert_eq!(a, b) + assert_eq!(a.data(), b.data()) } }