Skip to content

Commit

Permalink
fix custom hasher to also use a salt
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn committed Dec 30, 2024
1 parent 8671a04 commit a6df51e
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 13 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ k256 = { version = "0.13.4", features = ["ecdsa"] }
p256 = { version = "0.13.2", features = ["ecdsa"] }
# for keccak256
sha3 = "0.10.8"
rand = { workspace = true }

[dev-dependencies]
rstest = { workspace = true }
Expand Down
41 changes: 28 additions & 13 deletions src/serde/read_cache_lookup.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bitvec::prelude::*;
use bitvec::vec::BitVec;
use rand::Rng;
/// When deserializing a clvm object, a stack of deserialized child objects
/// is created, which can be used with back-references. A `ReadCacheLookup` keeps
/// track of the state of this stack and all child objects under each root
Expand All @@ -23,27 +24,43 @@ use std::hash::{BuildHasher, Hasher};
use super::bytes32::{hash_blob, hash_blobs, Bytes32};

#[derive(Default, Clone, Copy)]
pub struct IdentityHash(u64);
pub struct IdentityHash(u64, u64);

impl IdentityHash {
fn new(salt: u64) -> Self {
Self(0, salt)
}
}

impl Hasher for IdentityHash {
fn finish(&self) -> u64 {
self.0
}

fn write(&mut self, bytes: &[u8]) {
self.0 = u64::from_le_bytes(bytes[0..8].try_into().expect("expected 32 byte hashes"));
self.0 =
u64::from_le_bytes(bytes[0..8].try_into().expect("expected 32 byte hashes")) ^ self.1;
}

fn write_u64(&mut self, _i: u64) {
panic!("This hasher only takes bytes");
}
}

impl BuildHasher for IdentityHash {
type Hasher = Self;
pub struct RandomState(u64);

impl RandomState {
fn new() -> Self {
let mut rng = rand::thread_rng();
Self(rng.gen())
}
}

impl BuildHasher for RandomState {
type Hasher = IdentityHash;

fn build_hasher(&self) -> Self::Hasher {
*self
IdentityHash::new(self.0)
}
}

Expand All @@ -56,10 +73,10 @@ pub struct ReadCacheLookup {
/// the tree hashes of the contents on the left and right
read_stack: Vec<(Bytes32, Bytes32)>,

count: HashMap<Bytes32, u32, IdentityHash>,
count: HashMap<Bytes32, u32, RandomState>,

/// a mapping of tree hashes to `(parent, is_right)` tuples
parent_lookup: HashMap<Bytes32, Vec<(Bytes32, bool)>, IdentityHash>,
parent_lookup: HashMap<Bytes32, Vec<(Bytes32, bool)>, RandomState>,
}

impl Default for ReadCacheLookup {
Expand All @@ -74,9 +91,9 @@ impl ReadCacheLookup {
let read_stack = Vec::with_capacity(1000);
// all keys in count and parent_lookup are tree-hashes. There's no need
// to hash them again for the hash map
let mut count = HashMap::with_hasher(IdentityHash::default());
let mut count = HashMap::with_hasher(RandomState::new());
count.insert(root_hash, 1);
let parent_lookup = HashMap::with_hasher(IdentityHash::default());
let parent_lookup = HashMap::with_hasher(RandomState::new());
Self {
root_hash,
read_stack,
Expand Down Expand Up @@ -161,10 +178,8 @@ impl ReadCacheLookup {

// all the values we put in this hash set are themselves sha256 hashes.
// There's no point in hashing the hashes
let mut seen_ids = HashSet::<&Bytes32, IdentityHash>::with_capacity_and_hasher(
1000,
IdentityHash::default(),
);
let mut seen_ids =
HashSet::<&Bytes32, RandomState>::with_capacity_and_hasher(1000, RandomState::new());

let max_bytes_for_path_encoding = serialized_length - 2; // 1 byte for 0xfe, 1 min byte for savings
let max_path_length: usize = (max_bytes_for_path_encoding.saturating_mul(8) - 1)
Expand Down

0 comments on commit a6df51e

Please sign in to comment.