This commit is contained in:
tcsenpai 2024-11-28 21:43:16 +01:00
parent e91b0bbc99
commit edfa73c823
8 changed files with 493 additions and 42 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.env
best_matches.json
cuda_matches.json
__pycache__
found_addresses.txt

100
README.md
View File

@ -1,6 +1,6 @@
# 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.
A high-performance Ethereum vanity address generator that uses CUDA GPU acceleration to quickly generate Ethereum addresses matching a desired prefix pattern. It includes optional balance checking functionality across multiple RPC endpoints and tracks best partial matches.
## Features
@ -10,7 +10,9 @@ A high-performance Ethereum vanity address generator that uses CUDA GPU accelera
- ⚡ 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
- 💾 Automatic saving of matches and addresses with balances
- 🎯 Tracks best partial matches and saves them to JSON
- 🔐 Secure private key generation using CUDA's RNG
## Requirements
@ -23,59 +25,97 @@ A high-performance Ethereum vanity address generator that uses CUDA GPU accelera
## Installation
1. Clone the repository:
```bash
git clone https://github.com/tcsenpai/eth-vanity-address-generator-cuda.git
```
```bash
git clone https://github.com/tcsenpai/eth-vanity-address-generator-cuda.git
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
```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
```
```bash
# Enable balance checking (true/false)
CHECK_BALANCES=true
# RPC URLs for redundancy
RPC_URLS=[
"https://eth.llamarpc.com",
"https://rpc.ankr.com/eth",
"https://ethereum.publicnode.com",
"https://1rpc.io/eth"
]
# How many addresses to check in each batch
BALANCE_BATCH_SIZE=100
# Desired address prefix
PREFIX=dEAD000000000000000042069420694206942069
# Set to true for synchronous balance checking
SYNC_MODE=false
# CUDA batch size for address generation
BATCH_SIZE=500
```
## Usage
1. Run the script:
```bash
python main.py
```
You can run either the pure CUDA version or the version with balance checking:
2. Enter your desired address prefix when prompted, or configure it in the `.env` file.
1. CUDA-only version:
The script will begin generating addresses and checking balances if enabled. Status updates are printed every 10 seconds showing:
```bash
./find_address_fullcuda.sh
```
2. CUDA with balance checking:
```bash
./find_address_and_check_balance.sh
```
The script will begin generating addresses and checking balances if enabled. Status updates are printed every 10 seconds (5s for the full cuda version without balance checking) and show:
- Time elapsed
- Total attempts
- Generation speed
- Prefix check speed
- Balance check status
- Best match found so far
When a matching address is found, it will be displayed along with its private key.
## Output Files
- `found_addresses.txt`: Contains addresses found with balances
- `best_matches.json`: Tracks the best partial matches found
- `cuda_matches.json`: Records all matches found by the CUDA miner
## 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
- `CHECK_BALANCES`: Enable/disable balance checking (only works with balance checking version)
- `RPC_URLS`: List of Ethereum RPC endpoints for redundancy (only works with balance checking version)
- `BALANCE_BATCH_SIZE`: Number of addresses to check in each batch (only works with balance checking version)
- `PREFIX`: Target address prefix
- `SYNC_MODE`: Use synchronous or asynchronous balance checking
- `BATCH_SIZE`: CUDA batch size for address generation
## 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`
- Multiple RPC endpoints provide redundancy and failover
- The system automatically tracks and saves best partial matches
- CUDA batch size is optimized for RTX series GPUs
- Balance checking automatically determines optimal batch sizes for each RPC
## Performance
The following metrics are based on a RTX 4060 Mobile GPU (8GB VRAM).
- The full cuda version without balance checking manages to check about 25,000,000 (25 million) addresses per second.
- The balance checking version manages to check about 2000 prefixes and 1000 balances per second. Disabling balance checking increases the speed to about 12,000 prefixes per second.

11
env.example Normal file
View File

@ -0,0 +1,11 @@
# 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
# CUDA batch size for address generation
BATCH_SIZE=500

View File

@ -0,0 +1,5 @@
export CUDA_CACHE_DISABLE=0
export CUDA_CACHE_MAXSIZE=2147483647
export CUDA_CACHE_PATH=/tmp/cuda-cache
python vanity_and_balance.py

View File

@ -2,4 +2,4 @@ export CUDA_CACHE_DISABLE=0
export CUDA_CACHE_MAXSIZE=2147483647
export CUDA_CACHE_PATH=/tmp/cuda-cache
python main.py
python vanity_cuda.py

157
libs/cudacode.py Normal file
View File

@ -0,0 +1,157 @@
CUDA_CRYPTO_CODE = """
#include <stdint.h>
#include <cuda_runtime.h>
#include <curand_kernel.h>
#define KECCAK_ROUNDS 24
#define BATCH_SIZE 65536
// Keccak round constants
__device__ __constant__ uint64_t keccak_round_constants[24] = {
0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808AULL,
0x8000000080008000ULL, 0x000000000000808BULL, 0x0000000080000001ULL,
0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008AULL,
0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000AULL,
0x000000008000808BULL, 0x800000000000008BULL, 0x8000000000008089ULL,
0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
0x000000000000800AULL, 0x800000008000000AULL, 0x8000000080008081ULL,
0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL
};
// Keccak state rotation offsets
__device__ __constant__ int keccak_rotc[24] = {
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
};
// Keccak state permutation indices
__device__ __constant__ int keccak_piln[24] = {
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
};
// Keccak-256 hash function
__device__ void keccak256_transform(uint64_t* state) {
uint64_t temp, C[5];
int i, j;
for (int round = 0; round < KECCAK_ROUNDS; round++) {
// Theta step
for (i = 0; i < 5; i++) {
C[i] = state[i] ^ state[i + 5] ^ state[i + 10] ^ state[i + 15] ^ state[i + 20];
}
for (i = 0; i < 5; i++) {
temp = C[(i + 4) % 5] ^ ((C[(i + 1) % 5] << 1) | (C[(i + 1) % 5] >> 63));
for (j = 0; j < 25; j += 5) {
state[j + i] ^= temp;
}
}
// Rho and Pi steps
temp = state[1];
for (i = 0; i < 24; i++) {
j = keccak_piln[i];
C[0] = state[j];
state[j] = ((temp << keccak_rotc[i]) | (temp >> (64 - keccak_rotc[i])));
temp = C[0];
}
// Chi step
for (j = 0; j < 25; j += 5) {
for (i = 0; i < 5; i++) {
C[i] = state[j + i];
}
for (i = 0; i < 5; i++) {
state[j + i] ^= (~C[(i + 1) % 5]) & C[(i + 2) % 5];
}
}
// Iota step
state[0] ^= keccak_round_constants[round];
}
}
__device__ void keccak256_update(uint64_t* state, const uint8_t* data, size_t len) {
for (size_t i = 0; i < len; i++) {
state[i/8] ^= ((uint64_t)data[i]) << (8 * (i % 8));
}
keccak256_transform(state);
}
__device__ void keccak256_final(uint64_t* state, uint8_t* hash) {
keccak256_transform(state);
for (int i = 0; i < 4; i++) {
((uint64_t*)hash)[i] = state[i];
}
}
// Main kernel for address generation and checking
extern "C" __global__ void generate_and_check(
uint8_t* private_keys,
uint8_t* addresses,
int* match_lengths,
const uint8_t* target_prefix,
int prefix_len,
uint32_t seed
) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx >= BATCH_SIZE) return;
// Initialize random state
curandState rng_state;
curand_init(seed + idx, 0, 0, &rng_state);
// Generate random private key
uint8_t private_key[32];
for (int i = 0; i < 32; i++) {
private_key[i] = curand(&rng_state) & 0xFF;
}
// Store private key
for (int i = 0; i < 32; i++) {
private_keys[idx * 32 + i] = private_key[i];
}
// Initialize Keccak state
uint64_t keccak_state[25] = {0};
// Hash private key to get address
keccak256_update(keccak_state, private_key, 32);
uint8_t hash[32];
keccak256_final(keccak_state, hash);
// Take last 20 bytes as address
for (int i = 0; i < 20; i++) {
addresses[idx * 20 + i] = hash[i + 12];
}
// Convert address to hex and compare with target
int match_count = 0;
for (int i = 0; i < prefix_len && i < 20; i++) {
uint8_t addr_byte = addresses[idx * 20 + i];
uint8_t target_byte = target_prefix[i];
// Convert each byte to two hex characters
char addr_hex[2];
addr_hex[0] = (addr_byte >> 4) <= 9 ? (addr_byte >> 4) + '0' : (addr_byte >> 4) - 10 + 'a';
addr_hex[1] = (addr_byte & 0x0F) <= 9 ? (addr_byte & 0x0F) + '0' : (addr_byte & 0x0F) - 10 + 'a';
char target_hex[2];
target_hex[0] = (target_byte >> 4) <= 9 ? (target_byte >> 4) + '0' : (target_byte >> 4) - 10 + 'a';
target_hex[1] = (target_byte & 0x0F) <= 9 ? (target_byte & 0x0F) + '0' : (target_byte & 0x0F) - 10 + 'a';
// Compare characters
if (addr_hex[0] != target_hex[0]) {
break; // First character doesn't match
}
match_count++;
if (addr_hex[1] != target_hex[1]) {
break; // Second character doesn't match
}
match_count++;
}
match_lengths[idx] = match_count;
}
"""

View File

@ -13,6 +13,7 @@ from collections import deque
import aiohttp
import concurrent.futures
from threading import Lock
import json
cuda.init()
@ -28,6 +29,8 @@ RPC_URLS = [
# Add new configuration from .env
CHECK_BALANCES = os.getenv("CHECK_BALANCES", "false").lower() == "true"
if not CHECK_BALANCES:
print("🚫 Balance checking is disabled.")
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
@ -41,6 +44,10 @@ last_balance_check = {"address": None, "balance": None, "rpc": None}
pending_tasks = []
MAX_PENDING_TASKS = 10 # Adjust based on your needs
# Add near the top with other globals
FOUND_FILE = "found_addresses.txt"
BEST_MATCHES_FILE = "best_matches.json"
def get_next_web3():
global rpc_index
@ -111,8 +118,8 @@ def check_single_balance(address, private_key):
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")
with open(FOUND_FILE, "a") as f:
f.write(f"Address: {address}\nPrivate Key: {private_key}\nBalance: {Web3.from_wei(balance, 'ether')} ETH\n{'='*50}\n")
return balance
except Exception as e:
if "429" in str(e): # Rate limit error
@ -213,10 +220,28 @@ async def generate_vanity_address(prefix, num_attempts=0):
"address": address,
"similarity": similarity,
"private_key": priv_key_hex,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
}
print(
f"🎯 Best match so far: {best_match['address']} ({best_match['similarity']} chars)"
)
print(f"🎯 Best match so far: {best_match['address']} ({best_match['similarity']} chars)")
# Save to JSON file
try:
# Load existing matches
matches = []
try:
with open(BEST_MATCHES_FILE, 'r') as f:
matches = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
matches = []
# Add new match
matches.append(best_match)
# Save updated matches
with open(BEST_MATCHES_FILE, 'w') as f:
json.dump(matches, f, indent=2)
except Exception as e:
print(f"❌ Error saving best match: {str(e)}")
# Immediately check for balance if best match without waiting
ethBalance = check_single_balance(address, priv_key_hex)
print(f"💰 Balance: {ethBalance} ETH")
@ -385,8 +410,8 @@ async def batch_check_balances(addresses, private_keys):
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")
with open(FOUND_FILE, "a") as f:
f.write(f"Address: {address}\nPrivate Key: {private_key}\nBalance: {Web3.from_wei(balance, 'ether')} ETH\n{'='*50}\n")
return results
@ -488,9 +513,12 @@ async def main():
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")
if CHECK_BALANCES:
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")
else:
print("🚫 Balance checking is disabled.")
asyncio.run(main())

205
vanity_cuda.py Normal file
View File

@ -0,0 +1,205 @@
import pycuda.autoinit
import pycuda.driver as cuda
from pycuda.compiler import SourceModule
import numpy as np
import time
import json
import os
from dotenv import load_dotenv
from libs.cudacode import CUDA_CRYPTO_CODE
# Load environment variables
load_dotenv()
PREFIX = os.getenv("PREFIX", "dEAD000000000000000042069420694206942069").lower()
BATCH_SIZE = 2**16 # 65536 addresses per batch
class EthereumVanityMiner:
def __init__(self):
# Initialize CUDA
cuda.init()
self.device = cuda.Device(0)
self.context = self.device.make_context()
# Compile CUDA module
self.mod = SourceModule(CUDA_CRYPTO_CODE, no_extern_c=True)
self.kernel = self.mod.get_function("generate_and_check")
# Prepare memory buffers
self.gpu_private_keys = cuda.mem_alloc(BATCH_SIZE * 32)
self.gpu_addresses = cuda.mem_alloc(BATCH_SIZE * 20)
self.gpu_match_lengths = cuda.mem_alloc(BATCH_SIZE * 4)
# Prepare target prefix
prefix = PREFIX[2:] if PREFIX.startswith("0x") else PREFIX
# Debug print
print(f"Converting prefix: {prefix}")
# Convert each pair of hex chars to a byte
self.prefix_bytes = bytearray()
for i in range(0, len(prefix), 2):
hex_pair = prefix[i:i+2]
byte_val = int(hex_pair, 16)
self.prefix_bytes.append(byte_val)
# Debug print
print(f"Prefix bytes: {[hex(b) for b in self.prefix_bytes]}")
self.gpu_target = cuda.mem_alloc(len(self.prefix_bytes))
cuda.memcpy_htod(self.gpu_target, self.prefix_bytes)
# Host buffers
self.host_match_lengths = np.zeros(BATCH_SIZE, dtype=np.int32)
self.host_private_key = np.zeros(32, dtype=np.uint8)
self.host_address = np.zeros(20, dtype=np.uint8)
def cleanup(self):
"""Clean up GPU resources"""
try:
self.gpu_private_keys.free()
self.gpu_addresses.free()
self.gpu_match_lengths.free()
self.gpu_target.free()
finally:
self.context.pop()
self.context.detach()
def save_match(
self, address_hex, private_key_hex, match_length, raw_address, raw_private_key
):
"""Save match to JSON file"""
try:
matches = []
if os.path.exists("cuda_matches.json"):
with open("cuda_matches.json", "r") as f:
matches = json.load(f)
match_data = {
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"address": f"0x{address_hex}",
"private_key": private_key_hex,
"match_length": int(match_length),
"raw_address": raw_address,
"raw_private_key": raw_private_key,
}
matches.append(match_data)
with open("cuda_matches.json", "w") as f:
json.dump(matches, f, indent=2)
except Exception as e:
print(f"\nWarning: Failed to save match to file: {e}")
def check_batch(self, seed):
"""Run one batch of address generation and checking"""
block = (256, 1, 1)
grid = ((BATCH_SIZE + block[0] - 1) // block[0], 1)
self.kernel(
self.gpu_private_keys,
self.gpu_addresses,
self.gpu_match_lengths,
self.gpu_target,
np.int32(len(self.prefix_bytes)),
np.uint32(seed),
block=block,
grid=grid
)
# Get results
cuda.memcpy_dtoh(self.host_match_lengths, self.gpu_match_lengths)
best_idx = np.argmax(self.host_match_lengths)
match_length = self.host_match_lengths[best_idx]
if match_length > 0:
temp_private_key = np.zeros(32, dtype=np.uint8)
temp_address = np.zeros(20, dtype=np.uint8)
cuda.memcpy_dtoh(temp_private_key, self.gpu_private_keys)
cuda.memcpy_dtoh(temp_address, self.gpu_addresses)
address_hex = ''.join(format(x, '02x') for x in temp_address)
private_key_hex = ''.join(format(x, '02x') for x in temp_private_key)
# Verify the match
target = PREFIX[2:] if PREFIX.startswith('0x') else PREFIX
actual_match = 0
for i, (t, a) in enumerate(zip(target, address_hex)):
if t.lower() != a.lower():
break
actual_match += 1
if actual_match > 0:
return {
'address': address_hex,
'private_key': private_key_hex,
'match_length': actual_match,
'raw_address': temp_address.tobytes().hex(),
'raw_private_key': temp_private_key.tobytes().hex()
}
return None
def mine(self, target_score=None):
"""Main mining loop"""
try:
best_match = {"match_length": 0}
start_time = time.time()
last_status_time = start_time
addresses_checked = 0
while True:
seed = np.random.randint(0, 2**32, dtype=np.uint32)
result = self.check_batch(seed)
addresses_checked += BATCH_SIZE
current_time = time.time()
# Print status every 5 seconds
if current_time - last_status_time >= 5:
elapsed_time = current_time - start_time
rate = addresses_checked / elapsed_time
print(
f"\rChecked {addresses_checked:,} addresses ({rate:,.0f}/s) - Best match: {best_match['match_length']} chars",
end="",
)
last_status_time = current_time
if result and result["match_length"] > best_match["match_length"]:
best_match = result
print(f"\nNew best match ({result['match_length']} chars):")
print(f"Address: 0x{result['address']}")
print(f"Private key: {result['private_key']}")
self.save_match(
result["address"],
result["private_key"],
result["match_length"],
result["raw_address"],
result["raw_private_key"],
)
if target_score and result["match_length"] >= target_score:
return result
except KeyboardInterrupt:
print("\nMining interrupted by user")
return best_match
finally:
self.cleanup()
if __name__ == "__main__":
try:
miner = EthereumVanityMiner()
print(f"Mining with {cuda.Device.count()} GPU(s)")
print(f"Target prefix: {PREFIX}")
print(f"Batch size: {BATCH_SIZE}")
result = miner.mine(target_score=len(PREFIX))
if result:
print("\nFinal result:")
print(f"Address: 0x{result['address']}")
print(f"Private key: {result['private_key']}")
print(f"Match length: {result['match_length']}")
except Exception as e:
print(f"Error: {e}")