diff --git a/Cargo.lock b/Cargo.lock index aa89012..5df2874 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,12 +125,20 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "deadbeef" -version = "0.1.0" +version = "0.1.1" dependencies = [ "clap", + "deadbeef-core", "hex", - "hex-literal", "num_cpus", +] + +[[package]] +name = "deadbeef-core" +version = "0.1.0" +dependencies = [ + "hex", + "hex-literal", "rand", "tiny-keccak", ] diff --git a/Cargo.toml b/Cargo.toml index e53061d..cbd245a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,5 @@ -[package] -name = "deadbeef" -version = "0.1.0" -edition = "2021" -publish = false -license = "GPL-3.0-or-later" - -[dependencies] -clap = { version = "4", features = ["derive"] } -hex = "0.4" -hex-literal = "0.4" -num_cpus = "1" -rand = { version = "0.8", features = ["small_rng"] } -tiny-keccak = { version = "2", features = ["keccak"] } +[workspace] +members = [ + "cli", + "core", +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..fc338a1 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "deadbeef" +version = "0.1.1" +edition = "2021" +publish = false +license = "GPL-3.0-or-later" + +[dependencies] +clap = { version = "4", features = ["derive"] } +deadbeef-core = { version = "0.1.0", path = "../core" } +hex = "0.4" +num_cpus = "1" diff --git a/src/chain.rs b/cli/src/chain.rs similarity index 69% rename from src/chain.rs rename to cli/src/chain.rs index 278490e..7d81686 100644 --- a/src/chain.rs +++ b/cli/src/chain.rs @@ -1,7 +1,6 @@ //! Module for chain-specific data. -use crate::{address::address, safe::Contracts}; -use hex_literal::hex; +use deadbeef_core::{address, hex, Contracts}; use std::{ fmt::{self, Display, Formatter}, num::ParseIntError, @@ -80,3 +79,46 @@ impl FromStr for Chain { Ok(Self(value)) } } + +#[cfg(test)] +mod tests { + use super::*; + use deadbeef_core::Safe; + + #[test] + fn proxy_init_code_digest() { + assert_eq!( + Chain::default() + .contracts() + .unwrap() + .proxy_init_code_digest(), + hex!("76733d705f71b79841c0ee960a0ca880f779cde7ef446c989e6d23efc0a4adfb"), + ); + } + + #[test] + fn compute_address() { + // + let mut safe = Safe::new( + Chain::ethereum().contracts().unwrap(), + vec![ + address!("34f845773D4364999f2fbC7AA26ABDeE902cBb46"), + address!("E2Df39d8c1c393BDe653D96a09852508CA2816e5"), + address!("000000000dD7Bc0bcCE4392698dc3e11004F20eB"), + address!("Cbd6073f486714E6641bf87c22A9CEc25aCf5804"), + ], + 2, + ); + safe.update_salt_nonce(|n| { + n.copy_from_slice(&hex!( + "c437564b491906978ae4396733fbc0835f87e6b2578193331caa87645ebe9bdc" + )) + }); + + let address = safe.creation_address(); + assert_eq!( + address, + address!("000000000034065b3a94C2118CFe5B4C0067B615") + ); + } +} diff --git a/src/main.rs b/cli/src/main.rs similarity index 87% rename from src/main.rs rename to cli/src/main.rs index 793fcba..7ff83c1 100644 --- a/src/main.rs +++ b/cli/src/main.rs @@ -1,14 +1,9 @@ -mod address; mod chain; -mod create2; -mod safe; -use crate::{address::Address, safe::Safe}; -use chain::Chain; +use self::chain::Chain; use clap::Parser; +use deadbeef_core::{Address, Contracts, Safe}; use hex::FromHexError; -use rand::{rngs::SmallRng, Rng as _, SeedableRng as _}; -use safe::{Contracts, Info}; use std::{process, str::FromStr, sync::mpsc, thread}; /// Generate vanity addresses for Safe deployments. @@ -103,7 +98,10 @@ fn main() { let safe = Safe::new(contracts.clone(), args.owners.clone(), args.threshold); let prefix = args.prefix.0.clone(); let result = sender.clone(); - move || search_vanity_safe(safe, &prefix, result) + move || { + let safe = deadbeef_core::search(safe, &prefix); + let _ = result.send(safe); + } }) }) .collect::>(); @@ -128,13 +126,3 @@ fn main() { let _ = threads; process::exit(0); } - -fn search_vanity_safe(mut safe: Safe, prefix: &[u8], result: mpsc::Sender) { - let mut rng = SmallRng::from_entropy(); - - while !safe.creation_address().0.starts_with(prefix) { - safe.update_salt_nonce(|n| rng.fill(n)); - } - - let _ = result.send(safe.info()); -} diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..3001db7 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "deadbeef-core" +version = "0.1.0" +edition = "2021" +publish = false +license = "GPL-3.0-or-later" + +[dependencies] +hex = "0.4" +hex-literal = "0.4" +rand = { version = "0.8", features = ["small_rng"] } +tiny-keccak = { version = "2", features = ["keccak"] } diff --git a/src/address.rs b/core/src/address.rs similarity index 96% rename from src/address.rs rename to core/src/address.rs index 8de4e93..26afab1 100644 --- a/src/address.rs +++ b/core/src/address.rs @@ -72,14 +72,13 @@ impl FromStr for Address { } } +#[macro_export] macro_rules! address { ($s:literal) => { - $crate::address::Address(::hex_literal::hex!($s)) + $crate::Address($crate::hex!($s)) }; } -pub(crate) use address; - #[cfg(test)] mod tests { use super::*; diff --git a/src/create2.rs b/core/src/create2.rs similarity index 98% rename from src/create2.rs rename to core/src/create2.rs index 98c3bf3..43c6913 100644 --- a/src/create2.rs +++ b/core/src/create2.rs @@ -45,7 +45,6 @@ impl Create2 { #[cfg(test)] mod tests { use super::*; - use crate::address::address; use hex_literal::hex; #[test] diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 0000000..2ee4aa0 --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,20 @@ +#[macro_use] +mod address; +mod create2; +mod safe; + +pub use self::{ + address::Address, + safe::{Contracts, Safe, Transaction}, +}; +pub use hex_literal::hex; +use rand::{rngs::SmallRng, Rng as _, SeedableRng as _}; + +/// For the specified Safe parameters +pub fn search(mut safe: Safe, prefix: &[u8]) -> Transaction { + let mut rng = SmallRng::from_entropy(); + while !safe.creation_address().0.starts_with(prefix) { + safe.update_salt_nonce(|n| rng.fill(n)); + } + safe.transaction() +} diff --git a/src/safe.rs b/core/src/safe.rs similarity index 65% rename from src/safe.rs rename to core/src/safe.rs index 3f6db56..3bb68a9 100644 --- a/src/safe.rs +++ b/core/src/safe.rs @@ -28,7 +28,8 @@ pub struct Contracts { } /// Safe deployment transaction information. -pub struct Info { +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Transaction { /// The final address that the safe will end up on. pub creation_address: Address, /// The address of the proxy factory for deploying the Safe. @@ -53,7 +54,11 @@ impl Safe { hasher.update(&contracts.initializer(&owners, threshold)); hasher.finalize(&mut salt[0..32]); - let mut create2 = contracts.create2(); + let mut create2 = Create2::new( + contracts.proxy_factory, + Default::default(), + contracts.proxy_init_code_digest(), + ); let mut hasher = Keccak::v256(); hasher.update(&salt); hasher.finalize(create2.salt_mut()); @@ -88,11 +93,11 @@ impl Safe { } /// Returns the transaction information for the current safe deployment. - pub fn info(self) -> Info { + pub fn transaction(self) -> Transaction { let calldata = self.contracts .proxy_calldata(&self.owners, self.threshold, self.salt_nonce()); - Info { + Transaction { creation_address: self.creation_address(), factory: self.contracts.proxy_factory, singleton: self.contracts.singleton, @@ -106,7 +111,7 @@ impl Safe { impl Contracts { /// Returns the proxy init code digest. - fn proxy_init_code_digest(&self) -> [u8; 32] { + pub fn proxy_init_code_digest(&self) -> [u8; 32] { let mut output = [0_u8; 32]; let mut hasher = Keccak::v256(); hasher.update(&self.proxy_init_code); @@ -115,17 +120,8 @@ impl Contracts { output } - /// Returns the [`Create2`] instance associated with these contracts. - fn create2(&self) -> Create2 { - Create2::new( - self.proxy_factory, - Default::default(), - self.proxy_init_code_digest(), - ) - } - /// Computes the initializer calldata for the specified Safe parameters. - fn initializer(&self, owners: &[Address], threshold: usize) -> Vec { + pub fn initializer(&self, owners: &[Address], threshold: usize) -> Vec { use abi::*; let mut buffer = Vec::new(); @@ -147,7 +143,7 @@ impl Contracts { } /// Returns the calldata required for the transaction to deploy the proxy. - fn proxy_calldata( + pub fn proxy_calldata( &self, owners: &[Address], threshold: usize, @@ -188,12 +184,18 @@ mod abi { #[cfg(test)] mod tests { use super::*; - use crate::{address::address, chain::Chain}; #[test] fn initializer_bytes() { + let contracts = Contracts { + proxy_factory: address!("1111111111111111111111111111111111111111"), + proxy_init_code: vec![], + singleton: address!("2222222222222222222222222222222222222222"), + fallback_handler: address!("3333333333333333333333333333333333333333"), + }; + assert_eq!( - &Chain::ethereum().contracts().unwrap().initializer( + &contracts.initializer( &[ address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), @@ -207,7 +209,7 @@ mod tests { 0000000000000000000000000000000000000000000000000000000000000002 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000180 - 000000000000000000000000fd0732dc9e303f09fcef3a7388ad10a83459ec99 + 0000000000000000000000003333333333333333333333333333333333333333 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 @@ -221,39 +223,59 @@ mod tests { } #[test] - fn proxy_init_code_digest() { - assert_eq!( - Chain::default() - .contracts() - .unwrap() - .proxy_init_code_digest(), - hex!("76733d705f71b79841c0ee960a0ca880f779cde7ef446c989e6d23efc0a4adfb"), - ); - } - - #[test] - fn compute_address() { - // + fn transaction() { let mut safe = Safe::new( - Chain::ethereum().contracts().unwrap(), + Contracts { + proxy_factory: address!("1111111111111111111111111111111111111111"), + proxy_init_code: vec![], + singleton: address!("2222222222222222222222222222222222222222"), + fallback_handler: address!("3333333333333333333333333333333333333333"), + }, vec![ - address!("34f845773D4364999f2fbC7AA26ABDeE902cBb46"), - address!("E2Df39d8c1c393BDe653D96a09852508CA2816e5"), - address!("000000000dD7Bc0bcCE4392698dc3e11004F20eB"), - address!("Cbd6073f486714E6641bf87c22A9CEc25aCf5804"), + address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + address!("cccccccccccccccccccccccccccccccccccccccc"), ], 2, ); - safe.update_salt_nonce(|n| { - n.copy_from_slice(&hex!( - "c437564b491906978ae4396733fbc0835f87e6b2578193331caa87645ebe9bdc" - )) - }); + safe.update_salt_nonce(|nonce| nonce.fill(0xee)); - let address = safe.creation_address(); assert_eq!( - address, - address!("000000000034065b3a94C2118CFe5B4C0067B615") + safe.transaction(), + Transaction { + creation_address: address!("cDa7814460beF6D0BF5dc1b34AB29605b36c3bC4"), + factory: address!("1111111111111111111111111111111111111111"), + singleton: address!("2222222222222222222222222222222222222222"), + owners: vec![ + address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + address!("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + address!("cccccccccccccccccccccccccccccccccccccccc"), + ], + threshold: 2, + fallback_handler: address!("3333333333333333333333333333333333333333"), + calldata: hex!( + "1688f0b9 + 0000000000000000000000002222222222222222222222222222222222222222 + 0000000000000000000000000000000000000000000000000000000000000060 + eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + 00000000000000000000000000000000000000000000000000000000000001a4 + b63e800d00000000000000000000000000000000000000000000000000000000 + 0000010000000000000000000000000000000000000000000000000000000000 + 0000000200000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000018000000000000000000000000033333333333333333333333333333333 + 3333333300000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000003000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaa000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + bbbbbbbb000000000000000000000000cccccccccccccccccccccccccccccccc + cccccccc00000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000" + ) + .to_vec(), + } ); } }