Skip to content

Replace H256 with the one in rust-numext #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ exclude = ["/fixtures", "/proptest-regressions"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["std", "blake2b"]
std = []
default = ["blake2b"]
blake2b = ["blake2b-rs"]

[dependencies]
cfg-if = "0.1"
blake2b-rs = { version = "0.1", optional = true }
numext-fixed-hash = "0.1.6"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numext-fixed-hash breaks the no-std feature. We should introduce it as optional, if user choose feature=std then enable the numext_fixed_hash::H256, otherwise we use the H256([u8; 32]).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we had agreed earlier, that no_std feature here does not make any sense, it's best we remove it and we should only leverage C based verifier in smart contract? Has anything changed since then?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decided to remove no_std support, we should actually remove the std feature from features section.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With #16 coming I do suggest we remove no_std support. @joii2020 can you make the change per @jjyr's suggestion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK


[dev-dependencies]
proptest = "0.9"
Expand Down
9 changes: 6 additions & 3 deletions benches/smt_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
extern crate criterion;

use criterion::Criterion;
use numext_fixed_hash;
use rand::{thread_rng, Rng};
use sparse_merkle_tree::{
blake2b::Blake2bHasher, default_store::DefaultStore, tree::SparseMerkleTree, H256,
blake2b::Blake2bHasher, default_store::DefaultStore, tree::SparseMerkleTree,
};

// Because the direct 'use numext_fixed_hash::H256' may cause part of the IDE errors. (may be about macro expansion)
type H256 = numext_fixed_hash::H256;
const TARGET_LEAVES_COUNT: usize = 20;

type SMT = SparseMerkleTree<Blake2bHasher, H256, DefaultStore<H256>>;
Expand All @@ -23,7 +26,7 @@ fn random_smt(update_count: usize, rng: &mut impl Rng) -> (SMT, Vec<H256>) {
for _ in 0..update_count {
let key = random_h256(rng);
let value = random_h256(rng);
smt.update(key, value).unwrap();
smt.update(key.clone(), value.clone()).unwrap();
keys.push(key);
}
(smt, keys)
Expand Down Expand Up @@ -71,7 +74,7 @@ fn bench(c: &mut Criterion) {
let leaves: Vec<_> = keys
.iter()
.take(TARGET_LEAVES_COUNT)
.map(|k| (*k, smt.get(k).unwrap()))
.map(|k| (k.clone(), smt.get(&k).unwrap()))
.collect();
let proof = smt
.merkle_proof(keys.into_iter().take(TARGET_LEAVES_COUNT).collect())
Expand Down
1 change: 1 addition & 0 deletions c/rust-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
anyhow = "1.0"
numext-fixed-hash = "0.1.6"

[build-dependencies]
cc = "1.0"
23 changes: 12 additions & 11 deletions c/rust-tests/src/tests/smt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use proptest::prelude::*;
use rand::prelude::Rng;
use serde::{Deserialize, Serialize};
use sparse_merkle_tree::traits::Hasher;
use sparse_merkle_tree::{default_store::DefaultStore, SparseMerkleTree, H256};
use sparse_merkle_tree::{default_store::DefaultStore, SparseMerkleTree};
use std::collections::HashMap;
use std::fs;
//use std::fs;
use numext_fixed_hash::H256;

#[link(name = "dl-c-impl", kind = "static")]
extern "C" {
Expand Down Expand Up @@ -145,7 +146,7 @@ impl Hasher for CkbBlake2bHasher {
self.0.update(&[b][..]);
}
fn write_h256(&mut self, h: &H256) {
self.0.update(h.as_slice());
self.0.update(h.as_bytes());
}
fn finish(self) -> H256 {
let mut hash = [0u8; 32];
Expand Down Expand Up @@ -273,7 +274,7 @@ fn run_test_case(case: Case) -> AnyResult<()> {

assert_eq!(smt_state.len(), leaves.len() as u32);
smt_state
.verify(ckb_smt.root().as_slice(), &ckb_actual_compiled_proof_bin)
.verify(ckb_smt.root().as_bytes(), &ckb_actual_compiled_proof_bin)
.unwrap();
}
Ok(())
Expand All @@ -300,27 +301,27 @@ proptest! {
const EXPECTED_PROOF_SIZE: usize = 16;

let mut tree = CkbSMT::default();
tree.update(key, value).expect("update");
tree.update(key.clone(), value.clone()).expect("update");
if !tree.is_empty() {
let proof = tree.merkle_proof(vec![key]).expect("proof");
let proof = tree.merkle_proof(vec![key.clone()]).expect("proof");
let compiled_proof = proof
.clone()
.compile(vec![(key, value)])
.compile(vec![(key.clone(), value.clone())])
.expect("compile proof");
assert!(proof.merkle_path().len() < EXPECTED_PROOF_SIZE);
assert!(proof
.verify::<CkbBlake2bHasher>(tree.root(), vec![(key, value)])
.verify::<CkbBlake2bHasher>(tree.root(), vec![(key.clone(), value.clone())])
.expect("verify"));
assert!(compiled_proof
.verify::<CkbBlake2bHasher>(tree.root(), vec![(key, value)])
.verify::<CkbBlake2bHasher>(tree.root(), vec![(key.clone(), value.clone())])
.expect("compiled verify"));

let compiled_proof_bin: Vec<u8> = compiled_proof.into();
let mut smt_state = SmtCImpl::new(8);
smt_state.insert(key.as_slice(), value.as_slice()).unwrap();
smt_state.insert(key.as_bytes(), value.as_bytes()).unwrap();
smt_state.normalize();
smt_state
.verify(tree.root().as_slice(), &compiled_proof_bin)
.verify(tree.root().as_bytes(), &compiled_proof_bin)
.expect("verify with c");
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/blake2b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl Default for Blake2bHasher {

impl Hasher for Blake2bHasher {
fn write_h256(&mut self, h: &H256) {
self.0.update(h.as_slice());
self.0.update(h.as_bytes());
}
fn write_byte(&mut self, b: u8) {
self.0.update(&[b][..]);
Expand Down
11 changes: 2 additions & 9 deletions src/default_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,5 @@ impl<V: Clone> Store<V> for DefaultStore<V> {
}
}

cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
pub type Map<K, V> = collections::HashMap<K, V>;
pub type Entry<'a, K, V> = collections::hash_map::Entry<'a, K, V>;
} else {
pub type Map<K, V> = collections::BTreeMap<K, V>;
pub type Entry<'a, K, V> = collections::btree_map::Entry<'a, K, V>;
}
}
pub type Map<K, V> = collections::BTreeMap<K, V>;
pub type Entry<'a, K, V> = collections::btree_map::Entry<'a, K, V>;
1 change: 0 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,4 @@ impl core::fmt::Display for Error {
}
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}
124 changes: 30 additions & 94 deletions src/h256.rs
Original file line number Diff line number Diff line change
@@ -1,112 +1,48 @@
use core::cmp::Ordering;
use numext_fixed_hash;

/// Represent 256 bits
#[derive(Eq, PartialEq, Debug, Default, Hash, Clone, Copy)]
pub struct H256([u8; 32]);
pub(crate) type H256 = numext_fixed_hash::H256;

const ZERO: H256 = H256([0u8; 32]);
const BYTE_SIZE: u8 = 8;

impl H256 {
pub const fn zero() -> Self {
ZERO
}

pub fn is_zero(&self) -> bool {
self == &ZERO
}

#[inline]
pub fn get_bit(&self, i: u8) -> bool {
let byte_pos = i / BYTE_SIZE;
let bit_pos = i % BYTE_SIZE;
let bit = self.0[byte_pos as usize] >> bit_pos & 1;
bit != 0
}

#[inline]
pub fn set_bit(&mut self, i: u8) {
let byte_pos = i / BYTE_SIZE;
let bit_pos = i % BYTE_SIZE;
self.0[byte_pos as usize] |= 1 << bit_pos as u8;
}

#[inline]
pub fn clear_bit(&mut self, i: u8) {
let byte_pos = i / BYTE_SIZE;
let bit_pos = i % BYTE_SIZE;
self.0[byte_pos as usize] &= !((1 << bit_pos) as u8);
}

#[inline]
pub fn is_right(&self, height: u8) -> bool {
self.get_bit(height)
}

pub fn as_slice(&self) -> &[u8] {
&self.0[..]
}

/// Treat H256 as a path in a tree
/// fork height is the number of common bits(from heigher to lower: 255..=0) of two H256
pub fn fork_height(&self, key: &H256) -> u8 {
for h in (0..=core::u8::MAX).rev() {
if self.get_bit(h) != key.get_bit(h) {
return h;
}
pub fn fork_height(data: &H256, key: &H256) -> u8 {
for h in (0..=core::u8::MAX).rev() {
if data.bit(h.into()).unwrap_or(false) != key.bit(h.into()).unwrap_or(false) {
return h;
}
0
}
0
}

/// Treat H256 as a path in a tree
/// return parent_path of self
pub fn parent_path(&self, height: u8) -> Self {
if height == core::u8::MAX {
H256::zero()
} else {
self.copy_bits(height + 1)
}
pub fn parent_path(data: &H256, height: u8) -> H256 {
if height == core::u8::MAX {
H256::empty()
} else {
copy_bits(data, height + 1)
}
}

/// Copy bits and return a new H256
pub fn copy_bits(&self, start: u8) -> Self {
let mut target = H256::zero();

let start_byte = (start / BYTE_SIZE) as usize;
// copy bytes
target.0[start_byte..].copy_from_slice(&self.0[start_byte..]);
/// Copy bits and return a new H256
pub fn copy_bits(data: &H256, start: u8) -> H256 {
// It can also be implemented with And, but the performance is not as good as this
let mut target = H256::empty();

// reset remain bytes
let remain = start % BYTE_SIZE;
if remain > 0 {
target.0[start_byte] &= 0b11111111 << remain
}
let start_byte = (start / BYTE_SIZE) as usize;
// copy bytes
target.0[start_byte..].copy_from_slice(&data.0[start_byte..]);

target
// reset remain bytes
let remain = start % BYTE_SIZE;
if remain > 0 {
target.0[start_byte] &= 0b11111111 << remain
}
}

impl PartialOrd for H256 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for H256 {
fn cmp(&self, other: &Self) -> Ordering {
// Compare bits from heigher to lower (255..0)
self.0.iter().rev().cmp(other.0.iter().rev())
}
target
}

impl From<[u8; 32]> for H256 {
fn from(v: [u8; 32]) -> H256 {
H256(v)
}
pub(crate) fn smt_sort_unstable(v: &mut Vec<H256>) {
(*v).sort_unstable_by(|k1, k2| k1.0.iter().rev().cmp(k2.0.iter().rev()));
}

impl Into<[u8; 32]> for H256 {
fn into(self: H256) -> [u8; 32] {
self.0
}
pub(crate) fn smt_sort_unstable_kv(v: &mut Vec<(H256, H256)>) {
v.sort_unstable_by(|(k1, _v1), (k2, _v2)| k1.0.iter().rev().cmp(k2.0.iter().rev()));
}
24 changes: 7 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
//! use sparse_merkle_tree::{
//! blake2b::Blake2bHasher, default_store::DefaultStore,
//! error::Error, MerkleProof,
//! SparseMerkleTree, traits::Value, H256
//! SparseMerkleTree, traits::Value
//! };
//! use blake2b_rs::{Blake2b, Blake2bBuilder};
//! use numext_fixed_hash::H256;
//!
//! // define SMT
//! type SMT = SparseMerkleTree<Blake2bHasher, Word, DefaultStore<Word>>;
Expand All @@ -19,7 +20,7 @@
//! impl Value for Word {
//! fn to_h256(&self) -> H256 {
//! if self.0.is_empty() {
//! return H256::zero();
//! return H256::empty();
//! }
//! let mut buf = [0u8; 32];
//! let mut hasher = new_blake2b();
Expand Down Expand Up @@ -59,8 +60,6 @@
//! }
//! ```

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "blake2b")]
pub mod blake2b;
pub mod default_store;
Expand All @@ -73,7 +72,7 @@ mod tests;
pub mod traits;
pub mod tree;

pub use h256::H256;
pub(crate) use h256::H256;
pub use merkle_proof::{CompiledMerkleProof, MerkleProof};
pub use tree::SparseMerkleTree;

Expand All @@ -82,15 +81,6 @@ pub const EXPECTED_PATH_SIZE: usize = 16;
// Max stack size can be used when verify compiled proof
pub(crate) const MAX_STACK_SIZE: usize = 257;

cfg_if::cfg_if! {
if #[cfg(feature = "std")] {
use std::collections;
use std::vec;
use std::string;
} else {
extern crate alloc;
use alloc::collections;
use alloc::vec;
use alloc::string;
}
}
extern crate alloc;
use alloc::collections;
use alloc::string;
Loading