Skip to content
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

lib: move Rayon wrappers to lib crate #380

Merged
merged 3 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion common/cf-solana/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ blake3.workspace = true
bs58.workspace = true
bytemuck.workspace = true
derive_more.workspace = true
rayon = { workspace = true, optional = true }
solana-program = { workspace = true, optional = true }

lib = { workspace = true, features = ["bs58"] }
Expand All @@ -27,6 +26,7 @@ solana-accounts-db2 = { package = "solana-accounts-db", git = "https://github.co
solana-program-2 = { package = "solana-program", git = "https://github.com/ComposableFi/mantis-solana.git", branch = "mantis/dev" }

[features]
rayon = ["lib/rayon"]
solana-program = [
"dep:solana-program",
"lib/solana-program",
Expand Down
46 changes: 46 additions & 0 deletions common/cf-solana/src/blake3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
pub use ::blake3::Hasher;

use crate::types::Hash;

/// Calculates Blake3 hash of given byte slice.
///
/// When `solana-program` or `solana-program-2` feature is enabled and
/// building a solana program, this is using Solana’s `sol_blake3` syscall.
/// Otherwise, the calculation is done by `blake3` crate.
#[allow(dead_code)]
pub fn hash(bytes: &[u8]) -> Hash {
if cfg!(target_os = "solana-program") &&
(cfg!(feature = "solana-program") ||
cfg!(feature = "solana-program-2"))
{
hashv(&[bytes])
} else {
Hash(::blake3::hash(bytes).into())
mina86 marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Calculates Blake3 hash of concatenation of given byte slices.
///
/// When `solana` or `solana2` feature is enabled and building a Solana
/// program, this is using Solana’s `sol_blake3` syscall. Otherwise, the
/// calculation is done by `blake3` crate.
#[allow(dead_code)]
pub fn hashv(slices: &[&[u8]]) -> Hash {
#[cfg(all(target_os = "solana-program", feature = "solana-program-2"))]
return Hash(solana_program_2::blake3::hashv(slices).0);
#[cfg(all(
target_os = "solana-program",
feature = "solana-program",
not(feature = "solana-program-2")
))]
return Hash(solana_program::blake3::hashv(slices).0);

#[allow(dead_code)]
{
let mut hasher = Hasher::default();
for bytes in slices {
hasher.update(bytes);
}
hasher.finalize().into()
}
}
2 changes: 1 addition & 1 deletion common/cf-solana/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ extern crate alloc;
#[cfg(any(feature = "std", test))]
extern crate std;

mod blake3;
pub mod proof;
pub mod types;
mod utils;
16 changes: 8 additions & 8 deletions common/cf-solana/src/proof.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use alloc::vec::Vec;

use lib::hash::CryptoHash;
#[cfg(all(feature = "rayon", not(miri)))]
use rayon::prelude::*;
use lib::par::prelude::*;

#[cfg(test)]
mod tests;

use crate::types::{Hash, PubKey};
use crate::utils::{blake3, chunks, sort_unstable_by};

/// The fanout of a accounts delta Merkle tree.
///
Expand Down Expand Up @@ -67,7 +65,7 @@ impl MerkleProof {
accounts: &mut [(PubKey, Hash)],
pubkey: &PubKey,
) -> Option<(Hash, MerkleProof)> {
sort_unstable_by(accounts, |a, b| a.0.cmp(&b.0));
lib::par::sort_unstable_by(accounts, |a, b| a.0.cmp(&b.0));

let pos =
accounts.binary_search_by_key(&pubkey, |item| &item.0).ok()?;
Expand Down Expand Up @@ -240,7 +238,9 @@ impl AccountHashData {
pub fn key(&self) -> &PubKey { self.get::<32>(self.0.len() - 32).into() }

/// Returns hash of the account.
pub fn calculate_hash(&self) -> Hash { blake3::hash(self.0.as_slice()) }
pub fn calculate_hash(&self) -> Hash {
crate::blake3::hash(self.0.as_slice())
}

/// Returns `N`-byte long fragment of the account’s hash data starting at
/// index `start`.
Expand Down Expand Up @@ -347,7 +347,7 @@ pub fn hash_account(
return Hash::default();
}

let mut hasher = blake3::Hasher::default();
let mut hasher = crate::blake3::Hasher::default();

// allocate a buffer on the stack that's big enough
// to hold a token account or a stake account
Expand Down Expand Up @@ -386,7 +386,7 @@ pub fn hash_account(
/// we reimplement it because that method takes ownership of hashes which is
/// something we need to keep.
fn compute_merkle_root(accounts: &mut [(PubKey, Hash)]) -> Hash {
let mut hashes: Vec<Hash> = chunks(accounts, MERKLE_FANOUT)
let mut hashes: Vec<Hash> = lib::par::chunks(accounts, MERKLE_FANOUT)
.map(|chunk| {
let mut hasher = CryptoHash::builder();
for item in chunk {
Expand Down Expand Up @@ -449,7 +449,7 @@ fn generate_merkle_proof(
}

fn compute_hashes_at_next_level(hashes: &[Hash]) -> Vec<Hash> {
chunks(hashes, MERKLE_FANOUT)
lib::par::chunks(hashes, MERKLE_FANOUT)
.map(|chunk| {
let mut hasher = CryptoHash::builder();
for hash in chunk {
Expand Down
92 changes: 0 additions & 92 deletions common/cf-solana/src/utils.rs

This file was deleted.

3 changes: 2 additions & 1 deletion common/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ edition = "2021"

[dependencies]
base64.workspace = true
bs58 = { workspace = true, optional = true }
borsh = { workspace = true, optional = true }
bs58 = { workspace = true, optional = true }
bytemuck = { workspace = true, features = ["derive"] }
derive_more.workspace = true
rayon = { workspace = true, optional = true }
sha2.workspace = true
solana-program = { workspace = true, optional = true }

Expand Down
1 change: 1 addition & 0 deletions common/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ extern crate alloc;
extern crate std;

pub mod hash;
pub mod par;
#[cfg(any(feature = "test_utils", test))]
pub mod test_utils;
pub mod u3;
71 changes: 71 additions & 0 deletions common/lib/src/par.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#[cfg(all(feature = "rayon", not(miri)))]
use rayon::prelude::*;

pub mod prelude {
#[cfg(all(feature = "rayon", not(miri)))]
pub use rayon::iter::ParallelIterator;
}

#[cfg(all(feature = "rayon", not(miri)))]
pub type Chunks<'a, T> = rayon::slice::Chunks<'a, T>;
#[cfg(any(not(feature = "rayon"), miri))]
pub type Chunks<'a, T> = core::slice::Chunks<'a, T>;

/// Splits array into `count`-element chunks.
///
/// It uses conditional compilation and either uses Rayon’s `par_chunks` method
/// (to allow parallelisation of the chunk processing) or standard `[T]::chunks`
/// method. Specifically, if `rayon` feature is enabled and not building Miri
/// tests, Rayon is used.
///
/// Note that depending on compilation features and target the function is
/// defined as returning `rayon::slice::Chunks` or `core::slice::Chunks`. types.
///
/// # Example
///
/// ```
/// use lib::par::prelude::*;
///
/// let chunks = lib::par::chunks(&[0, 1, 2, 3, 4], 3)
/// .map(|chunk| chunk.to_vec())
/// .collect::<Vec<Vec<u32>>>();
/// assert_eq!(&[vec![0, 1, 2], vec![3, 4]], chunks.as_slice());
/// ```
pub fn chunks<T: Sync>(arr: &[T], count: usize) -> Chunks<'_, T> {
#[cfg(all(feature = "rayon", not(miri)))]
return arr.par_chunks(count);
#[cfg(any(not(feature = "rayon"), miri))]
return arr.chunks(count);
}

#[test]
fn test_chunks() {
let got = chunks(&[1u32, 2, 3, 4, 5], 3)
.map(|chunk| (chunk.len(), chunk.iter().sum::<u32>()))
.collect::<alloc::vec::Vec<(usize, u32)>>();
assert_eq!(&[(3, 6), (2, 9)], got.as_slice());
}


/// Sorts elements of a slice using given comparator.
///
/// It uses conditional compilation and either uses Rayon’s
/// `par_sort_unstable_by` or standard `sort_unstable_by` method. Specifically,
/// if `rayon` feature is enabled and not building Miri tests, Rayon is used.
///
/// # Example
///
/// ```
/// let mut arr = [5, 4, 3, 1, 2, 3];
/// lib::par::sort_unstable_by(&mut arr[..], |a, b| a.cmp(b));
/// assert_eq!(&[1, 2, 3, 3, 4, 5], &arr[..]);
/// ```
pub fn sort_unstable_by<T: Send + Sync>(
arr: &mut [T],
cmp: impl (Fn(&T, &T) -> core::cmp::Ordering) + Sync,
) {
#[cfg(all(feature = "rayon", not(miri)))]
arr.par_sort_unstable_by(cmp);
#[cfg(any(not(feature = "rayon"), miri))]
arr.sort_unstable_by(cmp);
}
Loading