Skip to content

Commit 0f3c74f

Browse files
committed
feat(lab): mine-udc-salt command
Only an inefficient single-core CPU miner for now.
1 parent b58907e commit 0f3c74f

File tree

4 files changed

+253
-0
lines changed

4 files changed

+253
-0
lines changed

src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ enum Subcommands {
100100
//
101101
#[clap(about = "Generate shell completions script")]
102102
Completions(Completions),
103+
//
104+
// Experimental
105+
//
106+
#[clap(
107+
about = "Experimental commands for fun and profit",
108+
long_about = "Experimental new commands that are shipped with no stability guarantee. \
109+
They might break or be removed anytime."
110+
)]
111+
Lab(Lab),
103112
}
104113

105114
#[tokio::main]
@@ -138,5 +147,6 @@ async fn run_command(cli: Cli) -> Result<()> {
138147
Subcommands::Declare(cmd) => cmd.run().await,
139148
Subcommands::Deploy(cmd) => cmd.run().await,
140149
Subcommands::Completions(cmd) => cmd.run(),
150+
Subcommands::Lab(cmd) => cmd.run(),
141151
}
142152
}

src/subcommands/lab/mine_udc_salt.rs

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
use std::time::SystemTime;
2+
3+
use anyhow::Result;
4+
use clap::Parser;
5+
use colored::Colorize;
6+
use starknet::core::{
7+
crypto::{compute_hash_on_elements, pedersen_hash},
8+
types::FieldElement,
9+
utils::{normalize_address, UdcUniqueSettings, UdcUniqueness},
10+
};
11+
12+
/// The default UDC address: 0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf.
13+
const DEFAULT_UDC_ADDRESS: FieldElement = FieldElement::from_mont([
14+
15144800532519055890,
15+
15685625669053253235,
16+
9333317513348225193,
17+
121672436446604875,
18+
]);
19+
20+
// Cairo string of "STARKNET_CONTRACT_ADDRESS"
21+
const CONTRACT_ADDRESS_PREFIX: FieldElement = FieldElement::from_mont([
22+
3829237882463328880,
23+
17289941567720117366,
24+
8635008616843941496,
25+
533439743893157637,
26+
]);
27+
28+
#[derive(Debug, Parser)]
29+
pub struct MineUdcSalt {
30+
#[clap(
31+
long,
32+
help = "Prefix bits in BINARY representation, ASSUMING 252-BIT ADDRESSES"
33+
)]
34+
prefix: String,
35+
#[clap(long, help = "Suffix bits in BINARY representation")]
36+
suffix: String,
37+
#[clap(long, help = "Do not derive contract address from deployer address")]
38+
not_unique: bool,
39+
#[clap(
40+
long,
41+
help = "Deployer address. Needed if and only if not using --no-unique"
42+
)]
43+
deployer_address: Option<FieldElement>,
44+
#[clap(help = "Class hash")]
45+
class_hash: FieldElement,
46+
#[clap(help = "Raw constructor arguments (argument resolution not supported yet)")]
47+
ctor_args: Vec<FieldElement>,
48+
}
49+
50+
impl MineUdcSalt {
51+
pub fn run(self) -> Result<()> {
52+
let udc_uniqueness = match (self.not_unique, self.deployer_address) {
53+
(true, Some(_)) => {
54+
anyhow::bail!("--deployer-address must not be used when --not-unique is on");
55+
}
56+
(false, None) => {
57+
anyhow::bail!("--deployer-address must be used when --not-unique is off");
58+
}
59+
(true, None) => UdcUniqueness::NotUnique,
60+
(false, Some(deployer_address)) => {
61+
eprintln!(
62+
"{}",
63+
"WARNING: mining without --not-unique is slower. \
64+
Try using --no-unique instead \
65+
(you need to also use this option for the deploy command)."
66+
.bright_magenta()
67+
);
68+
69+
UdcUniqueness::Unique(UdcUniqueSettings {
70+
deployer_address,
71+
udc_contract_address: DEFAULT_UDC_ADDRESS,
72+
})
73+
}
74+
};
75+
76+
if self.prefix.len() > 252 {
77+
anyhow::bail!("invalid prefix length");
78+
}
79+
if self.suffix.len() > 252 {
80+
anyhow::bail!("invalid suffix length");
81+
}
82+
83+
let prefix_bits = self
84+
.suffix
85+
.chars()
86+
.rev()
87+
.map(|bit| match bit {
88+
'1' => Ok(true),
89+
'0' => Ok(false),
90+
_ => anyhow::bail!("invalid bit: {}", bit),
91+
})
92+
.collect::<Result<Vec<_>>>()?;
93+
let suffix_bits = self
94+
.prefix
95+
.chars()
96+
.rev()
97+
.map(|bit| match bit {
98+
'1' => Ok(true),
99+
'0' => Ok(false),
100+
_ => anyhow::bail!("invalid bit: {}", bit),
101+
})
102+
.collect::<Result<Vec<_>>>()?;
103+
104+
let prefix_len = prefix_bits.len();
105+
let suffix_len = suffix_bits.len();
106+
107+
let mut bloom = [false; 252];
108+
bloom[..prefix_len].copy_from_slice(&prefix_bits);
109+
bloom[(252 - suffix_len)..].copy_from_slice(&suffix_bits);
110+
111+
if Self::validate_bloom(&bloom).is_err() {
112+
anyhow::bail!("prefix/suffix out of range and impossible to mine");
113+
}
114+
115+
let ctor_hash = compute_hash_on_elements(&self.ctor_args);
116+
117+
let mut nonce = FieldElement::ZERO;
118+
119+
let start_time = SystemTime::now();
120+
121+
// TODO: parallelize with rayon
122+
let resulting_address = loop {
123+
let (effective_salt, effective_deployer) = match &udc_uniqueness {
124+
UdcUniqueness::NotUnique => (nonce, FieldElement::ZERO),
125+
UdcUniqueness::Unique(settings) => (
126+
pedersen_hash(&settings.deployer_address, &nonce),
127+
settings.udc_contract_address,
128+
),
129+
};
130+
131+
let deployed_address = normalize_address(compute_hash_on_elements(&[
132+
CONTRACT_ADDRESS_PREFIX,
133+
effective_deployer,
134+
effective_salt,
135+
self.class_hash,
136+
ctor_hash,
137+
]));
138+
139+
let address_bits = deployed_address.to_bits_le();
140+
141+
if Self::validate_address(&address_bits[..252], &bloom, prefix_len, suffix_len) {
142+
break deployed_address;
143+
}
144+
145+
nonce += FieldElement::ONE;
146+
};
147+
148+
let end_time = SystemTime::now();
149+
150+
let duration = end_time.duration_since(start_time)?;
151+
152+
println!(
153+
"Time spent: {}",
154+
format!("{}s", duration.as_secs()).bright_yellow()
155+
);
156+
157+
println!("Salt: {}", format!("{:#064x}", nonce).bright_yellow());
158+
println!(
159+
"Address: {}",
160+
format!("{:#064x}", resulting_address).bright_yellow()
161+
);
162+
163+
Ok(())
164+
}
165+
166+
fn validate_bloom(bloom: &[bool]) -> Result<()> {
167+
let mut bloom_256 = [false; 256];
168+
bloom_256[..252].copy_from_slice(bloom);
169+
bloom_256.reverse();
170+
171+
let bytes = bloom_256
172+
.chunks_exact(8)
173+
.map(|bits| {
174+
(if bits[0] { 128u8 } else { 0 })
175+
+ (if bits[1] { 64u8 } else { 0 })
176+
+ (if bits[2] { 32u8 } else { 0 })
177+
+ (if bits[3] { 16u8 } else { 0 })
178+
+ (if bits[4] { 8u8 } else { 0 })
179+
+ (if bits[5] { 4u8 } else { 0 })
180+
+ (if bits[6] { 2u8 } else { 0 })
181+
+ (if bits[7] { 1u8 } else { 0 })
182+
})
183+
.collect::<Vec<_>>();
184+
185+
FieldElement::from_byte_slice_be(&bytes)?;
186+
187+
Ok(())
188+
}
189+
190+
#[inline(always)]
191+
fn validate_address(
192+
address: &[bool],
193+
bloom: &[bool],
194+
prefix_len: usize,
195+
suffix_len: usize,
196+
) -> bool {
197+
for ind in 0..prefix_len {
198+
unsafe {
199+
if address.get_unchecked(ind) != bloom.get_unchecked(ind) {
200+
return false;
201+
}
202+
}
203+
}
204+
205+
for ind in (252 - suffix_len)..252 {
206+
unsafe {
207+
if address.get_unchecked(ind) != bloom.get_unchecked(ind) {
208+
return false;
209+
}
210+
}
211+
}
212+
213+
true
214+
}
215+
}

src/subcommands/lab/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use anyhow::Result;
2+
use clap::{Parser, Subcommand};
3+
4+
mod mine_udc_salt;
5+
use mine_udc_salt::MineUdcSalt;
6+
7+
#[derive(Debug, Parser)]
8+
pub struct Lab {
9+
#[clap(subcommand)]
10+
command: Subcommands,
11+
}
12+
13+
#[derive(Debug, Subcommand)]
14+
enum Subcommands {
15+
#[clap(about = "Mine UDC contract deployment salt for specific address prefix and/or suffix")]
16+
MineUdcSalt(MineUdcSalt),
17+
}
18+
19+
impl Lab {
20+
pub fn run(self) -> Result<()> {
21+
match self.command {
22+
Subcommands::MineUdcSalt(cmd) => cmd.run(),
23+
}
24+
}
25+
}

src/subcommands/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,6 @@ pub use call::Call;
7575

7676
mod invoke;
7777
pub use invoke::Invoke;
78+
79+
mod lab;
80+
pub use lab::Lab;

0 commit comments

Comments
 (0)