Skip to content

Commit 6cfba48

Browse files
committed
initial commit: merkle exploration
<!-- ps-id: 682cbe9d-d3af-46de-89ed-ba202457fa74 -->
1 parent 3bf52bf commit 6cfba48

File tree

5 files changed

+177
-14
lines changed

5 files changed

+177
-14
lines changed

Cargo.lock

Lines changed: 49 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/node/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ license.workspace = true
99
repository.workspace = true
1010

1111
[dependencies]
12+
blake2b-rs = "0.2.0"
1213
essential-check = { workspace = true }
1314
essential-hash = { workspace = true }
1415
essential-node-db = { workspace = true }
1516
essential-node-types = { workspace = true }
1617
essential-relayer = { workspace = true }
1718
essential-types = { workspace = true }
1819
futures = { workspace = true }
20+
sparse-merkle-tree = "0.6.1"
1921
rusqlite = { workspace = true }
2022
thiserror = { workspace = true }
2123
tokio = { workspace = true }

crates/node/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use validation::validation_stream;
2020

2121
mod error;
2222
mod handles;
23+
mod merkle;
2324
#[cfg(any(feature = "test-utils", test))]
2425
#[allow(missing_docs)]
2526
pub mod test_utils;

crates/node/src/merkle.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use blake2b_rs::{Blake2b, Blake2bBuilder};
2+
use essential_types::{solution::Mutation, ContentAddress, Key, SolutionSet, Value, Word};
3+
use sparse_merkle_tree::{
4+
blake2b::Blake2bHasher, default_store::DefaultStore, SparseMerkleTree, H256,
5+
};
6+
use std::collections::HashMap;
7+
8+
#[cfg(test)]
9+
mod tests;
10+
11+
// TODO: chose between HashMap & BTreeMap. Is State already defined?
12+
#[derive(Clone, Debug)]
13+
pub struct State(HashMap<ContentAddress, HashMap<Key, Vec<Word>>>);
14+
15+
// define SMT
16+
type SMT = SparseMerkleTree<Blake2bHasher, Word, DefaultStore<Word>>;
17+
18+
impl State {
19+
// Empry state, fine for tests unrelated to reading state.
20+
pub fn empty() -> Self {
21+
State(HashMap::new())
22+
}
23+
}
24+
25+
impl PartialEq for State {
26+
fn eq(&self, other: &Self) -> bool {
27+
self.0 == other.0
28+
}
29+
}
30+
31+
// Expose a function that takes a solution set and a State, and applies it to the state.
32+
pub fn apply_solution_set(set: &SolutionSet, state: &mut State) -> State {
33+
for solution in set.solutions.iter() {
34+
let contract_address = &solution.predicate_to_solve.contract;
35+
36+
// Get or create the inner HashMap for the contract address
37+
let inner_map = state
38+
.0
39+
.entry(contract_address.clone())
40+
.or_insert_with(HashMap::new);
41+
42+
// Borrow `state_mutations` to avoid moving it
43+
for mutation in &solution.state_mutations {
44+
inner_map.insert(mutation.key.clone(), mutation.value.clone());
45+
}
46+
}
47+
state.clone()
48+
}
49+
50+
// Expose a function which takes a State and returns a merkle tree
51+
fn generate_merkle_tree(state: &State) -> SMT {
52+
// TODO: chose hashing algorithm to use.
53+
// TODO: look into how we generate a MerkleTree from a hashmap.
54+
SMT::default()
55+
}

crates/node/src/merkle/tests.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use super::*;
2+
use essential_types::{
3+
solution::Mutation, ContentAddress, PredicateAddress, Solution, SolutionSet, Word,
4+
};
5+
use sparse_merkle_tree::{
6+
blake2b::Blake2bHasher, default_store::DefaultStore, SparseMerkleTree, H256,
7+
};
8+
9+
fn test_mutation(salt: usize, val: Word) -> Mutation {
10+
Mutation {
11+
key: vec![salt as Word; 4],
12+
value: vec![val],
13+
}
14+
}
15+
16+
#[test]
17+
fn test_apply_solution_set() {
18+
let solution = Solution {
19+
predicate_to_solve: PredicateAddress {
20+
contract: ContentAddress([0u8; 32]),
21+
predicate: ContentAddress([0u8; 32]),
22+
},
23+
predicate_data: vec![],
24+
state_mutations: vec![test_mutation(0, 42)],
25+
};
26+
let solution_set = SolutionSet {
27+
solutions: vec![solution.clone()],
28+
};
29+
let mut state = State::empty();
30+
31+
let mut expected_state = state.clone();
32+
let inner_map = expected_state
33+
.0
34+
.entry(solution.predicate_to_solve.contract)
35+
.or_insert(HashMap::new());
36+
37+
inner_map.insert(
38+
solution.state_mutations[0].key.clone(),
39+
solution.state_mutations[0].value.clone(),
40+
);
41+
42+
let updated_state = apply_solution_set(&solution_set, &mut state);
43+
44+
assert_eq!(updated_state, expected_state);
45+
}
46+
47+
#[test]
48+
fn test_generate_merkle_tree() {
49+
// Create a mock state (key-value pairs)
50+
let key1 = H256::from([0u8; 32]);
51+
let value = H256::from([1u8; 32]);
52+
let key2 = H256::from([2u8; 32]);
53+
let value2 = H256::from([3u8; 32]);
54+
let state = State::empty();
55+
56+
let mut smt = SMT::default();
57+
58+
// Insert key-value pairs into the tree
59+
// smt.update(key1, value1).unwrap();
60+
// smt.update(key2, value2).unwrap();
61+
62+
// Generate the expected root hash manually
63+
let expected_root = smt.root();
64+
65+
// Call your function to generate the Merkle tree
66+
let generated_merkle_tree = generate_merkle_tree(&state);
67+
68+
// Assert that the generated Merkle tree's root matches the expected root
69+
assert_eq!(generated_merkle_tree.root(), expected_root);
70+
}

0 commit comments

Comments
 (0)