diff --git a/Cargo.lock b/Cargo.lock index 712123e..9330651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,6 +910,40 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -1831,6 +1865,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -2245,6 +2288,28 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2794,6 +2859,7 @@ dependencies = [ "log", "num-bigint", "num-integer", + "rayon", "regex", "rpassword", "serde", diff --git a/Cargo.toml b/Cargo.toml index cbdb25a..7de25ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ hex-literal = "0.4.1" log = "0.4.19" num-bigint = "0.4.3" num-integer = "0.1.45" +rayon = "1.7.0" regex = "1.8.4" rpassword = "7.2.0" serde = { version = "1.0.164", features = ["derive"] } diff --git a/src/subcommands/lab/mine_udc_salt.rs b/src/subcommands/lab/mine_udc_salt.rs index d1f63e5..c25486d 100644 --- a/src/subcommands/lab/mine_udc_salt.rs +++ b/src/subcommands/lab/mine_udc_salt.rs @@ -1,8 +1,15 @@ -use std::time::SystemTime; +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::SystemTime, +}; use anyhow::Result; use clap::Parser; use colored::Colorize; +use rayon::prelude::*; use starknet::core::{ crypto::{compute_hash_on_elements, pedersen_hash}, types::FieldElement, @@ -41,12 +48,31 @@ pub struct MineUdcSalt { help = "Deployer address. Needed if and only if not using --no-unique" )] deployer_address: Option, + #[clap(long, default_value = "1", help = "The number of parallel jobs to run")] + jobs: u32, #[clap(help = "Class hash")] class_hash: FieldElement, #[clap(help = "Raw constructor arguments (argument resolution not supported yet)")] ctor_args: Vec, } +struct Miner { + udc_uniqueness: UdcUniqueness, + class_hash: FieldElement, + ctor_hash: FieldElement, + bloom: [bool; 252], + prefix_length: usize, + suffix_length: usize, + start_nonce: FieldElement, + cancellation_token: Arc, +} + +#[derive(Debug)] +struct MineResult { + nonce: FieldElement, + deployed_address: FieldElement, +} + impl MineUdcSalt { pub fn run(self) -> Result<()> { let udc_uniqueness = match (self.not_unique, self.deployer_address) { @@ -114,36 +140,31 @@ impl MineUdcSalt { let ctor_hash = compute_hash_on_elements(&self.ctor_args); - let mut nonce = FieldElement::ZERO; - let start_time = SystemTime::now(); - // TODO: parallelize with rayon - let resulting_address = loop { - let (effective_salt, effective_deployer) = match &udc_uniqueness { - UdcUniqueness::NotUnique => (nonce, FieldElement::ZERO), - UdcUniqueness::Unique(settings) => ( - pedersen_hash(&settings.deployer_address, &nonce), - settings.udc_contract_address, - ), - }; - - let deployed_address = normalize_address(compute_hash_on_elements(&[ - CONTRACT_ADDRESS_PREFIX, - effective_deployer, - effective_salt, - self.class_hash, - ctor_hash, - ])); + let cancellation_token = Arc::new(AtomicBool::new(false)); - let address_bits = deployed_address.to_bits_le(); + let result = (0..self.jobs) + .into_par_iter() + .map(|job_id| { + let start_nonce = + FieldElement::MAX.floor_div(self.jobs.into()) * FieldElement::from(job_id); - if Self::validate_address(&address_bits[..252], &bloom, prefix_len, suffix_len) { - break deployed_address; - } + let miner = Miner { + udc_uniqueness: udc_uniqueness.clone(), + class_hash: self.class_hash, + ctor_hash, + bloom, + prefix_length: prefix_len, + suffix_length: suffix_len, + start_nonce, + cancellation_token: cancellation_token.clone(), + }; - nonce += FieldElement::ONE; - }; + miner.mine() + }) + .find_map_any(|result| result.ok()) + .expect("at least one job should return success"); let end_time = SystemTime::now(); @@ -154,10 +175,13 @@ impl MineUdcSalt { format!("{}s", duration.as_secs()).bright_yellow() ); - println!("Salt: {}", format!("{:#064x}", nonce).bright_yellow()); + println!( + "Salt: {}", + format!("{:#064x}", result.nonce).bright_yellow() + ); println!( "Address: {}", - format!("{:#064x}", resulting_address).bright_yellow() + format!("{:#064x}", result.deployed_address).bright_yellow() ); Ok(()) @@ -186,6 +210,49 @@ impl MineUdcSalt { Ok(()) } +} + +impl Miner { + fn mine(&self) -> Result { + let bloom = self.bloom; + let prefix_len = self.prefix_length; + let suffix_len = self.suffix_length; + + let mut nonce = self.start_nonce; + + while !self.cancellation_token.load(Ordering::Relaxed) { + let (effective_salt, effective_deployer) = match &self.udc_uniqueness { + UdcUniqueness::NotUnique => (nonce, FieldElement::ZERO), + UdcUniqueness::Unique(settings) => ( + pedersen_hash(&settings.deployer_address, &nonce), + settings.udc_contract_address, + ), + }; + + let deployed_address = normalize_address(compute_hash_on_elements(&[ + CONTRACT_ADDRESS_PREFIX, + effective_deployer, + effective_salt, + self.class_hash, + self.ctor_hash, + ])); + + let address_bits = deployed_address.to_bits_le(); + + if Self::validate_address(&address_bits[..252], &bloom, prefix_len, suffix_len) { + self.cancellation_token.store(true, Ordering::Relaxed); + + return Ok(MineResult { + nonce, + deployed_address, + }); + } + + nonce += FieldElement::ONE; + } + + Err(anyhow::anyhow!("job cancelled")) + } #[inline(always)] fn validate_address(