Skip to content

Commit

Permalink
feat(lab): mine-udc-salt command
Browse files Browse the repository at this point in the history
Only an inefficient single-core CPU miner for now.
  • Loading branch information
xJonathanLEI committed Jul 19, 2023
1 parent b58907e commit 0f3c74f
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ enum Subcommands {
//
#[clap(about = "Generate shell completions script")]
Completions(Completions),
//
// Experimental
//
#[clap(
about = "Experimental commands for fun and profit",
long_about = "Experimental new commands that are shipped with no stability guarantee. \
They might break or be removed anytime."
)]
Lab(Lab),
}

#[tokio::main]
Expand Down Expand Up @@ -138,5 +147,6 @@ async fn run_command(cli: Cli) -> Result<()> {
Subcommands::Declare(cmd) => cmd.run().await,
Subcommands::Deploy(cmd) => cmd.run().await,
Subcommands::Completions(cmd) => cmd.run(),
Subcommands::Lab(cmd) => cmd.run(),
}
}
215 changes: 215 additions & 0 deletions src/subcommands/lab/mine_udc_salt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
use std::time::SystemTime;

use anyhow::Result;
use clap::Parser;
use colored::Colorize;
use starknet::core::{
crypto::{compute_hash_on_elements, pedersen_hash},
types::FieldElement,
utils::{normalize_address, UdcUniqueSettings, UdcUniqueness},
};

/// The default UDC address: 0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf.
const DEFAULT_UDC_ADDRESS: FieldElement = FieldElement::from_mont([
15144800532519055890,
15685625669053253235,
9333317513348225193,
121672436446604875,
]);

// Cairo string of "STARKNET_CONTRACT_ADDRESS"
const CONTRACT_ADDRESS_PREFIX: FieldElement = FieldElement::from_mont([
3829237882463328880,
17289941567720117366,
8635008616843941496,
533439743893157637,
]);

#[derive(Debug, Parser)]
pub struct MineUdcSalt {
#[clap(
long,
help = "Prefix bits in BINARY representation, ASSUMING 252-BIT ADDRESSES"
)]
prefix: String,
#[clap(long, help = "Suffix bits in BINARY representation")]
suffix: String,
#[clap(long, help = "Do not derive contract address from deployer address")]
not_unique: bool,
#[clap(
long,
help = "Deployer address. Needed if and only if not using --no-unique"
)]
deployer_address: Option<FieldElement>,
#[clap(help = "Class hash")]
class_hash: FieldElement,
#[clap(help = "Raw constructor arguments (argument resolution not supported yet)")]
ctor_args: Vec<FieldElement>,
}

impl MineUdcSalt {
pub fn run(self) -> Result<()> {
let udc_uniqueness = match (self.not_unique, self.deployer_address) {
(true, Some(_)) => {
anyhow::bail!("--deployer-address must not be used when --not-unique is on");
}
(false, None) => {
anyhow::bail!("--deployer-address must be used when --not-unique is off");
}
(true, None) => UdcUniqueness::NotUnique,
(false, Some(deployer_address)) => {
eprintln!(
"{}",
"WARNING: mining without --not-unique is slower. \
Try using --no-unique instead \
(you need to also use this option for the deploy command)."
.bright_magenta()
);

UdcUniqueness::Unique(UdcUniqueSettings {
deployer_address,
udc_contract_address: DEFAULT_UDC_ADDRESS,
})
}
};

if self.prefix.len() > 252 {
anyhow::bail!("invalid prefix length");
}
if self.suffix.len() > 252 {
anyhow::bail!("invalid suffix length");
}

let prefix_bits = self
.suffix
.chars()
.rev()
.map(|bit| match bit {
'1' => Ok(true),
'0' => Ok(false),
_ => anyhow::bail!("invalid bit: {}", bit),
})
.collect::<Result<Vec<_>>>()?;
let suffix_bits = self
.prefix
.chars()
.rev()
.map(|bit| match bit {
'1' => Ok(true),
'0' => Ok(false),
_ => anyhow::bail!("invalid bit: {}", bit),
})
.collect::<Result<Vec<_>>>()?;

let prefix_len = prefix_bits.len();
let suffix_len = suffix_bits.len();

let mut bloom = [false; 252];
bloom[..prefix_len].copy_from_slice(&prefix_bits);
bloom[(252 - suffix_len)..].copy_from_slice(&suffix_bits);

if Self::validate_bloom(&bloom).is_err() {
anyhow::bail!("prefix/suffix out of range and impossible to mine");
}

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 address_bits = deployed_address.to_bits_le();

if Self::validate_address(&address_bits[..252], &bloom, prefix_len, suffix_len) {
break deployed_address;
}

nonce += FieldElement::ONE;
};

let end_time = SystemTime::now();

let duration = end_time.duration_since(start_time)?;

println!(
"Time spent: {}",
format!("{}s", duration.as_secs()).bright_yellow()
);

println!("Salt: {}", format!("{:#064x}", nonce).bright_yellow());
println!(
"Address: {}",
format!("{:#064x}", resulting_address).bright_yellow()
);

Ok(())
}

fn validate_bloom(bloom: &[bool]) -> Result<()> {
let mut bloom_256 = [false; 256];
bloom_256[..252].copy_from_slice(bloom);
bloom_256.reverse();

let bytes = bloom_256
.chunks_exact(8)
.map(|bits| {
(if bits[0] { 128u8 } else { 0 })
+ (if bits[1] { 64u8 } else { 0 })
+ (if bits[2] { 32u8 } else { 0 })
+ (if bits[3] { 16u8 } else { 0 })
+ (if bits[4] { 8u8 } else { 0 })
+ (if bits[5] { 4u8 } else { 0 })
+ (if bits[6] { 2u8 } else { 0 })
+ (if bits[7] { 1u8 } else { 0 })
})
.collect::<Vec<_>>();

FieldElement::from_byte_slice_be(&bytes)?;

Ok(())
}

#[inline(always)]
fn validate_address(
address: &[bool],
bloom: &[bool],
prefix_len: usize,
suffix_len: usize,
) -> bool {
for ind in 0..prefix_len {
unsafe {
if address.get_unchecked(ind) != bloom.get_unchecked(ind) {
return false;
}
}
}

for ind in (252 - suffix_len)..252 {
unsafe {
if address.get_unchecked(ind) != bloom.get_unchecked(ind) {
return false;
}
}
}

true
}
}
25 changes: 25 additions & 0 deletions src/subcommands/lab/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use anyhow::Result;
use clap::{Parser, Subcommand};

mod mine_udc_salt;
use mine_udc_salt::MineUdcSalt;

#[derive(Debug, Parser)]
pub struct Lab {
#[clap(subcommand)]
command: Subcommands,
}

#[derive(Debug, Subcommand)]
enum Subcommands {
#[clap(about = "Mine UDC contract deployment salt for specific address prefix and/or suffix")]
MineUdcSalt(MineUdcSalt),
}

impl Lab {
pub fn run(self) -> Result<()> {
match self.command {
Subcommands::MineUdcSalt(cmd) => cmd.run(),
}
}
}
3 changes: 3 additions & 0 deletions src/subcommands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,6 @@ pub use call::Call;

mod invoke;
pub use invoke::Invoke;

mod lab;
pub use lab::Lab;

0 comments on commit 0f3c74f

Please sign in to comment.