mirror of
https://github.com/tcsenpai/ethereum-vanity-address-generator-cuda.git
synced 2025-06-02 16:50:08 +00:00
working commit
This commit is contained in:
commit
6cf7c775de
9
.env
Normal file
9
.env
Normal file
@ -0,0 +1,9 @@
|
||||
# Enable balance checking (true/false)
|
||||
CHECK_BALANCES=true
|
||||
# RPC URL for Ethereum node
|
||||
RPC_URL=https://eth.llamarpc.com
|
||||
# How many addresses to check in each batch
|
||||
BALANCE_BATCH_SIZE=100
|
||||
# Desired address prefix
|
||||
PREFIX=dEAD000000000000000042069420694206942069
|
||||
SYNC_MODE=false # Set to true for synchronous balance checking
|
81
README.md
Normal file
81
README.md
Normal file
@ -0,0 +1,81 @@
|
||||
# Ethereum Vanity Address Generator with CUDA
|
||||
|
||||
A high-performance Ethereum vanity address generator that uses CUDA GPU acceleration to quickly generate Ethereum addresses matching a desired prefix pattern. It also includes optional balance checking functionality across multiple RPC endpoints.
|
||||
|
||||
## Features
|
||||
|
||||
- 🚀 CUDA GPU acceleration for fast address generation
|
||||
- 🔍 Configurable prefix matching with support for hex characters
|
||||
- 💰 Optional balance checking across multiple RPC endpoints
|
||||
- ⚡ Batch processing for efficient balance checks
|
||||
- 🔄 Automatic RPC failover and rate limit handling
|
||||
- 📊 Real-time status updates and progress tracking
|
||||
- 💾 Automatic saving of addresses with balances to file
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.7+
|
||||
- CUDA-capable NVIDIA GPU
|
||||
- PyCUDA
|
||||
- Web3.py
|
||||
- Other dependencies listed in requirements.txt
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone https://github.com/tcsenpai/eth-vanity-address-generator-cuda.git
|
||||
```
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Configure settings in `.env` file:
|
||||
```bash
|
||||
# Enable balance checking (true/false)
|
||||
CHECK_BALANCES=true
|
||||
# RPC URL for Ethereum node
|
||||
RPC_URL=https://eth.llamarpc.com
|
||||
# How many addresses to check in each batch
|
||||
BALANCE_BATCH_SIZE=100
|
||||
# Desired address prefix
|
||||
PREFIX=dEAD000000000000000042069420694206942069
|
||||
SYNC_MODE=false # Set to true for synchronous balance checking
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. Run the script:
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
2. Enter your desired address prefix when prompted, or configure it in the `.env` file.
|
||||
|
||||
The script will begin generating addresses and checking balances if enabled. Status updates are printed every 10 seconds showing:
|
||||
|
||||
- Time elapsed
|
||||
- Total attempts
|
||||
- Generation speed
|
||||
- Balance check status
|
||||
- Best match found so far
|
||||
|
||||
When a matching address is found, it will be displayed along with its private key.
|
||||
|
||||
## Configuration
|
||||
|
||||
The following settings can be configured in the `.env` file:
|
||||
|
||||
- `CHECK_BALANCES`: Enable/disable balance checking
|
||||
- `RPC_URL`: Ethereum RPC endpoint URL
|
||||
- `BALANCE_BATCH_SIZE`: Number of addresses to check in each batch
|
||||
- `PREFIX`: Target address prefix
|
||||
- `SYNC_MODE`: Use synchronous or asynchronous balance checking
|
||||
|
||||
## Tips
|
||||
|
||||
- Longer prefixes will take exponentially more time to find
|
||||
- Consider using shorter prefixes for testing
|
||||
- Multiple RPC endpoints are used for redundancy
|
||||
- Found addresses with balances are saved to `found.txt`
|
452
main.py
Normal file
452
main.py
Normal file
@ -0,0 +1,452 @@
|
||||
import pycuda.autoinit
|
||||
import pycuda.driver as cuda
|
||||
from pycuda.compiler import SourceModule
|
||||
import numpy as np
|
||||
from eth_keys import keys
|
||||
from eth_utils import to_checksum_address
|
||||
import time
|
||||
import dotenv
|
||||
import os
|
||||
from web3 import Web3, AsyncWeb3
|
||||
import asyncio
|
||||
from collections import deque
|
||||
import aiohttp
|
||||
import concurrent.futures
|
||||
from threading import Lock
|
||||
|
||||
cuda.init()
|
||||
|
||||
dotenv.load_dotenv()
|
||||
|
||||
# Add near the top after imports
|
||||
RPC_URLS = [
|
||||
"https://eth.llamarpc.com",
|
||||
"https://rpc.ankr.com/eth",
|
||||
"https://ethereum.publicnode.com",
|
||||
"https://1rpc.io/eth",
|
||||
]
|
||||
|
||||
# Add new configuration from .env
|
||||
CHECK_BALANCES = os.getenv("CHECK_BALANCES", "false").lower() == "true"
|
||||
BALANCE_BATCH_SIZE = int(os.getenv("BALANCE_BATCH_SIZE", "100"))
|
||||
SYNC_MODE = os.getenv("SYNC_MODE", "false").lower() == "true"
|
||||
BATCH_SIZE = int(os.getenv("BATCH_SIZE", "500")) # Number of addresses to check at once
|
||||
|
||||
# Create a round-robin RPC selector
|
||||
rpc_index = 0
|
||||
|
||||
# Add near the top after other global variables
|
||||
last_balance_check = {"address": None, "balance": None, "rpc": None}
|
||||
|
||||
|
||||
def get_next_web3():
|
||||
global rpc_index
|
||||
web3 = Web3(Web3.HTTPProvider(RPC_URLS[rpc_index]))
|
||||
rpc_index = (rpc_index + 1) % len(RPC_URLS)
|
||||
return web3
|
||||
|
||||
|
||||
# Create a queue for addresses to check
|
||||
address_queue = deque()
|
||||
|
||||
# Add to imports and configuration
|
||||
print_lock = Lock()
|
||||
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
|
||||
pending_checks = []
|
||||
|
||||
# Add this near the top of the file, after imports
|
||||
cuda_code = """
|
||||
#include <curand_kernel.h>
|
||||
|
||||
__device__ __forceinline__ void generate_private_key(unsigned char *out, const int idx, curandState *state) {
|
||||
// Generate 32 bytes (256 bits) for the private key using vectorized operations
|
||||
uint4 *out_vec = (uint4*)&out[idx * 32];
|
||||
|
||||
// Generate 4 random 32-bit values using CUDA's optimized RNG
|
||||
uint4 rand_val;
|
||||
rand_val.x = curand(state);
|
||||
rand_val.y = curand(state);
|
||||
rand_val.z = curand(state);
|
||||
rand_val.w = curand(state);
|
||||
|
||||
// Store using vector operation (more efficient than byte-by-byte)
|
||||
*out_vec = rand_val;
|
||||
}
|
||||
|
||||
extern "C" __global__ void generate_random(unsigned char *out, const int n) {
|
||||
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
|
||||
if (idx >= n) return;
|
||||
|
||||
// Initialize CUDA RNG state
|
||||
curandState state;
|
||||
curand_init(clock64() + idx, 0, 0, &state);
|
||||
|
||||
// Generate private key using vectorized operations
|
||||
generate_private_key(out, idx, &state);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def check_single_balance(address, private_key):
|
||||
"""Non-blocking balance check with RPC failover"""
|
||||
for _ in range(len(RPC_URLS)): # Try each RPC once
|
||||
try:
|
||||
web3 = get_next_web3()
|
||||
balance = web3.eth.get_balance(address)
|
||||
# Update last balance check info
|
||||
global last_balance_check
|
||||
last_balance_check = {
|
||||
"address": address,
|
||||
"balance": Web3.from_wei(balance, "ether"),
|
||||
"rpc": web3.provider.endpoint_uri
|
||||
}
|
||||
if balance > 0:
|
||||
with print_lock:
|
||||
print(f"\n{'='*50}")
|
||||
print(f"🔥 Found address with balance!")
|
||||
print(f"Address: {address}")
|
||||
print(f"Balance: {Web3.from_wei(balance, 'ether')} ETH")
|
||||
print(f"Private key: {private_key}")
|
||||
print(f"{'='*50}\n")
|
||||
with open("found.txt", "a") as f:
|
||||
f.write(f"{address} {private_key}\n")
|
||||
return balance
|
||||
except Exception as e:
|
||||
if "429" in str(e): # Rate limit error
|
||||
with print_lock:
|
||||
print(f"⚠️ Rate limit hit on {web3.provider.endpoint_uri}: {str(e)}")
|
||||
time.sleep(1) # Back off a bit
|
||||
else:
|
||||
with print_lock:
|
||||
print(f"⚠️ Error on {web3.provider.endpoint_uri}, trying next RPC: {str(e)}")
|
||||
continue
|
||||
|
||||
with print_lock:
|
||||
print(f"❌ All RPCs failed to check balance for {address}")
|
||||
return None
|
||||
|
||||
|
||||
async def generate_vanity_address(prefix, num_attempts=0):
|
||||
mod = SourceModule(
|
||||
cuda_code, no_extern_c=True, include_dirs=["/usr/local/cuda/include"]
|
||||
)
|
||||
generate_random = mod.get_function("generate_random")
|
||||
|
||||
# Use up to 4GB of GPU memory
|
||||
max_memory = 4 * 1024 * 1024 * 1024 # 4GB in bytes
|
||||
max_batch = max_memory // 32 # Each key is 32 bytes
|
||||
current_batch = min(max_batch, 4000000) # Start with 4 million addresses
|
||||
|
||||
# Create pinned memory directly instead of converting
|
||||
private_keys = cuda.pagelocked_empty((current_batch, 32), dtype=np.uint8)
|
||||
try:
|
||||
gpu_private_keys = cuda.mem_alloc(private_keys.nbytes)
|
||||
except cuda.LogicError as e:
|
||||
print(
|
||||
f"⚠️ GPU memory allocation failed with batch size {current_batch}. Trying smaller batch..."
|
||||
)
|
||||
current_batch = current_batch // 2
|
||||
private_keys = cuda.pagelocked_empty((current_batch, 32), dtype=np.uint8)
|
||||
gpu_private_keys = cuda.mem_alloc(private_keys.nbytes)
|
||||
|
||||
# Optimize thread configuration for RTX 4060
|
||||
block_size = 1024 # Maximum threads per block
|
||||
grid_size = (current_batch + block_size - 1) // block_size
|
||||
|
||||
print(f"\n🔍 Starting search for prefix: {prefix}")
|
||||
print(
|
||||
f"💾 Batch size: {current_batch:,} addresses ({private_keys.nbytes / 1024 / 1024:.1f} MB)"
|
||||
)
|
||||
print(f"🧮 Grid size: {grid_size} blocks × {block_size} threads")
|
||||
print(f"💡 This might take a while depending on prefix length...")
|
||||
|
||||
start_time = time.time()
|
||||
total_attempts = 0
|
||||
prefixes_checked = 0
|
||||
best_match = {"address": None, "similarity": 0, "private_key": None}
|
||||
last_status_time = time.time()
|
||||
total_balance_checks = 0
|
||||
active_checks = 0
|
||||
|
||||
def check_callback(future):
|
||||
nonlocal active_checks, total_balance_checks
|
||||
active_checks -= 1
|
||||
total_balance_checks += 1
|
||||
|
||||
address_batch = []
|
||||
private_key_batch = []
|
||||
|
||||
while True:
|
||||
generate_random(
|
||||
gpu_private_keys,
|
||||
np.int32(current_batch),
|
||||
block=(block_size, 1, 1),
|
||||
grid=(grid_size, 1),
|
||||
)
|
||||
|
||||
cuda.memcpy_dtoh(private_keys, gpu_private_keys)
|
||||
total_attempts += current_batch
|
||||
|
||||
# Process addresses without waiting for balance checks
|
||||
for priv_key in private_keys:
|
||||
priv_key_hex = "".join(format(x, "02x") for x in priv_key)
|
||||
|
||||
try:
|
||||
private_key = keys.PrivateKey(bytes.fromhex(priv_key_hex))
|
||||
public_key = private_key.public_key
|
||||
address = to_checksum_address(public_key.to_address())
|
||||
addr_without_prefix = address[2:].lower()
|
||||
|
||||
# Check for exact match first (fast path)
|
||||
if addr_without_prefix.startswith(prefix.lower()):
|
||||
# Found a match! Queue balance check if needed
|
||||
if CHECK_BALANCES:
|
||||
address_batch.append(address)
|
||||
private_key_batch.append(priv_key_hex)
|
||||
return private_key, address
|
||||
|
||||
# Track best partial match
|
||||
similarity = calculate_similarity(addr_without_prefix, prefix.lower())
|
||||
if similarity > best_match["similarity"]:
|
||||
best_match = {
|
||||
"address": address,
|
||||
"similarity": similarity,
|
||||
"private_key": priv_key_hex,
|
||||
}
|
||||
print(
|
||||
f"🎯 Best match so far: {best_match['address']} ({best_match['similarity']} chars)"
|
||||
)
|
||||
# Immediately check for balance if best match without waiting
|
||||
ethBalance = check_single_balance(address, priv_key_hex)
|
||||
print(f"💰 Balance: {ethBalance} ETH")
|
||||
# Queue balance check if enabled (without waiting)
|
||||
if CHECK_BALANCES:
|
||||
address_batch.append(address)
|
||||
private_key_batch.append(priv_key_hex)
|
||||
if len(address_batch) >= BATCH_SIZE:
|
||||
# Schedule balance check without awaiting
|
||||
asyncio.create_task(
|
||||
batch_check_balances(
|
||||
address_batch.copy(), private_key_batch.copy()
|
||||
)
|
||||
)
|
||||
address_batch.clear()
|
||||
private_key_batch.clear()
|
||||
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
# Status update every 10 seconds
|
||||
current_time = time.time()
|
||||
if current_time - last_status_time >= 10:
|
||||
elapsed_time = current_time - start_time
|
||||
speed = total_attempts / elapsed_time
|
||||
prefix_speed = prefixes_checked / elapsed_time
|
||||
print(f"\n{'='*30} Status Update {'='*30}")
|
||||
print(f"⏱️ Time elapsed: {elapsed_time:.1f}s")
|
||||
print(f"🔢 Attempts: {total_attempts:,}")
|
||||
print(f"🔍 Prefixes checked: {prefixes_checked:,}")
|
||||
print(f"⚡ Speed: {speed:,.2f} addr/s")
|
||||
print(f"🚀 Prefix check speed: {prefix_speed:,.2f} prefixes/s")
|
||||
if CHECK_BALANCES:
|
||||
print(f"💰 Balance checks queued: {len(address_batch)}")
|
||||
print(f"✓ Total balance checks: {total_balance_checks:,}")
|
||||
if last_balance_check["address"]:
|
||||
print(
|
||||
f"📊 Last check: {last_balance_check['address']} - {last_balance_check['balance']} ETH"
|
||||
)
|
||||
print(
|
||||
f"🎯 Best match so far: {best_match['address']} ({best_match['similarity']} chars)"
|
||||
)
|
||||
print(f"{'='*72}\n")
|
||||
last_status_time = current_time
|
||||
|
||||
prefixes_checked += 1 # we checked a key
|
||||
|
||||
# Only break if num_attempts is positive
|
||||
if num_attempts > 0 and total_attempts >= num_attempts:
|
||||
print(f"\n⚠️ Reached maximum attempts: {num_attempts:,}")
|
||||
break
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def suggest_leet_alternatives(text):
|
||||
# Only include leet mappings that result in valid hex characters
|
||||
leet_map = {
|
||||
"a": "4",
|
||||
"e": "3",
|
||||
"i": "1",
|
||||
"o": "0",
|
||||
"s": "5",
|
||||
"b": "8",
|
||||
# Removed 't':'7' and 'g':'9' as they're not valid hex
|
||||
}
|
||||
alternatives = [text]
|
||||
|
||||
# Generate variations
|
||||
for char, leet_char in leet_map.items():
|
||||
for existing in alternatives.copy():
|
||||
if char in existing.lower():
|
||||
alternatives.append(existing.lower().replace(char, leet_char))
|
||||
|
||||
# Filter to ensure only valid hex strings are suggested
|
||||
valid_chars = set("0123456789abcdefABCDEF")
|
||||
valid_alternatives = [
|
||||
alt for alt in alternatives if all(c in valid_chars for c in alt)
|
||||
]
|
||||
|
||||
return list(set(valid_alternatives))[:5] # Return up to 5 unique suggestions
|
||||
|
||||
|
||||
def sanitize_prefix(prefix):
|
||||
# Remove '0x' if present
|
||||
prefix = prefix.replace("0x", "")
|
||||
|
||||
# Check for valid hex characters
|
||||
valid_chars = set("0123456789abcdefABCDEF")
|
||||
if not all(c in valid_chars for c in prefix):
|
||||
invalid_chars = [c for c in prefix if c not in valid_chars]
|
||||
suggestions = suggest_leet_alternatives(prefix)
|
||||
raise ValueError(
|
||||
f"Invalid characters in prefix: {invalid_chars}\n"
|
||||
f"Prefix must only contain hex characters (0-9, a-f)\n"
|
||||
f"Try these leet alternatives: {', '.join(suggestions)}"
|
||||
)
|
||||
|
||||
return prefix.lower()
|
||||
|
||||
|
||||
def calculate_similarity(address, prefix):
|
||||
"""Count matching characters from start of address with prefix"""
|
||||
for i, (a, p) in enumerate(zip(address, prefix)):
|
||||
if a != p:
|
||||
return i
|
||||
return len(prefix)
|
||||
|
||||
|
||||
async def batch_check_balances(addresses, private_keys):
|
||||
"""Check multiple balances in a single RPC call with failover"""
|
||||
for _ in range(len(RPC_URLS)): # Try each RPC once
|
||||
try:
|
||||
web3 = get_next_web3()
|
||||
|
||||
# Create batch payload
|
||||
payload = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": [addr, "latest"],
|
||||
"id": i
|
||||
}
|
||||
for i, addr in enumerate(addresses)
|
||||
]
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(web3.provider.endpoint_uri, json=payload) as response:
|
||||
results = await response.json()
|
||||
|
||||
# Process results
|
||||
for result, address, private_key in zip(results, addresses, private_keys):
|
||||
if "result" in result:
|
||||
balance = int(result["result"], 16) # Convert hex to int
|
||||
# Update last balance check info
|
||||
global last_balance_check
|
||||
last_balance_check = {
|
||||
"address": address,
|
||||
"balance": Web3.from_wei(balance, "ether"),
|
||||
"rpc": web3.provider.endpoint_uri
|
||||
}
|
||||
if balance > 0:
|
||||
with print_lock:
|
||||
print(f"\n{'='*50}")
|
||||
print(f"🔥 Found address with balance!")
|
||||
print(f"Address: {address}")
|
||||
print(f"Balance: {Web3.from_wei(balance, 'ether')} ETH")
|
||||
print(f"Private key: {private_key}")
|
||||
print(f"{'='*50}\n")
|
||||
with open("found.txt", "a") as f:
|
||||
f.write(f"{address} {private_key}\n")
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
if "429" in str(e): # Rate limit error
|
||||
with print_lock:
|
||||
print(f"⚠️ Rate limit hit on {web3.provider.endpoint_uri}: {str(e)}")
|
||||
time.sleep(1) # Back off a bit
|
||||
else:
|
||||
with print_lock:
|
||||
print(f"⚠️ Batch check error on {web3.provider.endpoint_uri}, trying next RPC: {str(e)}")
|
||||
continue
|
||||
|
||||
with print_lock:
|
||||
print(f"❌ All RPCs failed for batch balance check of {len(addresses)} addresses")
|
||||
return None
|
||||
|
||||
|
||||
async def determine_optimal_batch_size(rpc_url):
|
||||
print("🔍 Testing RPC batch size limits...")
|
||||
optimal_size = 100 # Default fallback
|
||||
|
||||
test_sizes = [2000, 1000, 500, 100]
|
||||
for batch_size in test_sizes:
|
||||
try:
|
||||
web3 = Web3(Web3.HTTPProvider(rpc_url))
|
||||
addresses = ["0x" + "0" * 40] * batch_size
|
||||
|
||||
payload = [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "eth_getBalance",
|
||||
"params": [addr, "latest"],
|
||||
"id": i,
|
||||
}
|
||||
for i, addr in enumerate(addresses)
|
||||
]
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
web3.provider.endpoint_uri, json=payload
|
||||
) as response:
|
||||
results = await response.json()
|
||||
if isinstance(results, list) and len(results) == batch_size:
|
||||
optimal_size = batch_size
|
||||
print(f"✅ Found optimal batch size: {optimal_size}")
|
||||
return optimal_size
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Batch size {batch_size} failed: {str(e)}")
|
||||
continue
|
||||
|
||||
print(f"ℹ️ Using conservative batch size: {optimal_size}")
|
||||
return optimal_size
|
||||
|
||||
|
||||
async def main():
|
||||
prefix = os.getenv("PREFIX")
|
||||
if not prefix:
|
||||
prefix = input("Enter desired address prefix (without 0x): ")
|
||||
|
||||
try:
|
||||
prefix = sanitize_prefix(prefix)
|
||||
private_key, address = await generate_vanity_address(prefix)
|
||||
|
||||
# Cleanup thread pool
|
||||
executor.shutdown(wait=False)
|
||||
|
||||
return private_key, address
|
||||
|
||||
except ValueError as e:
|
||||
print(f"Error: {e}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
SUPPORTED_SIZES = []
|
||||
for url in RPC_URLS:
|
||||
SUPPORTED_SIZES.append(asyncio.run(determine_optimal_batch_size(url)))
|
||||
BALANCE_BATCH_SIZE = min(SUPPORTED_SIZES)
|
||||
print(f"🎯 Using safe batch size of {BALANCE_BATCH_SIZE} for all RPCs\n")
|
||||
|
||||
asyncio.run(main())
|
Loading…
x
Reference in New Issue
Block a user