From 451a04fc77d75c1b3fd5eeaaa764c9740bef3659 Mon Sep 17 00:00:00 2001 From: Matthew Trent Date: Tue, 17 Dec 2024 01:44:38 -0800 Subject: [PATCH] wip: merkle trees --- README.md | 19 +------- lib2/Cargo.toml | 4 +- lib2/src/hash.rs | 59 ++++++++++++++++++++++++ lib2/src/lib.rs | 3 ++ lib2/src/merkle_proof.rs | 16 +++++++ lib2/src/merkle_tree.rs | 19 ++++++++ lib2/src/merkle_tree/merkle_tree.rs | 0 lib2/src/merkle_tree/mod.rs | 2 - lib2/src/merkle_tree/node.rs | 71 ----------------------------- lib2/src/node.rs | 70 ++++++++++++++++++++++++++++ 10 files changed, 170 insertions(+), 93 deletions(-) create mode 100644 lib2/src/hash.rs create mode 100644 lib2/src/merkle_proof.rs create mode 100644 lib2/src/merkle_tree.rs delete mode 100644 lib2/src/merkle_tree/merkle_tree.rs delete mode 100644 lib2/src/merkle_tree/mod.rs delete mode 100644 lib2/src/merkle_tree/node.rs create mode 100644 lib2/src/node.rs diff --git a/README.md b/README.md index af0eba1..7d5d68a 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,6 @@ This is a template for creating an end-to-end [SP1](https://github.com/succinctlabs/sp1) project that can generate a proof of any RISC-V program. -## Short Hands - -PROVE: - -```sh -cd script -cargo run --release -- --prove -``` - -JUST EXEC: - -```sh -cd script -cargo run --release -- --execute -``` - - ## Requirements - [Rust](https://rustup.rs/) @@ -110,4 +93,4 @@ SP1_PROVER=network SP1_PRIVATE_KEY=... cargo run --release --bin evm ## Debugging Note -If you are running this on MacOS M1 aarch64, don't use `zsh` as your shell. Use `bash` instead; `zsh` throws unintelligible errors. \ No newline at end of file +If you are running this on MacOS M1 aarch64, don't use `zsh` as your shell. Use `bash` instead; `zsh` throws unintelligible errors. diff --git a/lib2/Cargo.toml b/lib2/Cargo.toml index 5a186ba..312fa09 100644 --- a/lib2/Cargo.toml +++ b/lib2/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] alloy-sol-types = { workspace = true } -sha2 = "0.10" # For SHA256 hashing -hex = "0.4" # For hex encoding +sha2 = "0.10" +hex = "0.4" diff --git a/lib2/src/hash.rs b/lib2/src/hash.rs new file mode 100644 index 0000000..a9b3261 --- /dev/null +++ b/lib2/src/hash.rs @@ -0,0 +1,59 @@ +use sha2::{Digest, Sha256}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Hash([u8; 32]); + +impl Hash { + pub fn new(data: &[u8]) -> Self { + let mut hasher = Sha256::new(); + hasher.update(data); + Hash(hasher.finalize().into()) + } + + pub fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } + + pub fn as_hex(&self) -> String { + self.0.iter().map(|b| format!("{:02x}", b)).collect() + } + + pub fn combine(left: &Hash, right: &Hash) -> Self { + let mut combined_data = Vec::with_capacity(64); + combined_data.extend_from_slice(left.as_bytes()); + combined_data.extend_from_slice(right.as_bytes()); + Self::new(&combined_data) + } + + pub fn empty() -> Self { + Hash([0u8; 32]) + } + + pub fn from_hex(hex: &str) -> Result> { + if hex.len() != 64 { + // Each byte is represented by 2 hex characters, so 32 bytes = 64 hex chars + return Err("invalid hex string length: expected 64 characters".into()); + } + + let mut hash = [0u8; 32]; + for i in 0..32 { + let byte = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16) + .map_err(|e| format!("failed to parse hex byte at position {}: {}", i, e))?; + hash[i] = byte; + } + + Ok(Hash::new(&hash)) + } +} + +mod test { + #[test] + fn test_hash() { + let data = b"hello world"; + let hash = crate::hash::Hash::new(data); + assert_eq!( + hash.as_hex(), + "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" + ); + } +} diff --git a/lib2/src/lib.rs b/lib2/src/lib.rs index 0e04fc0..222c45c 100644 --- a/lib2/src/lib.rs +++ b/lib2/src/lib.rs @@ -1,2 +1,5 @@ +pub mod hash; +pub mod merkle_proof; pub mod merkle_tree; +pub mod node; pub mod solidity; diff --git a/lib2/src/merkle_proof.rs b/lib2/src/merkle_proof.rs new file mode 100644 index 0000000..a4a31fe --- /dev/null +++ b/lib2/src/merkle_proof.rs @@ -0,0 +1,16 @@ +use crate::{hash::Hash, node::Node}; + +#[derive(Debug)] +pub struct MerkleProof { + proof: Vec, +} + +impl MerkleProof { + pub fn new(proof: Vec) -> Self { + MerkleProof { proof } + } + + pub fn verify(&self, root: Node) -> Result> { + Ok(true) // todo: implement + } +} diff --git a/lib2/src/merkle_tree.rs b/lib2/src/merkle_tree.rs new file mode 100644 index 0000000..d9fe213 --- /dev/null +++ b/lib2/src/merkle_tree.rs @@ -0,0 +1,19 @@ +use crate::node::Node; +use crate::hash::Hash; + +pub struct MerkleTree +where + T: Clone + AsRef<[u8]>, +{ + root: Node, + leaves: Vec>, +} + +impl MerkleTree +where + T: Clone + AsRef<[u8]>, +{ + // pub fn new() +} + + diff --git a/lib2/src/merkle_tree/merkle_tree.rs b/lib2/src/merkle_tree/merkle_tree.rs deleted file mode 100644 index e69de29..0000000 diff --git a/lib2/src/merkle_tree/mod.rs b/lib2/src/merkle_tree/mod.rs deleted file mode 100644 index 4c4e6ca..0000000 --- a/lib2/src/merkle_tree/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod merkle_tree; -pub mod node; diff --git a/lib2/src/merkle_tree/node.rs b/lib2/src/merkle_tree/node.rs deleted file mode 100644 index 909be5d..0000000 --- a/lib2/src/merkle_tree/node.rs +++ /dev/null @@ -1,71 +0,0 @@ -use sha2::{Digest, Sha256}; - -/// Represents a node in a Merkle Tree. -#[derive(Debug, Clone)] -pub struct Node { - hash: [u8; 32], // 32-byte hash - is_leaf: bool, // Is this node a leaf? - data: Option, // Data for leaf nodes -} - -impl Node { - /// Create a new leaf node from the given data. - pub fn new(data: Option) -> Self { - let is_leaf = data.is_some(); - let hash = if let Some(ref value) = data { - Self::compute_hash(value.as_bytes()) - } else { - [0u8; 32] - }; - - Node { - hash, - is_leaf, - data, - } - } - - /// Combine two child nodes to create a new parent node. - pub fn combine(left: &Node, right: &Node) -> Self { - let combined_hash = - Self::compute_hash(&[left.hash.as_slice(), right.hash.as_slice()].concat()); - - Node { - hash: combined_hash, - is_leaf: false, - data: None, - } - } - - /// Hash the contents using SHA256. - fn compute_hash(input: &[u8]) -> [u8; 32] { - let mut hasher = Sha256::new(); - hasher.update(input); - hasher.finalize().into() - } - - pub fn hash(&self) -> [u8; 32] { - self.hash - } - - pub fn is_leaf(&self) -> bool { - self.is_leaf - } - - pub fn data(&self) -> Option { - self.data.clone() - } -} - -mod tests { - #[test] - fn test_node_creation() { - let leaf = super::Node::new(Some("test".to_string())); - assert_eq!(leaf.is_leaf, true); - assert_eq!(leaf.data, Some("test".to_string())); - - let parent = super::Node::combine(&leaf, &leaf); - assert_eq!(parent.is_leaf, false); - assert_eq!(parent.data, None); - } -} diff --git a/lib2/src/node.rs b/lib2/src/node.rs new file mode 100644 index 0000000..c2c18b0 --- /dev/null +++ b/lib2/src/node.rs @@ -0,0 +1,70 @@ +use crate::hash::Hash; + +/// Represents a node in a Merkle Tree. +#[derive(Debug, Clone)] +pub struct Node +where + T: AsRef<[u8]> + Clone, +{ + hash: Hash, // Hash of the node + is_leaf: bool, // Is this node a leaf? + data: Option, // Data for leaf nodes +} + +impl Node +where + T: AsRef<[u8]> + Clone, +{ + /// Create a new leaf node from the given data. + pub fn new(data: Option) -> Self { + let is_leaf = data.is_some(); + let hash = if let Some(ref value) = data { + Hash::new(value.as_ref()) + } else { + Hash::empty() + }; + + Node { + hash, + is_leaf, + data, + } + } + + /// Combine two child nodes to create a new parent node. + pub fn new_from_children(left: &Node, right: &Node) -> Self { + let combined_hash = Hash::combine(&left.hash, &right.hash); + + Node { + hash: combined_hash, + is_leaf: false, + data: None, + } + } + + /// Retrieve the hash of the node. + pub fn hash(&self) -> &Hash { + &self.hash + } + + /// Check if the node is a leaf. + pub fn is_leaf(&self) -> bool { + self.is_leaf + } + + /// Retrieve the data (if any). + pub fn data(&self) -> Option { + self.data.clone() + } +} + + +mod tests { + #[test] + fn test_node() { + let data = b"hello world"; + let node = crate::node::Node::new(Some(data)); + assert_eq!(node.is_leaf(), true); + assert_eq!(node.data(), Some(data)); + } +}