Skip to content

Commit

Permalink
Merge pull request #79 from Chia-Network/tree-hash-crate
Browse files Browse the repository at this point in the history
move tree_hash() into its own crate (clvm-utils)
  • Loading branch information
arvidn authored Nov 12, 2022
2 parents 8360606 + 1112e6b commit 4db1449
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 114 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/build-crate-and-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ jobs:
- name: build
run: cargo build --release
- name: dry-run of `cargo publish`
run: cargo publish --dry-run
run: |
cargo publish --dry-run
cd clvm-utils
cargo publish --dry-run
- name: Upload crate artifacts
uses: actions/upload-artifact@v2
Expand All @@ -68,7 +71,10 @@ jobs:
if: startsWith(github.event.ref, 'refs/tags')
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.cargo_registry_token }}
run: cargo publish
run: |
cargo publish
cd clvm-utils
cargo publish
# this has not been tested, so probably needs to be debugged next time a tag is created
- name: publish to npmjs.com if tagged
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,16 @@ jobs:
toolchain: nightly
- name: cargo-fuzz
run: cargo +nightly install cargo-fuzz
- name: cargo fuzz
- name: cargo fuzz (chia_rs)
run: cargo +nightly fuzz build
- name: cargo fuzz (chia-bls)
run: |
cd chia-bls
cargo +nightly fuzz build
- name: cargo fuzz (clvm-utils)
run: |
cd clvm-utils
cargo +nightly fuzz build
unit_tests:
runs-on: ${{ matrix.os }}
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# the "wheel" crate is excluded from the workspace because pyo3 has problems with
# "cargo test" and "cargo bench"
[workspace]
members = ["wasm", "chia_streamable_macro", "chia-bls"]
members = ["wasm", "chia_streamable_macro", "chia-bls", "clvm-utils"]
exclude = ["wheel"]

[package]
Expand All @@ -22,6 +22,7 @@ clvmr = "=0.1.24"
hex = "=0.4.3"
pyo3 = { version = "=0.15.1", features = ["extension-module"], optional = true }
chia_streamable_macro = { version = "=0.2.3", path = "chia_streamable_macro" }
clvm-utils = { version = "=0.1.14", path = "clvm-utils" }

[dev-dependencies]
num-traits = "=0.2.15"
Expand Down
15 changes: 15 additions & 0 deletions clvm-utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "clvm-utils"
version = "0.1.14"
edition = "2021"
license = "Apache-2.0"
description = "Utility functions for processing clvm programs and structures"
authors = ["Arvid Norberg <[email protected]>"]
homepage = "https://github.com/Chia-Network/chia_rs/"
repository = "https://github.com/Chia-Network/chia_rs/clvm-utils"

[dependencies]
clvmr = "=0.1.24"

[profile.release]
lto = true
9 changes: 9 additions & 0 deletions clvm-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Library providing utility functions for handling CLVM programs


computing tree hash:

```rust
fn tree_hash(a: &Allocator, node: NodePtr) -> [u8; 32]
```

29 changes: 29 additions & 0 deletions clvm-utils/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "clvm-utils-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2021"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"
clvmr = "=0.1.24"

[dependencies.clvm-utils]
path = ".."

[dependencies.chia]
path = "../.."

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[[bin]]
name = "tree-hash"
path = "fuzz_targets/tree-hash.rs"
test = false
doc = false
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use libfuzzer_sys::fuzz_target;

use clvmr::allocator::Allocator;
use chia::fuzzing_utils::{BitCursor, make_tree};
use chia::gen::get_puzzle_and_solution::tree_hash;
use clvm_utils::tree_hash::tree_hash;

fuzz_target!(|data: &[u8]| {
let mut a = Allocator::new();
Expand Down
1 change: 1 addition & 0 deletions clvm-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod tree_hash;
102 changes: 102 additions & 0 deletions clvm-utils/src/tree_hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use clvmr::allocator::{Allocator, NodePtr, SExp};
use clvmr::sha2::{Digest, Sha256};

enum TreeOp {
SExp(NodePtr),
Cons,
}

pub fn tree_hash(a: &Allocator, node: NodePtr) -> [u8; 32] {
let mut hashes: Vec<[u8; 32]> = vec![];
let mut ops = vec![TreeOp::SExp(node)];

while !ops.is_empty() {
match ops.pop().unwrap() {
TreeOp::SExp(node) => match a.sexp(node) {
SExp::Atom(atom) => {
let mut sha256 = Sha256::new();
sha256.update([1_u8]);
sha256.update(a.buf(&atom));
hashes.push(sha256.finalize().into())
}
SExp::Pair(left, right) => {
ops.push(TreeOp::Cons);
ops.push(TreeOp::SExp(left));
ops.push(TreeOp::SExp(right));
}
},
TreeOp::Cons => {
let mut sha256 = Sha256::new();
sha256.update([2_u8]);
sha256.update(hashes.pop().unwrap());
sha256.update(hashes.pop().unwrap());
hashes.push(sha256.finalize().into());
}
}
}

assert!(hashes.len() == 1);
hashes[0]
}

#[test]
fn test_tree_hash() {
let mut a = Allocator::new();
let atom1 = a.new_atom(&[1, 2, 3]).unwrap();
let atom2 = a.new_atom(&[4, 5, 6]).unwrap();
let root = a.new_pair(atom1, atom2).unwrap();

// test atom1 hash
let atom1_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[1_u8]);
sha256.update(&[1, 2, 3]);
let atom1_hash = sha256.finalize();

assert_eq!(tree_hash(&a, atom1), atom1_hash.as_slice());
atom1_hash
};

// test atom2 hash
let atom2_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[1_u8]);
sha256.update(&[4, 5, 6]);
let atom2_hash = sha256.finalize();

assert_eq!(tree_hash(&a, atom2), atom2_hash.as_slice());
atom2_hash
};

// test tree hash
let root_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[2_u8]);
sha256.update(atom1_hash.as_slice());
sha256.update(atom2_hash.as_slice());
let root_hash = sha256.finalize();

assert_eq!(tree_hash(&a, root), root_hash.as_slice());
root_hash
};

let atom3 = a.new_atom(&[7, 8, 9]).unwrap();
let root2 = a.new_pair(root, atom3).unwrap();

let atom3_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[1_u8]);
sha256.update(&[7, 8, 9]);
sha256.finalize()
};

// test deeper tree hash
{
let mut sha256 = Sha256::new();
sha256.update(&[2_u8]);
sha256.update(root_hash.as_slice());
sha256.update(atom3_hash.as_slice());

assert_eq!(tree_hash(&a, root2), sha256.finalize().as_slice());
}
}
6 changes: 0 additions & 6 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,3 @@ name = "parse-conditions"
path = "fuzz_targets/parse-conditions.rs"
test = false
doc = false

[[bin]]
name = "tree-hash"
path = "fuzz_targets/tree-hash.rs"
test = false
doc = false
108 changes: 5 additions & 103 deletions src/gen/get_puzzle_and_solution.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,10 @@
use crate::bytes::Bytes32;
use crate::gen::validation_error::{atom, check_nil, first, next, rest, ErrorCode, ValidationErr};
use clvmr::allocator::{Allocator, NodePtr, SExp};
use clvm_utils::tree_hash::tree_hash;
use clvmr::allocator::{Allocator, NodePtr};
use clvmr::op_utils::u64_from_bytes;
use clvmr::sha2::{Digest, Sha256};
use std::convert::AsRef;

enum TreeOp {
SExp(NodePtr),
Cons,
}

// TODO: perhaps this should move to clvm_rs
pub fn tree_hash(a: &Allocator, node: NodePtr) -> [u8; 32] {
let mut hashes: Vec<[u8; 32]> = vec![];
let mut ops = vec![TreeOp::SExp(node)];

while !ops.is_empty() {
match ops.pop().unwrap() {
TreeOp::SExp(node) => match a.sexp(node) {
SExp::Atom(atom) => {
let mut sha256 = Sha256::new();
sha256.update([1_u8]);
sha256.update(a.buf(&atom));
hashes.push(sha256.finalize().into())
}
SExp::Pair(left, right) => {
ops.push(TreeOp::Cons);
ops.push(TreeOp::SExp(left));
ops.push(TreeOp::SExp(right));
}
},
TreeOp::Cons => {
let mut sha256 = Sha256::new();
sha256.update([2_u8]);
sha256.update(hashes.pop().unwrap());
sha256.update(hashes.pop().unwrap());
hashes.push(sha256.finalize().into());
}
}
}

assert!(hashes.len() == 1);
hashes[0]
}

// returns parent-coin ID, amount, puzzle-reveal and solution
pub fn parse_coin_spend(
a: &Allocator,
Expand Down Expand Up @@ -113,6 +74,9 @@ fn u64_to_bytes(n: u64) -> Vec<u8> {
buf
}

#[cfg(test)]
use clvmr::sha2::{Digest, Sha256};

#[cfg(test)]
fn make_dummy_id(seed: u64) -> Bytes32 {
let mut sha256 = Sha256::new();
Expand Down Expand Up @@ -248,65 +212,3 @@ fn test_parse_coin_spend() {
ErrorCode::InvalidCoinAmount
);
}

#[test]
fn test_tree_hash() {
let mut a = Allocator::new();
let atom1 = a.new_atom(&[1, 2, 3]).unwrap();
let atom2 = a.new_atom(&[4, 5, 6]).unwrap();
let root = a.new_pair(atom1, atom2).unwrap();

// test atom1 hash
let atom1_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[1_u8]);
sha256.update(&[1, 2, 3]);
let atom1_hash = sha256.finalize();

assert_eq!(tree_hash(&a, atom1), atom1_hash.as_slice());
atom1_hash
};

// test atom2 hash
let atom2_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[1_u8]);
sha256.update(&[4, 5, 6]);
let atom2_hash = sha256.finalize();

assert_eq!(tree_hash(&a, atom2), atom2_hash.as_slice());
atom2_hash
};

// test tree hash
let root_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[2_u8]);
sha256.update(atom1_hash.as_slice());
sha256.update(atom2_hash.as_slice());
let root_hash = sha256.finalize();

assert_eq!(tree_hash(&a, root), root_hash.as_slice());
root_hash
};

let atom3 = a.new_atom(&[7, 8, 9]).unwrap();
let root2 = a.new_pair(root, atom3).unwrap();

let atom3_hash = {
let mut sha256 = Sha256::new();
sha256.update(&[1_u8]);
sha256.update(&[7, 8, 9]);
sha256.finalize()
};

// test deeper tree hash
{
let mut sha256 = Sha256::new();
sha256.update(&[2_u8]);
sha256.update(root_hash.as_slice());
sha256.update(atom3_hash.as_slice());

assert_eq!(tree_hash(&a, root2), sha256.finalize().as_slice());
}
}

0 comments on commit 4db1449

Please sign in to comment.