Skip to content

Commit ba58745

Browse files
committed
Drop ahash dependency in favor of core's SipHasher
tkaitchuck/aHash#196 bumped the MSRV of `ahash` in a patch release, which makes it rather difficult for us to have it as a dependency. Further, it seems that `ahash` hasn't been particularly robust in the past, notably tkaitchuck/aHash#163 and tkaitchuck/aHash#166. Luckily, `core` provides `SipHasher` even on no-std (sadly its SipHash-2-4 unlike the SipHash-1-3 used by the `DefaultHasher` in `std`). Thus, we drop the `ahash` dependency entirely here and simply wrap `SipHasher` for our `no-std` HashMaps.
1 parent 6181758 commit ba58745

File tree

5 files changed

+169
-115
lines changed

5 files changed

+169
-115
lines changed

bench/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ name = "bench"
99
harness = false
1010

1111
[features]
12-
hashbrown = ["lightning/hashbrown", "lightning/ahash"]
12+
hashbrown = ["lightning/hashbrown"]
1313

1414
[dependencies]
1515
lightning = { path = "../lightning", features = ["_test_utils", "criterion"] }

lightning/Cargo.toml

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ unsafe_revoked_tx_signing = []
3131
# Override signing to not include randomness when generating signatures for test vectors.
3232
_test_vectors = []
3333

34-
no-std = ["hashbrown", "ahash", "bitcoin/no-std", "core2/alloc", "libm"]
34+
no-std = ["hashbrown", "possiblyrandom", "bitcoin/no-std", "core2/alloc", "libm"]
3535
std = ["bitcoin/std"]
3636

3737
# Generates low-r bitcoin signatures, which saves 1 byte in 50% of the cases
@@ -43,24 +43,14 @@ default = ["std", "grind_signatures"]
4343
bitcoin = { version = "0.30.2", default-features = false, features = ["secp-recovery"] }
4444

4545
hashbrown = { version = "0.13", optional = true }
46-
ahash = { version = "0.8", optional = true, default-features = false }
46+
possiblyrandom = { path = "../possiblyrandom", optional = true, default-features = false }
4747
hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
4848
regex = { version = "1.5.6", optional = true }
4949
backtrace = { version = "0.3", optional = true }
5050

5151
core2 = { version = "0.3.0", optional = true, default-features = false }
5252
libm = { version = "0.2", optional = true, default-features = false }
5353

54-
# Because ahash no longer (kinda poorly) does it for us, (roughly) list out the targets that
55-
# getrandom supports and turn on ahash's `runtime-rng` feature for them.
56-
[target.'cfg(not(any(target_os = "unknown", target_os = "none")))'.dependencies]
57-
ahash = { version = "0.8", optional = true, default-features = false, features = ["runtime-rng"] }
58-
59-
# Not sure what target_os gets set to for sgx, so to be safe always enable runtime-rng for x86_64
60-
# platforms (assuming LDK isn't being used on embedded x86-64 running directly on metal).
61-
[target.'cfg(target_arch = "x86_64")'.dependencies]
62-
ahash = { version = "0.8", optional = true, default-features = false, features = ["runtime-rng"] }
63-
6454
[dev-dependencies]
6555
regex = "1.5.6"
6656

lightning/src/lib.rs

Lines changed: 1 addition & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -165,113 +165,12 @@ mod io_extras {
165165
}
166166

167167
mod prelude {
168-
#[cfg(feature = "hashbrown")]
169-
extern crate hashbrown;
170-
#[cfg(feature = "ahash")]
171-
extern crate ahash;
172-
173168
pub use alloc::{vec, vec::Vec, string::String, collections::VecDeque, boxed::Box};
174169

175170
pub use alloc::borrow::ToOwned;
176171
pub use alloc::string::ToString;
177172

178-
// For no-std builds, we need to use hashbrown, however, by default, it doesn't randomize the
179-
// hashing and is vulnerable to HashDoS attacks. Thus, when not fuzzing, we use its default
180-
// ahash hashing algorithm but randomize, opting to not randomize when fuzzing to avoid false
181-
// positive branch coverage.
182-
183-
#[cfg(not(feature = "hashbrown"))]
184-
mod std_hashtables {
185-
pub(crate) use std::collections::{HashMap, HashSet, hash_map};
186-
187-
pub(crate) type OccupiedHashMapEntry<'a, K, V> =
188-
std::collections::hash_map::OccupiedEntry<'a, K, V>;
189-
pub(crate) type VacantHashMapEntry<'a, K, V> =
190-
std::collections::hash_map::VacantEntry<'a, K, V>;
191-
}
192-
#[cfg(not(feature = "hashbrown"))]
193-
pub(crate) use std_hashtables::*;
194-
195-
#[cfg(feature = "hashbrown")]
196-
pub(crate) use self::hashbrown::hash_map;
197-
198-
#[cfg(all(feature = "hashbrown", fuzzing))]
199-
mod nonrandomized_hashbrown {
200-
pub(crate) use hashbrown::{HashMap, HashSet};
201-
202-
pub(crate) type OccupiedHashMapEntry<'a, K, V> =
203-
hashbrown::hash_map::OccupiedEntry<'a, K, V, hashbrown::hash_map::DefaultHashBuilder>;
204-
pub(crate) type VacantHashMapEntry<'a, K, V> =
205-
hashbrown::hash_map::VacantEntry<'a, K, V, hashbrown::hash_map::DefaultHashBuilder>;
206-
}
207-
#[cfg(all(feature = "hashbrown", fuzzing))]
208-
pub(crate) use nonrandomized_hashbrown::*;
209-
210-
211-
#[cfg(all(feature = "hashbrown", not(fuzzing)))]
212-
mod randomized_hashtables {
213-
use super::*;
214-
use ahash::RandomState;
215-
216-
pub(crate) type HashMap<K, V> = hashbrown::HashMap<K, V, RandomState>;
217-
pub(crate) type HashSet<K> = hashbrown::HashSet<K, RandomState>;
218-
219-
pub(crate) type OccupiedHashMapEntry<'a, K, V> =
220-
hashbrown::hash_map::OccupiedEntry<'a, K, V, RandomState>;
221-
pub(crate) type VacantHashMapEntry<'a, K, V> =
222-
hashbrown::hash_map::VacantEntry<'a, K, V, RandomState>;
223-
224-
pub(crate) fn new_hash_map<K, V>() -> HashMap<K, V> {
225-
HashMap::with_hasher(RandomState::new())
226-
}
227-
pub(crate) fn hash_map_with_capacity<K, V>(cap: usize) -> HashMap<K, V> {
228-
HashMap::with_capacity_and_hasher(cap, RandomState::new())
229-
}
230-
pub(crate) fn hash_map_from_iter<K: core::hash::Hash + Eq, V, I: IntoIterator<Item=(K, V)>>(iter: I) -> HashMap<K, V> {
231-
let iter = iter.into_iter();
232-
let min_size = iter.size_hint().0;
233-
let mut res = HashMap::with_capacity_and_hasher(min_size, RandomState::new());
234-
res.extend(iter);
235-
res
236-
}
237-
238-
pub(crate) fn new_hash_set<K>() -> HashSet<K> {
239-
HashSet::with_hasher(RandomState::new())
240-
}
241-
pub(crate) fn hash_set_with_capacity<K>(cap: usize) -> HashSet<K> {
242-
HashSet::with_capacity_and_hasher(cap, RandomState::new())
243-
}
244-
pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item=K>>(iter: I) -> HashSet<K> {
245-
let iter = iter.into_iter();
246-
let min_size = iter.size_hint().0;
247-
let mut res = HashSet::with_capacity_and_hasher(min_size, RandomState::new());
248-
res.extend(iter);
249-
res
250-
}
251-
}
252-
253-
#[cfg(any(not(feature = "hashbrown"), fuzzing))]
254-
mod randomized_hashtables {
255-
use super::*;
256-
257-
pub(crate) fn new_hash_map<K, V>() -> HashMap<K, V> { HashMap::new() }
258-
pub(crate) fn hash_map_with_capacity<K, V>(cap: usize) -> HashMap<K, V> {
259-
HashMap::with_capacity(cap)
260-
}
261-
pub(crate) fn hash_map_from_iter<K: core::hash::Hash + Eq, V, I: IntoIterator<Item=(K, V)>>(iter: I) -> HashMap<K, V> {
262-
HashMap::from_iter(iter)
263-
}
264-
265-
pub(crate) fn new_hash_set<K>() -> HashSet<K> { HashSet::new() }
266-
pub(crate) fn hash_set_with_capacity<K>(cap: usize) -> HashSet<K> {
267-
HashSet::with_capacity(cap)
268-
}
269-
pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item=K>>(iter: I) -> HashSet<K> {
270-
HashSet::from_iter(iter)
271-
}
272-
}
273-
274-
pub(crate) use randomized_hashtables::*;
173+
pub(crate) use crate::util::hash_tables::*;
275174
}
276175

277176
#[cfg(all(not(ldk_bench), feature = "backtrace", feature = "std", test))]

lightning/src/util/hash_tables.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//! Generally LDK uses `std`'s `HashMap`s, however when building for no-std, LDK uses `hashbrown`'s
2+
//! `HashMap`s with the `std` `SipHasher` and uses `getrandom` to opportunistically randomize it,
3+
//! if randomization is available.
4+
//!
5+
//! This module simply re-exports the `HashMap` used in LDK for public consumption.
6+
7+
#[cfg(feature = "hashbrown")]
8+
extern crate hashbrown;
9+
#[cfg(feature = "possiblyrandom")]
10+
extern crate possiblyrandom;
11+
12+
// For no-std builds, we need to use hashbrown, however, by default, it doesn't randomize the
13+
// hashing and is vulnerable to HashDoS attacks. Thus, we use the core SipHasher when not using
14+
// std, but use `getrandom` to randomize it if its available.
15+
16+
#[cfg(not(feature = "hashbrown"))]
17+
mod std_hashtables {
18+
pub use std::collections::HashMap;
19+
pub use std::collections::hash_map::RandomState;
20+
21+
pub(crate) use std::collections::{HashSet, hash_map};
22+
23+
pub(crate) type OccupiedHashMapEntry<'a, K, V> =
24+
std::collections::hash_map::OccupiedEntry<'a, K, V>;
25+
pub(crate) type VacantHashMapEntry<'a, K, V> =
26+
std::collections::hash_map::VacantEntry<'a, K, V>;
27+
}
28+
#[cfg(not(feature = "hashbrown"))]
29+
pub use std_hashtables::*;
30+
31+
#[cfg(feature = "hashbrown")]
32+
pub(crate) use self::hashbrown::hash_map;
33+
34+
#[cfg(all(feature = "hashbrown", fuzzing))]
35+
mod nonrandomized_hashbrown {
36+
pub use hashbrown::HashMap;
37+
pub use std::collections::hash_map::RandomState;
38+
39+
pub(crate) use hashbrown::HashSet;
40+
41+
pub(crate) type OccupiedHashMapEntry<'a, K, V> =
42+
hashbrown::hash_map::OccupiedEntry<'a, K, V, hashbrown::hash_map::DefaultHashBuilder>;
43+
pub(crate) type VacantHashMapEntry<'a, K, V> =
44+
hashbrown::hash_map::VacantEntry<'a, K, V, hashbrown::hash_map::DefaultHashBuilder>;
45+
}
46+
#[cfg(all(feature = "hashbrown", fuzzing))]
47+
pub use nonrandomized_hashbrown::*;
48+
49+
#[cfg(all(feature = "hashbrown", not(fuzzing)))]
50+
mod randomized_hashtables {
51+
#[cfg(feature = "std")]
52+
mod hasher {
53+
pub use std::collections::hash_map::RandomState;
54+
}
55+
#[cfg(not(feature = "std"))]
56+
mod hasher {
57+
#![allow(deprecated)] // hash::SipHasher was deprecated in favor of something only in std.
58+
use core::hash::{BuildHasher, SipHasher};
59+
60+
#[derive(Clone, Copy)]
61+
/// A simple implementation of [`BuildHasher`] that uses `getrandom` to opportunistically
62+
/// randomize, if the platform supports it.
63+
pub struct RandomState {
64+
k0: u64, k1: u64,
65+
}
66+
67+
impl RandomState {
68+
/// Constructs a new [`RandomState`] which may or may not be random, depending on the
69+
/// target platform.
70+
pub fn new() -> RandomState {
71+
let (k0, k1);
72+
#[cfg(feature = "possiblyrandom")] {
73+
let mut keys = [0; 16];
74+
possiblyrandom::getpossiblyrandom(&mut keys);
75+
76+
let mut k0_bytes = [0; 8];
77+
let mut k1_bytes = [0; 8];
78+
k0_bytes.copy_from_slice(&keys[..8]);
79+
k1_bytes.copy_from_slice(&keys[8..]);
80+
k0 = u64::from_le_bytes(k0_bytes);
81+
k1 = u64::from_le_bytes(k1_bytes);
82+
}
83+
#[cfg(not(feature = "possiblyrandom"))] {
84+
k0 = 0;
85+
k1 = 0;
86+
}
87+
RandomState { k0, k1 }
88+
}
89+
}
90+
91+
impl BuildHasher for RandomState {
92+
type Hasher = SipHasher;
93+
fn build_hasher(&self) -> SipHasher {
94+
SipHasher::new_with_keys(self.k0, self.k1)
95+
}
96+
}
97+
}
98+
99+
pub use hasher::*;
100+
use super::*;
101+
102+
/// The HashMap type used in LDK.
103+
pub type HashMap<K, V> = hashbrown::HashMap<K, V, RandomState>;
104+
pub(crate) type HashSet<K> = hashbrown::HashSet<K, RandomState>;
105+
106+
pub(crate) type OccupiedHashMapEntry<'a, K, V> =
107+
hashbrown::hash_map::OccupiedEntry<'a, K, V, RandomState>;
108+
pub(crate) type VacantHashMapEntry<'a, K, V> =
109+
hashbrown::hash_map::VacantEntry<'a, K, V, RandomState>;
110+
111+
pub(crate) fn new_hash_map<K, V>() -> HashMap<K, V> {
112+
HashMap::with_hasher(RandomState::new())
113+
}
114+
pub(crate) fn hash_map_with_capacity<K, V>(cap: usize) -> HashMap<K, V> {
115+
HashMap::with_capacity_and_hasher(cap, RandomState::new())
116+
}
117+
pub(crate) fn hash_map_from_iter<K: core::hash::Hash + Eq, V, I: IntoIterator<Item=(K, V)>>(iter: I) -> HashMap<K, V> {
118+
let iter = iter.into_iter();
119+
let min_size = iter.size_hint().0;
120+
let mut res = HashMap::with_capacity_and_hasher(min_size, RandomState::new());
121+
res.extend(iter);
122+
res
123+
}
124+
125+
pub(crate) fn new_hash_set<K>() -> HashSet<K> {
126+
HashSet::with_hasher(RandomState::new())
127+
}
128+
pub(crate) fn hash_set_with_capacity<K>(cap: usize) -> HashSet<K> {
129+
HashSet::with_capacity_and_hasher(cap, RandomState::new())
130+
}
131+
pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item=K>>(iter: I) -> HashSet<K> {
132+
let iter = iter.into_iter();
133+
let min_size = iter.size_hint().0;
134+
let mut res = HashSet::with_capacity_and_hasher(min_size, RandomState::new());
135+
res.extend(iter);
136+
res
137+
}
138+
}
139+
#[cfg(all(feature = "hashbrown", not(fuzzing)))]
140+
pub use randomized_hashtables::*;
141+
142+
#[cfg(any(not(feature = "hashbrown"), fuzzing))]
143+
mod hashtable_constructors {
144+
use super::*;
145+
146+
pub(crate) fn new_hash_map<K, V>() -> HashMap<K, V> { HashMap::new() }
147+
pub(crate) fn hash_map_with_capacity<K, V>(cap: usize) -> HashMap<K, V> {
148+
HashMap::with_capacity(cap)
149+
}
150+
pub(crate) fn hash_map_from_iter<K: core::hash::Hash + Eq, V, I: IntoIterator<Item=(K, V)>>(iter: I) -> HashMap<K, V> {
151+
HashMap::from_iter(iter)
152+
}
153+
154+
pub(crate) fn new_hash_set<K>() -> HashSet<K> { HashSet::new() }
155+
pub(crate) fn hash_set_with_capacity<K>(cap: usize) -> HashSet<K> {
156+
HashSet::with_capacity(cap)
157+
}
158+
pub(crate) fn hash_set_from_iter<K: core::hash::Hash + Eq, I: IntoIterator<Item=K>>(iter: I) -> HashSet<K> {
159+
HashSet::from_iter(iter)
160+
}
161+
}
162+
#[cfg(any(not(feature = "hashbrown"), fuzzing))]
163+
164+
pub use hashtable_constructors::*;

lightning/src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub(crate) mod atomic_counter;
3232
pub(crate) mod byte_utils;
3333
pub(crate) mod transaction_utils;
3434
pub(crate) mod time;
35+
pub mod hash_tables;
3536

3637
pub mod indexed_map;
3738

0 commit comments

Comments
 (0)