-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a rust tool to create mixhash and calc proof by block hash
- Loading branch information
Showing
4 changed files
with
365 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "rust" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
clap = { version = "4.5", features = ["derive"] } | ||
sha2 = "0.10" | ||
sha3 = "0.10" | ||
generic-array = "0.14" | ||
serde_json = "1" | ||
hex = "0.4" | ||
rand = { version = "0.8", features = ["min_const_gen"] } | ||
serde = { version = "1.0.208", features = ["derive"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
use clap::ValueEnum; | ||
use generic_array::GenericArray; | ||
use generic_array::typenum::{U16, U32}; | ||
use sha2::{Sha256, Digest}; | ||
use sha3::Keccak256; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Serialize, Deserialize)] | ||
pub enum HashType { | ||
Sha256, | ||
Keccak256, | ||
} | ||
|
||
pub type Hash = GenericArray<u8, U32>; | ||
type HalfHash = GenericArray<u8, U16>; | ||
|
||
pub fn hex(hash: &[u8]) -> String { | ||
format!("0x{}", hex::encode(hash)) | ||
} | ||
|
||
fn decode_hash(hex: &str) -> Hash { | ||
Hash::from_slice(hex::decode(&hex.as_bytes()[2..]).unwrap().as_slice()).clone() | ||
} | ||
|
||
fn decode_half_hash(hex: &str) -> HalfHash { | ||
HalfHash::from_slice(hex::decode(&hex.as_bytes()[2..]).unwrap().as_slice()).clone() | ||
} | ||
|
||
pub fn calc_hash(hash_type: &HashType, data: &[u8]) -> Hash { | ||
match hash_type { | ||
HashType::Sha256 => { | ||
let mut sha256 = Sha256::new(); | ||
sha256.update(data); | ||
sha256.finalize() | ||
} | ||
HashType::Keccak256 => { | ||
let mut sha3 = Keccak256::new(); | ||
sha3.update(data); | ||
sha3.finalize() | ||
} | ||
} | ||
} | ||
|
||
#[derive(Serialize, Deserialize)] | ||
pub struct MerkleTreeData { | ||
hash_type: HashType, | ||
tree: Vec<Vec<String>>, | ||
root: String | ||
} | ||
|
||
pub struct MerkleTree { | ||
leaf_hash: Vec<HalfHash>, | ||
tree: Vec<Vec<HalfHash>>, | ||
root: Hash, | ||
hash_type: HashType, | ||
} | ||
|
||
impl MerkleTree { | ||
pub fn new(hash_type: HashType) -> Self { | ||
MerkleTree { | ||
leaf_hash: vec![], | ||
tree: vec![], | ||
root: Default::default(), | ||
hash_type, | ||
} | ||
} | ||
|
||
pub fn load(data: MerkleTreeData) -> Self { | ||
Self { | ||
leaf_hash: vec![], | ||
tree: data.tree.iter().map(|v|v.iter().map(|s|decode_half_hash(s)).collect()).collect(), | ||
root: decode_hash(&data.root), | ||
hash_type: data.hash_type, | ||
} | ||
} | ||
|
||
pub fn save(&self) -> MerkleTreeData { | ||
MerkleTreeData { | ||
hash_type: self.hash_type.clone(), | ||
tree: self.tree.iter().map(|v|v.iter().map(|h|hex(h.as_slice())).collect()).collect(), | ||
root: hex(self.root.as_slice()), | ||
} | ||
} | ||
|
||
pub fn add_leaf(&mut self, leaf: &[u8]) { | ||
let hash32 = calc_hash(&self.hash_type, leaf); | ||
self.leaf_hash.push(HalfHash::from_slice(&hash32.as_slice()[16..]).clone()); | ||
} | ||
|
||
pub fn calc_tree(&mut self) { | ||
let mut cur_layer = self.leaf_hash.clone(); | ||
let mut next_layer= Vec::new(); | ||
self.tree.push(cur_layer.clone()); | ||
let mut hash = Hash::default(); | ||
while cur_layer.len() > 1 { | ||
for chunk in cur_layer.chunks(2) { | ||
if chunk.len() == 1 { | ||
next_layer.push(chunk[0].clone()); | ||
} else { | ||
hash = calc_hash(&self.hash_type, &chunk.concat()); | ||
next_layer.push(HalfHash::from_slice(&hash.as_slice()[16..]).clone()) | ||
} | ||
} | ||
self.tree.push(next_layer.clone()); | ||
cur_layer = next_layer.clone(); | ||
next_layer.clear(); | ||
} | ||
|
||
self.root = hash; | ||
} | ||
|
||
pub fn get_root(&self) -> &Hash { | ||
&self.root | ||
} | ||
|
||
pub fn update_root(&mut self, new_root: Hash) { | ||
self.root = new_root; | ||
} | ||
|
||
pub fn get_path(&self, index: u64) -> Vec<HalfHash> { | ||
let mut cur_index = index as usize; | ||
let mut ret: Vec<HalfHash> = Vec::new(); | ||
for layer in &self.tree { | ||
if layer.len() < 2 { | ||
break; | ||
} | ||
|
||
if cur_index % 2 == 1 { | ||
ret.push(layer.get(cur_index-1).unwrap().clone()); | ||
} else { | ||
ret.push(layer.get(cur_index+1).unwrap_or(&HalfHash::default()).clone()) | ||
} | ||
|
||
cur_index = cur_index / 2; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
pub fn proof_by_path(&self, proofs: Vec<HalfHash>, leaf_index: u64, leafdata: &[u8]) -> Hash { | ||
//println!("check leaf index {}", leaf_index); | ||
let leaf_hash = calc_hash(&self.hash_type, leafdata); | ||
let mut current32hash = leaf_hash; | ||
let mut cur_index = leaf_index; | ||
|
||
for proof in proofs { | ||
let current16hash = HalfHash::from_slice(¤t32hash.as_slice()[16..]).clone(); | ||
if proof.ne(&HalfHash::default()) { | ||
if cur_index % 2 == 0 { | ||
//println!("leaf index {}, proof i at right", cur_index); | ||
//println!("calc hash {} + {}", hex(current16hash.as_slice()), hex(proof.as_slice())); | ||
current32hash = calc_hash(&self.hash_type, &[current16hash, proof].concat()).into(); | ||
//println!("\t = {}", hex(current32hash.as_slice())); | ||
} else { | ||
//println!("leaf index {}, proof i at left", cur_index); | ||
//println!("calc hash {} + {}", hex(proof.as_slice()), hex(current16hash.as_slice())); | ||
current32hash = calc_hash(&self.hash_type, &[proof, current16hash].concat()).into(); | ||
//println!("\t = {}", hex(current32hash.as_slice())); | ||
} | ||
} | ||
|
||
cur_index = cur_index / 2; | ||
} | ||
|
||
return current32hash; | ||
} | ||
|
||
pub fn verify(&self, proof: Vec<HalfHash>, leaf_index: u64, leafdata: &[u8]) -> bool { | ||
let calc_root = self.proof_by_path(proof, leaf_index, leafdata); | ||
calc_root.eq(&self.root) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
mod ercmerkle_tree; | ||
|
||
use std::env::join_paths; | ||
use std::io::{Read, Write}; | ||
use std::os::windows::prelude::FileExt; | ||
use std::path::PathBuf; | ||
use clap::{Parser, Subcommand}; | ||
use crate::ercmerkle_tree::{calc_hash, HashType, hex, MerkleTree}; | ||
|
||
fn compare_bytes(a: &[u8], b: &[u8]) -> i32 { | ||
let n = a.len().min(b.len()); | ||
|
||
for i in 0..n { | ||
if a[i] != b[i] { | ||
return a[i] as i32 - b[i] as i32; | ||
} | ||
} | ||
|
||
return (a.len() - b.len()) as i32; | ||
} | ||
|
||
|
||
#[derive(Parser)] | ||
struct App { | ||
#[command(subcommand)] | ||
command: Subcommands | ||
} | ||
|
||
#[derive(Subcommand)] | ||
enum Subcommands { | ||
Create { | ||
#[arg(value_name="FILE")] | ||
file_path: PathBuf, | ||
#[arg(value_enum)] | ||
hash_type: HashType | ||
}, | ||
Proof { | ||
#[arg(value_name="FILE")] | ||
file_path: PathBuf, | ||
nonce_hash: String, | ||
|
||
leaf_index: Option<u64>, | ||
} | ||
} | ||
|
||
fn main() { | ||
let cli = App::parse(); | ||
match cli.command { | ||
Subcommands::Create { file_path, hash_type } => { | ||
let mut file = std::fs::File::open(&file_path).unwrap(); | ||
let size = file.metadata().unwrap().len(); | ||
println!("calcuting merkle tree..."); | ||
let mut merkle_tree = MerkleTree::new(hash_type); | ||
let mut read_buf = Vec::with_capacity(1024); | ||
read_buf.resize(1024, 0); | ||
loop { | ||
read_buf.fill(0); | ||
let readed = file.read(&mut read_buf).unwrap(); | ||
if readed == 0 { break; } | ||
merkle_tree.add_leaf(&read_buf); | ||
} | ||
merkle_tree.calc_tree(); | ||
|
||
let mut root_hash = merkle_tree.get_root().clone(); | ||
root_hash.as_mut_slice().write(&size.to_be_bytes()).unwrap(); | ||
root_hash.as_mut_slice()[0] &= (1 << 6) - 1; | ||
|
||
match hash_type { | ||
HashType::Sha256 => { | ||
// do nothing | ||
} | ||
HashType::Keccak256 => { | ||
root_hash.as_mut_slice()[0] |= 1 << 7; | ||
} | ||
} | ||
|
||
merkle_tree.update_root(root_hash); | ||
|
||
let merkle_data = merkle_tree.save(); | ||
std::fs::write(file_path.with_extension("merkle"), &serde_json::to_vec(&merkle_data).unwrap()).unwrap(); | ||
|
||
println!("create file root hash 0x{}", hex::encode(&root_hash)); | ||
} | ||
Subcommands::Proof { file_path, nonce_hash , leaf_index } => { | ||
let merkle_data = serde_json::from_slice(&std::fs::read(file_path.with_extension("merkle")).unwrap()).unwrap(); | ||
let merkle_tree = MerkleTree::load(merkle_data); | ||
let hash = hex::decode(&nonce_hash.as_str()[2..]).unwrap(); | ||
|
||
let file = std::fs::File::open(file_path).unwrap(); | ||
let length = file.metadata().unwrap().len(); | ||
let total_leaf_size = (length as f64 / 1024f64).ceil() as u64; | ||
|
||
let mut min_root: Option<ercmerkle_tree::Hash> = None; | ||
let mut min_index = None; | ||
|
||
let mut read_buf = Vec::with_capacity(1024); | ||
read_buf.resize(1024, 0); | ||
if let Some(i) = leaf_index { | ||
read_buf.fill(0); | ||
file.seek_read(&mut read_buf, i * 1024).unwrap(); | ||
let path = merkle_tree.get_path(i); | ||
let new_leaf: Vec<u8> = read_buf.iter().chain(hash.iter()).map(|v|*v).collect(); | ||
let new_root = merkle_tree.proof_by_path(path, i, &new_leaf); | ||
//println!("index {} new root {}", i, hex(new_root.as_slice())); | ||
if let Some(root) = min_root { | ||
if compare_bytes(new_root.as_slice(), root.as_slice()) < 0 { | ||
min_root.insert(new_root); | ||
min_index.insert(i); | ||
} | ||
} else { | ||
min_root.insert(new_root); | ||
min_index.insert(i); | ||
} | ||
} else { | ||
for i in 0..total_leaf_size { | ||
read_buf.fill(0); | ||
file.seek_read(&mut read_buf, i*1024).unwrap(); | ||
let path = merkle_tree.get_path(i); | ||
let new_leaf: Vec<u8> = read_buf.iter().chain(hash.iter()).map(|v|*v).collect(); | ||
let new_root = merkle_tree.proof_by_path(path, i, &new_leaf); | ||
//println!("index {} new root {}", i, hex(new_root.as_slice())); | ||
if let Some(root) = min_root { | ||
if compare_bytes(new_root.as_slice(), root.as_slice()) < 0 { | ||
min_root.insert(new_root); | ||
min_index.insert(i); | ||
} | ||
} else { | ||
min_root.insert(new_root); | ||
min_index.insert(i); | ||
} | ||
} | ||
} | ||
|
||
|
||
println!("found min index {}, min root {}", min_index.unwrap(), hex(min_root.unwrap().as_slice())); | ||
let paths: Vec<String> = merkle_tree.get_path(min_index.unwrap()).iter().map(|v|hex(v.as_slice())).collect(); | ||
println!("min path ["); | ||
for path in paths { | ||
println!("\t{},", path) | ||
} | ||
println!("]") | ||
} | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.