From 6e22f368c916afa7bd4ab3bf1ef57873cb7409e5 Mon Sep 17 00:00:00 2001 From: CleverWild Date: Wed, 8 Apr 2026 01:32:59 +0200 Subject: [PATCH] refactor(hashing): introduce Hashable derive macro and migrate server types --- server/Cargo.lock | 14 ++ server/Cargo.toml | 1 + server/crates/arbiter-crypto/Cargo.toml | 3 + server/crates/arbiter-crypto/src/authn/v1.rs | 11 +- .../v1 => arbiter-crypto/src}/hashing.rs | 22 +-- server/crates/arbiter-crypto/src/lib.rs | 2 +- server/crates/arbiter-macros/Cargo.toml | 18 +++ server/crates/arbiter-macros/src/hashable.rs | 133 ++++++++++++++++++ server/crates/arbiter-macros/src/lib.rs | 10 ++ server/crates/arbiter-macros/src/utils.rs | 19 +++ server/crates/arbiter-server/Cargo.toml | 3 +- .../arbiter-server/src/actors/client/mod.rs | 10 +- .../src/actors/user_agent/mod.rs | 17 +-- .../arbiter-server/src/crypto/integrity/v1.rs | 23 +-- .../crates/arbiter-server/src/evm/policies.rs | 43 +----- .../src/evm/policies/ether_transfer/mod.rs | 11 +- .../src/evm/policies/ether_transfer/tests.rs | 2 +- .../src/evm/policies/token_transfers/mod.rs | 12 +- .../src/evm/policies/token_transfers/tests.rs | 2 +- 19 files changed, 238 insertions(+), 118 deletions(-) rename server/crates/{arbiter-server/src/crypto/integrity/v1 => arbiter-crypto/src}/hashing.rs (83%) create mode 100644 server/crates/arbiter-macros/Cargo.toml create mode 100644 server/crates/arbiter-macros/src/hashable.rs create mode 100644 server/crates/arbiter-macros/src/lib.rs create mode 100644 server/crates/arbiter-macros/src/utils.rs diff --git a/server/Cargo.lock b/server/Cargo.lock index 5b89b7e..ce49a42 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -696,12 +696,25 @@ dependencies = [ name = "arbiter-crypto" version = "0.1.0" dependencies = [ + "alloy", "base64", + "chrono", + "hmac", "memsafe", "ml-dsa", "rand 0.10.0", ] +[[package]] +name = "arbiter-macros" +version = "0.1.0" +dependencies = [ + "arbiter-crypto", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "arbiter-proto" version = "0.1.0" @@ -736,6 +749,7 @@ dependencies = [ "alloy", "anyhow", "arbiter-crypto", + "arbiter-macros", "arbiter-proto", "arbiter-tokens-registry", "argon2", diff --git a/server/Cargo.toml b/server/Cargo.toml index 98018a0..f13b0c2 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -47,3 +47,4 @@ miette = { version = "7.6.0", features = ["fancy", "serde"] } mutants = "0.0.4" ml-dsa = { version = "0.1.0-rc.8", features = ["zeroize"] } base64 = "0.22.1" +hmac = "0.12.1" diff --git a/server/crates/arbiter-crypto/Cargo.toml b/server/crates/arbiter-crypto/Cargo.toml index e39c7eb..be3b873 100644 --- a/server/crates/arbiter-crypto/Cargo.toml +++ b/server/crates/arbiter-crypto/Cargo.toml @@ -8,6 +8,9 @@ ml-dsa = {workspace = true, optional = true } rand = {workspace = true, optional = true} base64 = {workspace = true, optional = true } memsafe = {version = "0.4.0", optional = true} +hmac.workspace = true +alloy.workspace = true +chrono.workspace = true [lints] workspace = true diff --git a/server/crates/arbiter-crypto/src/authn/v1.rs b/server/crates/arbiter-crypto/src/authn/v1.rs index 6536383..c33de49 100644 --- a/server/crates/arbiter-crypto/src/authn/v1.rs +++ b/server/crates/arbiter-crypto/src/authn/v1.rs @@ -1,4 +1,5 @@ use base64::{Engine as _, prelude::BASE64_STANDARD}; +use hmac::digest::Digest; use ml_dsa::{ EncodedVerifyingKey, Error, KeyGen, MlDsa87, Seed, Signature as MlDsaSignature, SigningKey as MlDsaSigningKey, VerifyingKey as MlDsaVerifyingKey, signature::Keypair as _, @@ -17,6 +18,12 @@ pub type KeyParams = MlDsa87; #[derive(Clone, Debug, PartialEq)] pub struct PublicKey(Box>); +impl crate::hashing::Hashable for PublicKey { + fn hash(&self, hasher: &mut H) { + hasher.update(self.to_bytes()); + } +} + #[derive(Clone, Debug, PartialEq)] pub struct Signature(Box>); @@ -25,7 +32,7 @@ pub struct SigningKey(Box>); impl PublicKey { pub fn to_bytes(&self) -> Vec { - self.0.encode().to_vec() + self.0.encode().0.to_vec() } pub fn verify(&self, nonce: i32, context: &[u8], signature: &Signature) -> bool { @@ -39,7 +46,7 @@ impl PublicKey { impl Signature { pub fn to_bytes(&self) -> Vec { - self.0.encode().to_vec() + self.0.encode().0.to_vec() } } diff --git a/server/crates/arbiter-server/src/crypto/integrity/v1/hashing.rs b/server/crates/arbiter-crypto/src/hashing.rs similarity index 83% rename from server/crates/arbiter-server/src/crypto/integrity/v1/hashing.rs rename to server/crates/arbiter-crypto/src/hashing.rs index ec1aa71..48b54ee 100644 --- a/server/crates/arbiter-server/src/crypto/integrity/v1/hashing.rs +++ b/server/crates/arbiter-crypto/src/hashing.rs @@ -1,21 +1,25 @@ -use hmac::digest::Digest; +pub use hmac::digest::Digest; use std::collections::HashSet; /// Deterministically hash a value by feeding its fields into the hasher in a consistent order. +#[diagnostic::on_unimplemented( + note = "for local types consider adding `#[derive(arbiter_macros::Hashable)]` to your `{Self}` type", + note = "for types from other crates check whether the crate offers a `Hashable` implementation" +)] pub trait Hashable { fn hash(&self, hasher: &mut H); } macro_rules! impl_numeric { -($($t:ty),*) => { - $( - impl Hashable for $t { - fn hash(&self, hasher: &mut H) { - hasher.update(&self.to_be_bytes()); + ($($t:ty),*) => { + $( + impl Hashable for $t { + fn hash(&self, hasher: &mut H) { + hasher.update(&self.to_be_bytes()); + } } - } - )* -}; + )* + }; } impl_numeric!(u8, u16, u32, u64, i8, i16, i32, i64); diff --git a/server/crates/arbiter-crypto/src/lib.rs b/server/crates/arbiter-crypto/src/lib.rs index 5015af2..b9c7503 100644 --- a/server/crates/arbiter-crypto/src/lib.rs +++ b/server/crates/arbiter-crypto/src/lib.rs @@ -1,5 +1,5 @@ #[cfg(feature = "authn")] pub mod authn; - +pub mod hashing; #[cfg(feature = "safecell")] pub mod safecell; diff --git a/server/crates/arbiter-macros/Cargo.toml b/server/crates/arbiter-macros/Cargo.toml new file mode 100644 index 0000000..210652b --- /dev/null +++ b/server/crates/arbiter-macros/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "arbiter-macros" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["derive", "fold", "full", "visit-mut"] } + +[dev-dependencies] +arbiter-crypto = { path = "../arbiter-crypto" } + +[lints] +workspace = true diff --git a/server/crates/arbiter-macros/src/hashable.rs b/server/crates/arbiter-macros/src/hashable.rs new file mode 100644 index 0000000..7473497 --- /dev/null +++ b/server/crates/arbiter-macros/src/hashable.rs @@ -0,0 +1,133 @@ +use proc_macro2::{Span, TokenStream, TokenTree}; +use quote::quote; +use syn::parse_quote; +use syn::spanned::Spanned; +use syn::{DataStruct, DeriveInput, Fields, Generics, Index}; + +use crate::utils::{HASHABLE_TRAIT_PATH, HMAC_DIGEST_PATH}; + +pub(crate) fn derive(input: &DeriveInput) -> TokenStream { + match &input.data { + syn::Data::Struct(struct_data) => hashable_struct(input, struct_data), + syn::Data::Enum(_) => { + syn::Error::new_spanned(input, "Hashable can currently be derived only for structs") + .to_compile_error() + } + syn::Data::Union(_) => { + syn::Error::new_spanned(input, "Hashable cannot be derived for unions") + .to_compile_error() + } + } +} + +fn hashable_struct(input: &DeriveInput, struct_data: &syn::DataStruct) -> TokenStream { + let ident = &input.ident; + let hashable_trait = HASHABLE_TRAIT_PATH.to_path(); + let hmac_digest = HMAC_DIGEST_PATH.to_path(); + let generics = add_hashable_bounds(input.generics.clone(), &hashable_trait); + let field_accesses = collect_field_accesses(struct_data); + let hash_calls = build_hash_calls(&field_accesses, &hashable_trait); + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl #impl_generics #hashable_trait for #ident #ty_generics #where_clause { + fn hash(&self, hasher: &mut H) { + #(#hash_calls)* + } + } + } +} + +fn add_hashable_bounds(mut generics: Generics, hashable_trait: &syn::Path) -> Generics { + for type_param in generics.type_params_mut() { + type_param.bounds.push(parse_quote!(#hashable_trait)); + } + + generics +} + +struct FieldAccess { + access: TokenStream, + span: Span, +} + +fn collect_field_accesses(struct_data: &DataStruct) -> Vec { + match &struct_data.fields { + Fields::Named(fields) => { + // Keep deterministic alphabetical order for named fields. + // Do not remove this sort, because it keeps hash output stable regardless of source order. + let mut named_fields = fields + .named + .iter() + .map(|field| { + let name = field + .ident + .as_ref() + .expect("Fields::Named(fields) must have names") + .clone(); + (name.to_string(), name) + }) + .collect::>(); + + named_fields.sort_by(|a, b| a.0.cmp(&b.0)); + + named_fields + .into_iter() + .map(|(_, name)| FieldAccess { + access: quote! { #name }, + span: name.span(), + }) + .collect() + } + Fields::Unnamed(fields) => fields + .unnamed + .iter() + .enumerate() + .map(|(i, field)| FieldAccess { + access: { + let index = Index::from(i); + quote! { #index } + }, + span: field.ty.span(), + }) + .collect(), + Fields::Unit => Vec::new(), + } +} + +fn build_hash_calls( + field_accesses: &[FieldAccess], + hashable_trait: &syn::Path, +) -> Vec { + field_accesses + .iter() + .map(|field| { + let access = &field.access; + let call = quote! { + #hashable_trait::hash(&self.#access, hasher); + }; + + respan(call, field.span) + }) + .collect() +} + +/// Recursively set span on all tokens, including interpolated ones. +fn respan(tokens: TokenStream, span: Span) -> TokenStream { + tokens + .into_iter() + .map(|tt| match tt { + TokenTree::Group(g) => { + let mut new = proc_macro2::Group::new(g.delimiter(), respan(g.stream(), span)); + new.set_span(span); + TokenTree::Group(new) + } + mut other => { + other.set_span(span); + other + } + }) + .collect() +} diff --git a/server/crates/arbiter-macros/src/lib.rs b/server/crates/arbiter-macros/src/lib.rs new file mode 100644 index 0000000..51b8f79 --- /dev/null +++ b/server/crates/arbiter-macros/src/lib.rs @@ -0,0 +1,10 @@ +use syn::{DeriveInput, parse_macro_input}; + +mod hashable; +mod utils; + +#[proc_macro_derive(Hashable)] +pub fn derive_hashable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + hashable::derive(&input).into() +} diff --git a/server/crates/arbiter-macros/src/utils.rs b/server/crates/arbiter-macros/src/utils.rs new file mode 100644 index 0000000..460aa94 --- /dev/null +++ b/server/crates/arbiter-macros/src/utils.rs @@ -0,0 +1,19 @@ +pub struct ToPath(pub &'static str); + +impl ToPath { + pub fn to_path(&self) -> syn::Path { + syn::parse_str(self.0).expect("Invalid path") + } +} + +macro_rules! ensure_path { + ($path:path) => {{ + #[cfg(test)] + #[expect(unused_imports)] + use $path as _; + ToPath(stringify!($path)) + }}; +} + +pub const HASHABLE_TRAIT_PATH: ToPath = ensure_path!(::arbiter_crypto::hashing::Hashable); +pub const HMAC_DIGEST_PATH: ToPath = ensure_path!(::arbiter_crypto::hashing::Digest); diff --git a/server/crates/arbiter-server/Cargo.toml b/server/crates/arbiter-server/Cargo.toml index c563ee0..636f564 100644 --- a/server/crates/arbiter-server/Cargo.toml +++ b/server/crates/arbiter-server/Cargo.toml @@ -18,6 +18,7 @@ diesel-async = { version = "0.8.0", features = [ ] } arbiter-proto.path = "../arbiter-proto" arbiter-crypto.path = "../arbiter-crypto" +arbiter-macros.path = "../arbiter-macros" tracing.workspace = true tracing-subscriber = { version = "0.3", features = ["env-filter"] } tonic.workspace = true @@ -44,7 +45,7 @@ restructed = "0.2.2" strum = { version = "0.28.0", features = ["derive"] } pem = "3.0.6" sha2.workspace = true -hmac = "0.12" +hmac.workspace = true spki.workspace = true alloy.workspace = true prost-types.workspace = true diff --git a/server/crates/arbiter-server/src/actors/client/mod.rs b/server/crates/arbiter-server/src/actors/client/mod.rs index 03b8861..13e5733 100644 --- a/server/crates/arbiter-server/src/actors/client/mod.rs +++ b/server/crates/arbiter-server/src/actors/client/mod.rs @@ -5,7 +5,7 @@ use tracing::{error, info}; use crate::{ actors::{GlobalActors, client::session::ClientSession}, - crypto::integrity::{Integrable, hashing::Hashable}, + crypto::integrity::Integrable, db, }; @@ -15,6 +15,7 @@ pub struct ClientProfile { pub metadata: ClientMetadata, } +#[derive(arbiter_macros::Hashable)] pub struct ClientCredentials { pub pubkey: authn::PublicKey, pub nonce: i32, @@ -24,13 +25,6 @@ impl Integrable for ClientCredentials { const KIND: &'static str = "client_credentials"; } -impl Hashable for ClientCredentials { - fn hash(&self, hasher: &mut H) { - hasher.update(self.pubkey.to_bytes()); - self.nonce.hash(hasher); - } -} - pub struct ClientConnection { pub(crate) db: db::DatabasePool, pub(crate) actors: GlobalActors, diff --git a/server/crates/arbiter-server/src/actors/user_agent/mod.rs b/server/crates/arbiter-server/src/actors/user_agent/mod.rs index ac571d9..5e87d23 100644 --- a/server/crates/arbiter-server/src/actors/user_agent/mod.rs +++ b/server/crates/arbiter-server/src/actors/user_agent/mod.rs @@ -5,7 +5,7 @@ use crate::{ }; use arbiter_crypto::authn; -#[derive(Debug)] +#[derive(Debug, arbiter_macros::Hashable)] pub struct UserAgentCredentials { pub pubkey: authn::PublicKey, pub nonce: i32, @@ -38,18 +38,3 @@ pub mod session; pub use auth::authenticate; pub use session::UserAgentSession; - -use crate::crypto::integrity::hashing::Hashable; - -impl Hashable for authn::PublicKey { - fn hash(&self, hasher: &mut H) { - hasher.update(self.to_bytes()); - } -} - -impl Hashable for UserAgentCredentials { - fn hash(&self, hasher: &mut H) { - self.pubkey.hash(hasher); - self.nonce.hash(hasher); - } -} diff --git a/server/crates/arbiter-server/src/crypto/integrity/v1.rs b/server/crates/arbiter-server/src/crypto/integrity/v1.rs index 4b67217..1b89df8 100644 --- a/server/crates/arbiter-server/src/crypto/integrity/v1.rs +++ b/server/crates/arbiter-server/src/crypto/integrity/v1.rs @@ -1,6 +1,6 @@ -use crate::{actors::keyholder, crypto::integrity::hashing::Hashable}; -use arbiter_crypto::safecell::SafeCellHandle as _; -use hmac::{Hmac, Mac as _}; +use crate::actors::keyholder; +use arbiter_crypto::hashing::Hashable; +use hmac::Hmac; use sha2::Sha256; use diesel::{ExpressionMethods as _, QueryDsl, dsl::insert_into, sqlite::Sqlite}; @@ -8,8 +8,6 @@ use diesel_async::{AsyncConnection, RunQueryDsl}; use kameo::{actor::ActorRef, error::SendError}; use sha2::Digest as _; -pub mod hashing; - use crate::{ actors::keyholder::{KeyHolder, SignIntegrity, VerifyIntegrity}, db::{ @@ -203,10 +201,6 @@ mod tests { use diesel::{ExpressionMethods as _, QueryDsl}; use diesel_async::RunQueryDsl; use kameo::{actor::ActorRef, prelude::Spawn}; - - use sha2::Digest; - - use crate::{ actors::keyholder::{Bootstrap, KeyHolder}, @@ -215,20 +209,11 @@ mod tests { use arbiter_crypto::safecell::{SafeCell, SafeCellHandle as _}; use super::{Error, Integrable, sign_entity, verify_entity}; - use super::hashing::Hashable; - - #[derive(Clone)] + #[derive(Clone, arbiter_macros::Hashable)] struct DummyEntity { payload_version: i32, payload: Vec, } - - impl Hashable for DummyEntity { - fn hash(&self, hasher: &mut H) { - self.payload_version.hash(hasher); - self.payload.hash(hasher); - } - } impl Integrable for DummyEntity { const KIND: &'static str = "dummy_entity"; } diff --git a/server/crates/arbiter-server/src/evm/policies.rs b/server/crates/arbiter-server/src/evm/policies.rs index 2ce22e2..828c52e 100644 --- a/server/crates/arbiter-server/src/evm/policies.rs +++ b/server/crates/arbiter-server/src/evm/policies.rs @@ -127,19 +127,19 @@ pub enum SpecificMeaning { TokenTransfer(token_transfers::Meaning), } -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, arbiter_macros::Hashable)] pub struct TransactionRateLimit { pub count: u32, pub window: Duration, } -#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, arbiter_macros::Hashable)] pub struct VolumeRateLimit { pub max_volume: U256, pub window: Duration, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, arbiter_macros::Hashable)] pub struct SharedGrantSettings { pub wallet_access_id: i32, pub chain: ChainId, @@ -200,7 +200,7 @@ pub enum SpecificGrant { TokenTransfer(token_transfers::Settings), } -#[derive(Debug)] +#[derive(Debug, arbiter_macros::Hashable)] pub struct CombinedSettings { pub shared: SharedGrantSettings, pub specific: PolicyGrant, @@ -219,38 +219,3 @@ impl Integrable for CombinedSettings

{ const KIND: &'static str = P::KIND; const VERSION: i32 = P::VERSION; } - -use crate::crypto::integrity::hashing::Hashable; - -impl Hashable for TransactionRateLimit { - fn hash(&self, hasher: &mut H) { - self.count.hash(hasher); - self.window.hash(hasher); - } -} - -impl Hashable for VolumeRateLimit { - fn hash(&self, hasher: &mut H) { - self.max_volume.hash(hasher); - self.window.hash(hasher); - } -} - -impl Hashable for SharedGrantSettings { - fn hash(&self, hasher: &mut H) { - self.wallet_access_id.hash(hasher); - self.chain.hash(hasher); - self.valid_from.hash(hasher); - self.valid_until.hash(hasher); - self.max_gas_fee_per_gas.hash(hasher); - self.max_priority_fee_per_gas.hash(hasher); - self.rate_limit.hash(hasher); - } -} - -impl Hashable for CombinedSettings

{ - fn hash(&self, hasher: &mut H) { - self.shared.hash(hasher); - self.specific.hash(hasher); - } -} diff --git a/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs b/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs index a641403..3fa507b 100644 --- a/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs +++ b/server/crates/arbiter-server/src/evm/policies/ether_transfer/mod.rs @@ -52,7 +52,7 @@ impl From for SpecificMeaning { } // A grant for ether transfers, which can be scoped to specific target addresses and volume limits -#[derive(Debug, Clone)] +#[derive(Debug, Clone, arbiter_macros::Hashable)] pub struct Settings { pub target: Vec

, pub limit: VolumeRateLimit, @@ -61,15 +61,6 @@ impl Integrable for Settings { const KIND: &'static str = "EtherTransfer"; } -use crate::crypto::integrity::hashing::Hashable; - -impl Hashable for Settings { - fn hash(&self, hasher: &mut H) { - self.target.hash(hasher); - self.limit.hash(hasher); - } -} - impl From for SpecificGrant { fn from(val: Settings) -> SpecificGrant { SpecificGrant::EtherTransfer(val) diff --git a/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs b/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs index 6549fb4..5253a25 100644 --- a/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs +++ b/server/crates/arbiter-server/src/evm/policies/ether_transfer/tests.rs @@ -340,7 +340,7 @@ proptest::proptest! { ) { use rand::{SeedableRng, seq::SliceRandom}; use sha2::Digest; - use crate::crypto::integrity::hashing::Hashable; + use arbiter_crypto::hashing::Hashable; let addrs: Vec
= raw_addrs.iter().map(|b| Address::from(*b)).collect(); let mut shuffled = addrs.clone(); diff --git a/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs b/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs index 49e03b6..f540c82 100644 --- a/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs +++ b/server/crates/arbiter-server/src/evm/policies/token_transfers/mod.rs @@ -62,7 +62,7 @@ impl From for SpecificMeaning { } // A grant for token transfers, which can be scoped to specific target addresses and volume limits -#[derive(Debug, Clone)] +#[derive(Debug, Clone, arbiter_macros::Hashable)] pub struct Settings { pub token_contract: Address, pub target: Option
, @@ -72,16 +72,6 @@ impl Integrable for Settings { const KIND: &'static str = "TokenTransfer"; } -use crate::crypto::integrity::hashing::Hashable; - -impl Hashable for Settings { - fn hash(&self, hasher: &mut H) { - self.token_contract.hash(hasher); - self.target.hash(hasher); - self.volume_limits.hash(hasher); - } -} - impl From for SpecificGrant { fn from(val: Settings) -> SpecificGrant { SpecificGrant::TokenTransfer(val) diff --git a/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs b/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs index 4f868e7..c059b0b 100644 --- a/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs +++ b/server/crates/arbiter-server/src/evm/policies/token_transfers/tests.rs @@ -419,7 +419,7 @@ proptest::proptest! { ) { use rand::{SeedableRng, seq::SliceRandom}; use sha2::Digest; - use crate::crypto::integrity::hashing::Hashable; + use arbiter_crypto::hashing::Hashable; let limits: Vec = raw_limits .iter()