Skip to content

Commit 9826c51

Browse files
committed
feat(u): optimize create3 vanity miner
1 parent 0e22238 commit 9826c51

File tree

2 files changed

+121
-89
lines changed

2 files changed

+121
-89
lines changed

lib/unionlabs-primitives/src/lib.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
clippy::alloc_instead_of_core
66
)]
77

8-
use core::ptr::addr_of;
8+
use core::ptr::{addr_of, addr_of_mut};
99

1010
use crate::encoding::HexPrefixed;
1111

@@ -59,6 +59,26 @@ pub trait ByteArrayExt<const N: usize> {
5959
/// assert_eq!(arr.array_slice::<0, 2>(), [1, 2]);
6060
/// ```
6161
fn array_slice<const OFFSET: usize, const LEN: usize>(&self) -> [u8; LEN];
62+
63+
/// Slice into an array at `FROM..(FROM + LEN)`, returning a mutable reference to an array of length `LEN`. This will fail to compile if the equivalent slicing would panic at runtime.
64+
///
65+
/// ```compile_fail
66+
/// # use unionlabs_primitives::ByteArrayExt;
67+
/// let arr = [1, 2, 3, 4, 5];
68+
///
69+
/// // attempt to read `arr[4..(4 + 2)]`
70+
/// arr.array_slice_mut::<4, 2>();
71+
/// ```
72+
///
73+
/// ```rust
74+
/// # use unionlabs_primitives::ByteArrayExt;
75+
/// # let mut arr = [1, 2, 3, 4, 5];
76+
/// // checked at compile time!
77+
/// let new_arr: &mut [u8; 2] = arr.array_slice_mut::<0, 2>();
78+
/// *new_arr = [42_u8, 42];
79+
/// assert_eq!(arr, [42, 42, 3, 4, 5]);
80+
/// ```
81+
fn array_slice_mut<const OFFSET: usize, const LEN: usize>(&mut self) -> &mut [u8; LEN];
6282
}
6383

6484
impl<const N: usize> ByteArrayExt<N> for [u8; N] {
@@ -67,6 +87,12 @@ impl<const N: usize> ByteArrayExt<N> for [u8; N] {
6787

6888
unsafe { *addr_of!(self[OFFSET..(OFFSET + LEN)]).cast::<[u8; LEN]>() }
6989
}
90+
91+
fn array_slice_mut<const OFFSET: usize, const LEN: usize>(&mut self) -> &mut [u8; LEN] {
92+
const { assert!(OFFSET + LEN <= N) };
93+
94+
unsafe { &mut *addr_of_mut!(self[OFFSET..(OFFSET + LEN)]).cast::<[u8; LEN]>() }
95+
}
7096
}
7197

7298
#[test]
@@ -79,3 +105,21 @@ fn array_slice() {
79105
assert_eq!(arr.array_slice::<0, 0>(), [0; 0]);
80106
assert_eq!(arr.array_slice::<5, 0>(), [0; 0]);
81107
}
108+
109+
#[test]
110+
fn array_slice_mut() {
111+
let mut arr = [1, 2, 3, 4, 5];
112+
113+
assert_eq!(*arr.array_slice_mut::<0, 2>(), [1, 2]);
114+
assert_eq!(*arr.array_slice_mut::<1, 1>(), [2]);
115+
assert_eq!(*arr.array_slice_mut::<4, 1>(), [5]);
116+
assert_eq!(*arr.array_slice_mut::<0, 0>(), [0; 0]);
117+
assert_eq!(*arr.array_slice_mut::<5, 0>(), [0; 0]);
118+
119+
arr.array_slice_mut::<0, 2>()[0] = 255;
120+
assert_eq!(arr, [255, 2, 3, 4, 5]);
121+
arr.array_slice_mut::<1, 1>()[0] = 255;
122+
assert_eq!(arr, [255, 255, 3, 4, 5]);
123+
arr.array_slice_mut::<4, 1>()[0] = 255;
124+
assert_eq!(arr, [255, 255, 3, 4, 255]);
125+
}

tools/u/src/vanity/create3.rs

Lines changed: 76 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use alloy::{
1313
primitives::{keccak256, Address, U256},
1414
};
1515
use clap::Args;
16-
use sha2::{digest::generic_array::GenericArray, Digest};
16+
use sha2::digest::{generic_array::GenericArray, FixedOutputReset, Update};
1717
use unionlabs::{
18-
primitives::{H160, H256},
18+
primitives::{ByteArrayExt, H160, H256},
1919
typenum,
2020
};
2121

@@ -90,14 +90,16 @@ impl Cmd {
9090
})
9191
.unwrap_or((Vec::new(), None));
9292

93-
let mut salt_preimage = (<H160>::new(self.sender.into()).to_string() + "/")
93+
let mut salt_preimage: [u8; 42 + 1 + 32] = (<H160>::new(self.sender.into()).to_string()
94+
+ "/")
9495
.into_bytes()
9596
.into_iter()
9697
.chain([0; 32])
97-
.collect::<Vec<_>>();
98-
let range = (salt_preimage.len() - 32)..salt_preimage.len();
99-
98+
.collect::<Vec<_>>()
99+
.try_into()
100+
.unwrap();
100101
let seed = self.seed;
102+
let range = (salt_preimage.len() - 32)..salt_preimage.len();
101103

102104
let mut handles = Vec::new();
103105
for i in 0..self.threads {
@@ -106,112 +108,98 @@ impl Cmd {
106108
let deployer = self.deployer;
107109
let prefix_bytes = prefix_bytes.clone();
108110
let suffix_bytes = suffix_bytes.clone();
109-
let mut salt_preimage = salt_preimage.clone();
110-
let range = range.clone();
111111

112112
let handle = thread::spawn(move || -> Option<H256> {
113113
let mut local_attempts = 0u64;
114114

115115
let mut salt = (0..(i + 1)).fold(seed, |acc, _| {
116-
U256::from_be_bytes(sha2::Sha256::digest(acc.to_be_bytes::<32>()).into())
116+
U256::from_be_bytes(
117+
<sha2::Sha256 as sha2::Digest>::digest(acc.to_be_bytes::<32>()).into(),
118+
)
117119
});
118120
println!("{i}: {salt}");
119121

122+
*salt_preimage.array_slice_mut::<{ 42 + 1 }, 32>() = salt.to_be_bytes();
123+
124+
let mut counter =
125+
u64::from_be_bytes(salt.to_be_bytes::<32>().array_slice::<0, 8>());
126+
127+
let mut proxy_preimage: [u8; 1 + 20 + 32 + 32] = [0xff]
128+
.into_iter()
129+
.chain(deployer)
130+
.chain(salt.to_be_bytes::<32>())
131+
.chain(hex!(
132+
"21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f"
133+
))
134+
.collect::<Vec<_>>()
135+
.try_into()
136+
.unwrap();
137+
138+
let mut hasher = <keccak_asm::Keccak256 as sha2::Digest>::new();
139+
140+
let mut out: GenericArray<u8, typenum::U32> = [0_u8; 32].into();
141+
120142
while !found.load(Ordering::Relaxed) {
121-
loop {
122-
salt += U256::ONE;
123-
salt_preimage[range.clone()].copy_from_slice(&salt.to_be_bytes::<32>());
124-
125-
let addr = {
126-
let salt = keccak_asm::Keccak256::digest(&salt_preimage);
127-
let mut out: GenericArray<u8, typenum::U32> = [0_u8; 32].into();
128-
keccak_asm::Keccak256::new()
129-
.chain_update([0xff])
130-
.chain_update(deployer)
131-
.chain_update(salt)
132-
.chain_update(hex!("21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f"))
133-
.finalize_into(&mut out);
134-
135-
let mut address_out: GenericArray<u8, typenum::U32> = [0_u8; 32].into();
136-
keccak_asm::Keccak256::new()
137-
.chain_update([0xd6, 0x94])
138-
.chain_update(&out[12..])
139-
.chain_update([0x01])
140-
.finalize_into(&mut address_out);
141-
142-
Address::from_slice(&address_out[12..])
143-
};
143+
'inner: while local_attempts < 100000 {
144+
*salt_preimage.array_slice_mut::<{ 42 + 1 }, 8>() = counter.to_be_bytes();
144145

145-
let address_bytes = addr.as_slice();
146+
<_ as Update>::update(&mut hasher, &salt_preimage);
147+
<_ as FixedOutputReset>::finalize_into_reset(
148+
&mut hasher,
149+
proxy_preimage.array_slice_mut::<21, 32>().into(),
150+
);
146151

147-
let matches_prefix = if prefix_bytes.is_empty() && prefix_nibble.is_none() {
148-
true
149-
} else {
150-
let full_bytes_match = if prefix_bytes.is_empty() {
151-
true
152-
} else if address_bytes.len() < prefix_bytes.len() {
153-
false
154-
} else {
155-
address_bytes[..prefix_bytes.len()] == prefix_bytes[..]
156-
};
152+
<_ as Update>::update(&mut hasher, &proxy_preimage);
153+
<_ as FixedOutputReset>::finalize_into_reset(&mut hasher, &mut out);
154+
155+
<_ as Update>::update(&mut hasher, &[0xd6, 0x94]);
156+
<_ as Update>::update(&mut hasher, &out[12..]);
157+
<_ as Update>::update(&mut hasher, &[0x01]);
158+
<_ as FixedOutputReset>::finalize_into_reset(&mut hasher, &mut out);
159+
160+
let addr_bytes = &out[12..];
157161

162+
let matches_prefix = {
158163
if let Some(nibble) = prefix_nibble {
159-
if address_bytes.len() <= prefix_bytes.len() {
160-
false
161-
} else {
162-
let byte_to_check = address_bytes[prefix_bytes.len()];
163-
let high_nibble = (byte_to_check >> 4) & 0xF;
164-
full_bytes_match && high_nibble == nibble
165-
}
164+
let bytes_match = addr_bytes[..prefix_bytes.len()] == prefix_bytes;
165+
bytes_match
166+
&& ((addr_bytes[prefix_bytes.len()] >> 4) & 0xF == nibble)
166167
} else {
167-
full_bytes_match
168+
addr_bytes[..prefix_bytes.len()] == prefix_bytes
168169
}
169170
};
170171

171-
let matches_suffix =
172-
if suffix_bytes.is_empty() && suffix_leading_nibble.is_none() {
173-
true
174-
} else if let Some(leading_nibble) = suffix_leading_nibble {
175-
let required_bytes = suffix_bytes.len() + 1; // +1 for the nibble
176-
if address_bytes.len() < required_bytes {
177-
false
178-
} else {
179-
let suffix_start = address_bytes.len() - suffix_bytes.len();
180-
let bytes_match = if suffix_bytes.is_empty() {
181-
true
182-
} else {
183-
address_bytes[suffix_start..] == suffix_bytes[..]
184-
};
185-
186-
let nibble_byte_index =
187-
address_bytes.len() - suffix_bytes.len() - 1;
188-
let byte_with_nibble = address_bytes[nibble_byte_index];
189-
let low_nibble = byte_with_nibble & 0xF;
190-
let nibble_matches = low_nibble == leading_nibble;
191-
192-
bytes_match && nibble_matches
193-
}
194-
} else if address_bytes.len() < suffix_bytes.len() {
195-
false
196-
} else {
197-
let suffix_start = address_bytes.len() - suffix_bytes.len();
198-
address_bytes[suffix_start..] == suffix_bytes[..]
199-
};
172+
let matches_suffix = || {
173+
if let Some(leading_nibble) = suffix_leading_nibble {
174+
let bytes_match =
175+
addr_bytes[20 - suffix_bytes.len()..] == suffix_bytes;
200176

201-
local_attempts += 1;
177+
bytes_match
178+
&& (addr_bytes[20 - suffix_bytes.len() - 1] & 0xF
179+
== leading_nibble)
180+
} else {
181+
addr_bytes[20 - suffix_bytes.len()..] == suffix_bytes
182+
}
183+
};
202184

203-
if matches_prefix && matches_suffix {
185+
if matches_prefix && matches_suffix() {
186+
let salt_bytes =
187+
H256::new(salt_preimage.array_slice::<{ 42 + 1 }, 32>());
204188
found.store(true, Ordering::Relaxed);
205189
total_attempts.fetch_add(local_attempts, Ordering::Relaxed);
206-
return Some(salt.to_be_bytes::<32>().into());
207-
}
190+
println!("Salt: {}", salt_bytes);
191+
println!("Address: {}", <H160>::try_from(addr_bytes).unwrap());
192+
return Some(salt_bytes);
193+
} else {
194+
counter = counter.wrapping_add(1);
195+
local_attempts += 1;
208196

209-
if local_attempts % 200000 == 0 {
210-
total_attempts.fetch_add(200000, Ordering::Relaxed);
211-
local_attempts = 0;
212-
break;
197+
continue 'inner;
213198
}
214199
}
200+
201+
total_attempts.fetch_add(local_attempts, Ordering::Relaxed);
202+
local_attempts = 0;
215203
}
216204

217205
total_attempts.fetch_add(local_attempts, Ordering::Relaxed);

0 commit comments

Comments
 (0)