Pages:
Author

Topic: the fastest possible way to mass-generate addresses in Python (Read 1349 times)

copper member
Activity: 1330
Merit: 899
🖤😏
Change public key to hex to compressed=False as well, that should work.
copper member
Activity: 193
Merit: 255
Click "+Merit" top-right corner

I was looking only for public key generation part

Here's a simplified script that generates compressed public keys from a given range of private keys:

Code:
import hashlib, sys
import gmpy2

# Constants as mpz
Gx = gmpy2.mpz('0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16)
Gy = gmpy2.mpz('0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)
p = gmpy2.mpz('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16)
n = gmpy2.mpz('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)

def private_key_to_public_key(private_key):
    Q = point_multiply(Gx, Gy, private_key, p)
    return Q

def point_multiply(x, y, k, p):
    result = (gmpy2.mpz(0), gmpy2.mpz(0))
    addend = (x, y)
   
    while k > 0:
        if k & 1:
            result = point_add(result, addend, p)
        addend = point_double(addend, p)
        k >>= 1

    return result

def point_double(point, p):
    x, y = point
    lmbda = (3 * x * x * gmpy2.powmod(2 * y, -1, p)) % p
    x3 = (lmbda * lmbda - 2 * x) % p
    y3 = (lmbda * (x - x3) - y) % p
    return x3, y3

def point_add(point1, point2, p):
    x1, y1 = point1
    x2, y2 = point2

    if point1 == (gmpy2.mpz(0), gmpy2.mpz(0)):
        return point2
    if point2 == (gmpy2.mpz(0), gmpy2.mpz(0)):
        return point1

    if point1 != point2:
        lmbda = ((y2 - y1) * gmpy2.powmod(x2 - x1, -1, p)) % p
    else:
        lmbda = ((3 * x1 * x1) * gmpy2.powmod(2 * y1, -1, p)) % p

    x3 = (lmbda * lmbda - x1 - x2) % p
    y3 = (lmbda * (x1 - x3) - y1) % p
    return x3, y3

def encode_base58(byte_str):
    __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    __b58base = len(__b58chars)
    long_value = gmpy2.mpz(int.from_bytes(byte_str, byteorder='big'))
    result = ''
    while long_value >= __b58base:
        div, mod = gmpy2.f_divmod(long_value, __b58base)
        result = __b58chars[int(mod)] + result
        long_value = div
    result = __b58chars[int(long_value)] + result

    # Add leading '1's for zero bytes
    nPad = 0
    for byte in byte_str:
        if byte == 0:
            nPad += 1
        else:
            break

    return __b58chars[0] * nPad + result

def public_key_to_hex(public_key, compressed=True):
    x_hex = format(public_key[0], '064x')[2:]  # Remove '0x' prefix
    if compressed:
        # Use '02' prefix if Y coordinate is even, '03' if odd
        return ('02' if public_key[1] % 2 == 0 else '03') + x_hex

def public_key_to_address(public_key, compressed=True):
    public_key_hex = ('02' if compressed else '04') + format(public_key[0], '064x')
    sha256_hash = hashlib.sha256(bytes.fromhex(public_key_hex)).digest()
    ripemd160_hash = hashlib.new('ripemd160', sha256_hash).digest()
    versioned_hash = (b'\x00' if compressed else b'\x04') + ripemd160_hash
    checksum = hashlib.sha256(hashlib.sha256(versioned_hash).digest()).digest()[:4]
    address_bytes = versioned_hash + checksum
    return encode_base58(address_bytes)

# Define the range
start_range = gmpy2.mpz('36893488147419132058')
end_range = gmpy2.mpz('36893488149419115809')

# Iterate through the range and generate Bitcoin Addresses (Compressed) and their Public Keys
for key in range(start_range, end_range + 1):
    public_key = private_key_to_public_key(key)
    bitcoin_address = public_key_to_address(public_key, compressed=True)
    public_key = public_key_to_hex(public_key)
    sys.stdout.write("\033c")
    sys.stdout.write("\033[01;33m")
    sys.stdout.write(f"\r[+] Private Key (dec): {key}\n[+] Bitcoin Address (Compressed): {bitcoin_address}\n[+] Public Key: {public_key}" + "\n")
    sys.stdout.flush()


Mobile phones typically run Android or iOS. Python can be run on both platforms, but there are some differences in compatibility..
For Android, you can use the QPython app or Termux to run Python scripts. On iOS, you might need to use apps like Pythonista or Pyto.
It may require a lot of CPU power and memory. Make sure your mobile phone can handle the computational requirements.
You can even translate this script into a mobile app  but I don't see the purpose of it on the phone. Neither the script will work as it should nor the phone.


Lovely. However "def public_key_to_address" behaves not right if "compressed=False". Would you mind taking a look at it?
member
Activity: 462
Merit: 24

I was looking only for public key generation part

Here's a simplified script that generates compressed public keys from a given range of private keys:

Code:
import hashlib, sys
import gmpy2

# Constants as mpz
Gx = gmpy2.mpz('0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16)
Gy = gmpy2.mpz('0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)
p = gmpy2.mpz('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16)
n = gmpy2.mpz('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)

def private_key_to_public_key(private_key):
    Q = point_multiply(Gx, Gy, private_key, p)
    return Q

def point_multiply(x, y, k, p):
    result = (gmpy2.mpz(0), gmpy2.mpz(0))
    addend = (x, y)
   
    while k > 0:
        if k & 1:
            result = point_add(result, addend, p)
        addend = point_double(addend, p)
        k >>= 1

    return result

def point_double(point, p):
    x, y = point
    lmbda = (3 * x * x * gmpy2.powmod(2 * y, -1, p)) % p
    x3 = (lmbda * lmbda - 2 * x) % p
    y3 = (lmbda * (x - x3) - y) % p
    return x3, y3

def point_add(point1, point2, p):
    x1, y1 = point1
    x2, y2 = point2

    if point1 == (gmpy2.mpz(0), gmpy2.mpz(0)):
        return point2
    if point2 == (gmpy2.mpz(0), gmpy2.mpz(0)):
        return point1

    if point1 != point2:
        lmbda = ((y2 - y1) * gmpy2.powmod(x2 - x1, -1, p)) % p
    else:
        lmbda = ((3 * x1 * x1) * gmpy2.powmod(2 * y1, -1, p)) % p

    x3 = (lmbda * lmbda - x1 - x2) % p
    y3 = (lmbda * (x1 - x3) - y1) % p
    return x3, y3

def encode_base58(byte_str):
    __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    __b58base = len(__b58chars)
    long_value = gmpy2.mpz(int.from_bytes(byte_str, byteorder='big'))
    result = ''
    while long_value >= __b58base:
        div, mod = gmpy2.f_divmod(long_value, __b58base)
        result = __b58chars[int(mod)] + result
        long_value = div
    result = __b58chars[int(long_value)] + result

    # Add leading '1's for zero bytes
    nPad = 0
    for byte in byte_str:
        if byte == 0:
            nPad += 1
        else:
            break

    return __b58chars[0] * nPad + result

def public_key_to_hex(public_key, compressed=True):
    x_hex = format(public_key[0], '064x')[2:]  # Remove '0x' prefix
    if compressed:
        # Use '02' prefix if Y coordinate is even, '03' if odd
        return ('02' if public_key[1] % 2 == 0 else '03') + x_hex

def public_key_to_address(public_key, compressed=True):
    public_key_hex = ('02' if compressed else '04') + format(public_key[0], '064x')
    sha256_hash = hashlib.sha256(bytes.fromhex(public_key_hex)).digest()
    ripemd160_hash = hashlib.new('ripemd160', sha256_hash).digest()
    versioned_hash = (b'\x00' if compressed else b'\x04') + ripemd160_hash
    checksum = hashlib.sha256(hashlib.sha256(versioned_hash).digest()).digest()[:4]
    address_bytes = versioned_hash + checksum
    return encode_base58(address_bytes)

# Define the range
start_range = gmpy2.mpz('36893488147419132058')
end_range = gmpy2.mpz('36893488149419115809')

# Iterate through the range and generate Bitcoin Addresses (Compressed) and their Public Keys
for key in range(start_range, end_range + 1):
    public_key = private_key_to_public_key(key)
    bitcoin_address = public_key_to_address(public_key, compressed=True)
    public_key = public_key_to_hex(public_key)
    sys.stdout.write("\033c")
    sys.stdout.write("\033[01;33m")
    sys.stdout.write(f"\r[+] Private Key (dec): {key}\n[+] Bitcoin Address (Compressed): {bitcoin_address}\n[+] Public Key: {public_key}" + "\n")
    sys.stdout.flush()


Mobile phones typically run Android or iOS. Python can be run on both platforms, but there are some differences in compatibility..
For Android, you can use the QPython app or Termux to run Python scripts. On iOS, you might need to use apps like Pythonista or Pyto.
It may require a lot of CPU power and memory. Make sure your mobile phone can handle the computational requirements.
You can even translate this script into a mobile app  but I don't see the purpose of it on the phone. Neither the script will work as it should nor the phone.
copper member
Activity: 1330
Merit: 899
🖤😏

Thanks for the effort, but I was looking only for public key generation part, and then I would have changed add/subtract/ values and just like that I could have a mobile version of key subtracter. On my phone I couldn't run your script, I will try on laptop to see what happens, I just hope all the functions such ad wif, base58, uncompressed pubs etc are for decoration in this script, I mean I can barely handle public keys. 😉

I just remembered there was a script posted on puzzle thread which used subtraction by a desired value, but it was sequential. Thanks and sorry to post here asking help from others @OP.
member
Activity: 462
Merit: 24

I tried using the script you posted but I can't generate millions on an android phone, I changed many things but it returned an error everytime, I'm interested in this one because  it uses no external libraries, and since iceland library doesn't support arm/mobile architecture.

What do I need to change to generate my desired range for public keys?


Maybe everything? Grin


Code:
#!/usr/bin/python3
import hashlib, sys

# Constants as regular integers
Gx = int('0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798', 16)
Gy = int('0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8', 16)
p = int('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16)
n = int('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16)

def private_key_to_public_key(private_key):
    Q = point_multiply(Gx, Gy, private_key, p)
    return Q

def point_multiply(x, y, k, p):
    result = (0, 0)
    addend = (x, y)

    while k > 0:
        if k & 1:
            result = point_add(result, addend, p)
        addend = point_double(addend, p)
        k >>= 1

    return result

def point_double(point, p):
    x, y = point
    lmbda = (3 * x * x * pow(2 * y, -1, p)) % p
    x3 = (lmbda * lmbda - 2 * x) % p
    y3 = (lmbda * (x - x3) - y) % p
    return x3, y3

def point_add(point1, point2, p):
    x1, y1 = point1
    x2, y2 = point2

    if point1 == (0, 0):
        return point2
    if point2 == (0, 0):
        return point1

    if point1 != point2:
        lmbda = ((y2 - y1) * pow(x2 - x1, -1, p)) % p
    else:
        lmbda = ((3 * x1 * x1) * pow(2 * y1, -1, p)) % p

    x3 = (lmbda * lmbda - x1 - x2) % p
    y3 = (lmbda * (x1 - x3) - y1) % p
    return x3, y3

def encode_base58(byte_str):
    __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
    __b58base = len(__b58chars)
    long_value = int.from_bytes(byte_str, byteorder='big')
    result = ''
    while long_value >= __b58base:
        div, mod = divmod(long_value, __b58base)
        result = __b58chars[mod] + result
        long_value = div
    result = __b58chars[long_value] + result

    # Add leading '1's for zero bytes
    nPad = 0
    for byte in byte_str:
        if byte == 0:
            nPad += 1
        else:
            break

    return __b58chars[0] * nPad + result

def public_key_to_hex(public_key, compressed=True):
    x_hex = format(public_key[0], '064x')[2:]  # Remove '0x' prefix
    if compressed:
        # Use '02' prefix if Y coordinate is even, '03' if odd
        return ('02' if public_key[1] % 2 == 0 else '03') + x_hex

def public_key_to_address(public_key, compressed=True):
    public_key_hex = ('02' if compressed else '04') + format(public_key[0], '064x')
    sha256_hash = hashlib.sha256(bytes.fromhex(public_key_hex)).digest()
    ripemd160_hash = hashlib.new('ripemd160', sha256_hash).digest()
    versioned_hash = (b'\x00' if compressed else b'\x04') + ripemd160_hash
    checksum = hashlib.sha256(hashlib.sha256(versioned_hash).digest()).digest()[:4]
    address_bytes = versioned_hash + checksum
    return encode_base58(address_bytes)

# Define the range
start_range = int('36893488147419103232')
end_range = int('73786976294838206463')

# Iterate through the range and generate Bitcoin Addresses (Compressed) and their Public Keys
for key in range(start_range, end_range + 1):
    public_key = private_key_to_public_key(key)
    bitcoin_address = public_key_to_address(public_key, compressed=True)
    public_key = public_key_to_hex(public_key)
    sys.stdout.write("\033c")
    sys.stdout.write("\033[01;33m")
    sys.stdout.write(f"\r[+] Private Key (dec): {key}\n[+] Bitcoin Address (Compressed): {bitcoin_address}\n[+] Public Key: {public_key}" + "\n")
    sys.stdout.flush()

p.s.
updated as desired
copper member
Activity: 1330
Merit: 899
🖤😏

I tried using the script you posted but I can't generate millions on an android phone, I changed many things but it returned an error everytime, I'm interested in this one because  it uses no external libraries, and since iceland library doesn't support arm/mobile architecture.

What do I need to change to generate my desired range for public keys?
member
Activity: 462
Merit: 24
There is Makefile to compile. All build commands are there. Just cd to the directory with code and use make in terminal.
$ make
$ ./VanitySearch


cat gen.cpp

Quote
#include "secp256k1/SECP256k1.h"
#include "secp256k1/Int.h"
#include
#include

int main() {
  
    Secp256K1* secp256k1 = new Secp256K1();
    secp256k1->Init();
    Int privKey;
    privKey.SetBase10("1");
    Point pub;
    std::string bitAddr;
    std::ofstream outFile;
    outFile.open("address.txt", std::ios::app);
    for(int i = 0; i < 1000000; i++) {
        pub = secp256k1->ComputePublicKey(&privKey);
        bitAddr = secp256k1->GetAddress(0, false, pub);
        outFile << bitAddr << '\n';
        privKey.AddOne();
    }
    outFile.close();
    return 0;
}

git clone https://github.com/JeanLucPons/VanitySearch.git

main.cpp :
Code:
#include "SECP256k1.h"
#include "Int.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

const int numThreads =  4;  //You can adjust this number based on your CPU cores

// Function to generate keys and check for a specific address
void generateKeysAndCheckForAddress(const Int& minKey, Int maxKey, std::shared_ptr secp256k1, const std::string& targetAddress) {
    Int privateKey = minKey;
    Point publicKey;
    std::string caddr;
    std::string wifc;

    while (true) {
        publicKey = secp256k1->ComputePublicKey(&privateKey);
        caddr = secp256k1->GetAddress(0, true, publicKey);
        wifc = secp256k1->GetPrivAddress(true, privateKey);
        // Display the generated address
        std::string message = "\r\033[01;33m[+] " + caddr;
        std::cout << message << "\e[?25l";
        std::cout.flush();

        // Check if the generated address matches the target address
        if (caddr.find(targetAddress) != std::string::npos) {
          time_t currentTime = std::time(nullptr);
    
          // Format the current time into a human-readable string
          std::tm tmStruct = *std::localtime(¤tTime);
          std::stringstream timeStringStream;
          timeStringStream << std::put_time(&tmStruct, "%Y-%m-%d %H:%M:%S");
          std::string formattedTime = timeStringStream.str();

          std::cout << "\n\033[32m[+] PUZZLE SOLVED: " << formattedTime << "\033[0m" << std::endl;
          std::cout << "\033[32m[+] WIF: " << wifc << "\033[0m" << std::endl;
          
           // Append the private key information to a file if it matches
           std::ofstream file("KEYFOUNDKEYFOUND.txt", std::ios::app);
           if (file.is_open()) {
                file << "\nPUZZLE SOLVED " << formattedTime;
                file << "\nPublic Address Compressed: " << caddr;
                file << "\nPrivatekey (dec): " << privateKey.GetBase10();
                file << "\nPrivatekey Compressed (wif): " << wifc;
                file << "\n----------------------------------------------------------------------------------------------------------------------------------";
                file.close();
            }
        }

        privateKey.AddOne();

        if (privateKey.IsGreater(&maxKey)) {
            break;
        }
    }
}

int main() {
    // Clear the console
    std::system("clear");

    time_t currentTime = std::time(nullptr);
    std::cout << "\033[01;33m[+] " << std::ctime(¤tTime) << "\r";
    std::cout.flush();

    Int minKey;
    Int maxKey;
    // Configuration for the Puzzle
    minKey.SetBase10("67079069358943824031");
    maxKey.SetBase10("69594534459904217431");
    std::string targetAddress = "13zb1hQbWVsc2S7ZTZnP2G4undNNpdh5so";

    // Initialize SECP256k1
    std::shared_ptr secp256k1 = std::make_shared();
    secp256k1->Init();

    // Create threads for key generation and checking
    std::vector threads;

    for (int i = 0; i < numThreads; ++i) {
        threads.emplace_back(generateKeysAndCheckForAddress, minKey, maxKey, secp256k1, targetAddress);
    }

    // Wait for all threads to finish
    for (std::thread& thread : threads) {
        thread.join();
    }

    return 0;
}
 
Makefile(for cpu only. similar can be done for gpu):
Code:
SRC = Base58.cpp IntGroup.cpp main.cpp Random.cpp Timer.cpp \
      Int.cpp IntMod.cpp Point.cpp SECP256K1.cpp \
      hash/ripemd160.cpp hash/sha256.cpp hash/sha512.cpp \
      hash/ripemd160_sse.cpp hash/sha256_sse.cpp Bech32.cpp

OBJDIR = obj

OBJET = $(addprefix $(OBJDIR)/, \
        Base58.o IntGroup.o main.o Random.o Int.o Timer.o \
        IntMod.o Point.o SECP256K1.o \
        hash/ripemd160.o hash/sha256.o hash/sha512.o \
        hash/ripemd160_sse.o hash/sha256_sse.o Bech32.o)

CXX = g++
CXXFLAGS = -m64 -mssse3 -Wno-write-strings -O2 -I.

LFLAGS = -lpthread

$(OBJDIR)/%.o : %.cpp
$(CXX) $(CXXFLAGS) -o $@ -c $<



VanitySearch: $(OBJET)
@echo Making Lottery...
$(CXX) $(OBJET) $(LFLAGS) -o LOTTO.bin && chmod +x LOTTO.bin

$(OBJET): | $(OBJDIR) $(OBJDIR)/hash

$(OBJDIR):
mkdir -p $(OBJDIR)

$(OBJDIR)/hash: $(OBJDIR)
cd $(OBJDIR) && mkdir -p hash

clean:
@echo Cleaning...
@rm -f obj/*.o
@rm -f obj/hash/*.o

I started from that little code first. Now I got to point trying to solve Puzzle 66 with it. Which is the goal in the first place of  the fast generation of addresses. Grin
full member
Activity: 291
Merit: 133
Try this code:

Why it does not create and write to file?

I've changed number of cores to 4 but it didn't helped...

Also, I've uncommented the second list line but with no luck.

Seems like the program does something but no effect to file...
newbie
Activity: 72
Merit: 0
ok will try --- thank you
legendary
Activity: 1932
Merit: 2077
Quote from: arulbero
randbelow(n-1)+1

how to replace this line
Code:
private_keys = list(map(secrets.randbelow,n))

Try this code:

Code:
#!/usr/bin/env python3
# 2023/Jan/01, citb0in_multicore_secrets.py
import concurrent.futures
import os
import secrets
import secp256k1 as ice

# how many cores to use
num_cores = 10
#num_cores = os.cpu_count()

# Set the number of addresses to generate
num_addresses = 10000000



# Define a worker function that generates a batch of addresses and returns them
def worker(start, end, i):
 
  # Generate a list of random private keys using "secrets" library
  n = [0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141] * (end - start) #n
  one = [1] * (end - start)
  my_secure_rng = secrets.SystemRandom()
  private_keys = list(map(my_secure_rng.randrange,one,n)) #from 1 to n-1

  
  # Use secp256k1 to convert the private keys to addresses
  addr_type = [2] * (end - start)
  is_compressed = [True] * (end - start)
  thread_addresses = list(map(ice.privatekey_to_address, addr_type, is_compressed, private_keys))
  
  
  # Write the addresses in the thread file
  f = open("addresses_1M_multicore_secrets" + str(i) + ".txt", "w")
  list(map(lambda x:f.write(x+"\n"),thread_addresses))
  
  #####or, if you want to store the private keys, along with the addresses###############
  #list(map(lambda x,y: f.write(hex(x)[2:].rjust(64,'0') + ' -> ' + y + '\n'),private_keys,thread_addresses)
  
  f.close()
  
  return
  

# Use a ProcessPoolExecutor to generate the addresses in parallel
with concurrent.futures.ProcessPoolExecutor() as executor:
  # Divide the addresses evenly among the available CPU cores
  addresses_per_core = num_addresses // num_cores
  
  
  # Submit a task for each batch of addresses to the executor
  tasks = []
  for i in range(num_cores):
    start = i * addresses_per_core
    end = (i+1) * addresses_per_core
    tasks.append(executor.submit(worker, start, end, i))
newbie
Activity: 72
Merit: 0

  # Write the addresses in the thread file
  list(map(lambda x:f.write(x+"\n"),thread_addresses))

  f.close()
  
  return

what is difference using
Code:
    with open("addresses_1M_multicore_secrets" + str(i) + ".txt", "a") as f:
      f.write(f'{thred_addresses}n')

Quote from: arulbero
randbelow(n-1)+1

how to replace this line
Code:
private_keys = list(map(secrets.randbelow,n))
legendary
Activity: 1932
Merit: 2077
With last code, you should save at least 2.5 - 3s on your hardware, I guess from 25,8s to < 23s to compute 10 million keys.

Did you try? I'm curious.


Absolutely correct arulbero. Thanks for pointing out. I used 2**256 as a quick'n'dirty solution and forgot about the edge of the finite field 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
Good catch.

To be more precise,

randbelow(n-1)+1  

otherwise with

randbelow(n) could occur '0' (extremely unlikely).


...

==> I have generated 1,007,862 addresses (about 1 million keys) in about 15 seconds using one single core with your program.
==> I have generated exactly 1 millions addresses in 10 seconds using one single core with my program.


Conclusion:

The originally enormous speed advantage is therefore, according to these tests, not really to be found in the code difference but in the fact that randomly generated keys are simply more computationally intensive and cost more time than simple sequential key generation. So just as you originally said arulbero.

It always depends on the purpose of use, of course, but I have my doubts that the sequential generation of private keys is not the best way. I therefore think, also in terms of security for other applications, that random private keys are always preferable to sequential ones despite the fact they are more time-intensive.

If you try to substitute randbelow(n) with randbelow(10000000) (or another small number), you'll see how much time it requires to sum up many keys to get a high key. I think you may get almost 1 Million keys per sec.
hero member
Activity: 630
Merit: 731
Bitcoin g33k
Absolutely correct arulbero. Thanks for pointing out. I used 2**256 as a quick'n'dirty solution and forgot about the edge of the finite field 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
Good catch.
legendary
Activity: 1932
Merit: 2077
I have modified to create 10 million addresses to make the difference more clear.
Code:
$ time python3 citb0in_multicore_secrets.py 
Quote
real   0m38,349s
user   5m47,453s
sys   0m16,060s

Code:
$ time python3 citb0in_multicore_secrets_splitsave.py 
Quote
real   0m25,835s
user   5m57,795s
sys   0m14,681s

10 million addresses generated in less than 26 seconds. Rate = 387.071 (Python).

==> this is a performance boost of additional + 32.7 % on my computer. Crazy!  Tongue Roll Eyes Cool thank you for pointing out @arulbero. Great!


You don't use any specific function from numpy, then I suggest you to eliminate numpy

besides you have to generate a random key k with
k < n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
not
k < 2**256

you can comment the check at this line:
https://github.com/iceland2k14/secp256k1/blob/main/secp256k1.py#L290


This is your code optimized:

Code:
#!/usr/bin/env python3
# 2023/Jan/01, citb0in_multicore_secrets.py
import concurrent.futures
import os
import secrets
import secp256k1 as ice

# how many cores to use
num_cores = 10
#num_cores = os.cpu_count()

# Set the number of addresses to generate
num_addresses = 10000000


# Define a worker function that generates a batch of addresses and returns them
def worker(start, end, i):
 
  addr_type = [2] * (end - start)
  is_compressed = [True] * (end - start)
  n = [0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141] * (end - start)
 
  f = open("addresses_1M_multicore_secrets" + str(i) + ".txt", "w")
  
  # Generate a list of random private keys using "secrets" library
  private_keys = list(map(secrets.randbelow,n))

  
  # Use secp256k1 to convert the private keys to addresses
  thread_addresses = list(map(ice.privatekey_to_address, addr_type, is_compressed, private_keys))
  
  # Write the addresses in the thread file
  list(map(lambda x:f.write(x+"\n"),thread_addresses))

  f.close()
  
  return
  

# Use a ProcessPoolExecutor to generate the addresses in parallel
with concurrent.futures.ProcessPoolExecutor() as executor:
  # Divide the addresses evenly among the available CPU cores
  addresses_per_core = num_addresses // num_cores
  
  
  # Submit a task for each batch of addresses to the executor
  tasks = []
  for i in range(num_cores):
    start = i * addresses_per_core
    end = (i+1) * addresses_per_core
    tasks.append(executor.submit(worker, start, end, i))
newbie
Activity: 72
Merit: 0
@yoshimitsu777:

try this:

- rename main.cpp to main.cpp.bak
- rename gen.cpp to main.cpp
- delete existing ./VanitySearch
- run "make"

then just execute ./VanitySearch and you got this program executed. Hope this helps.

understand now working - thanks!
hero member
Activity: 630
Merit: 731
Bitcoin g33k
Have you tried adding a large prime number, like 0xdeadbeef (just an example, I don't even know if that's prime)?
Sure, you'll have to check for overflow more frequently - fortunately, that's just a matter of doing a Greater-Than comparison followed by a subtraction - but a sufficiently large increment should make the keys look pseudorandom as far as the bits are concerned.
Whoops, now that I checked again, it turns out that I was referring to a post written by @citb0in. Sorry for the misunderstanding.
[...]

  # Generate a batch of private keys sequentially
  for i in range(start, end):
    # Increment the private key
    private_key += 1 #TODO why not use a large prime number instead of 1? 1 is not random at all, but a few dozen bits varying at once can be made to look random to others.

Hi NotAtTether.

Long time ago I wrote a python script that computes 16.4 million of consecutive (non random) public keys (not addresses) in 12.3 s.
No numpy, pure python, only 1 thread.
[...]
Generating consecutive public keys is way faster than generate random public keys.

I was just following up on arulbero's comment. I wanted to try how it behaves when the key creation is not random but sequential. That was the point. It is up to you if you want to do it differently as you suggested with the help of prime numbers. You could try it out and see how the performance could be affected or not. In spite of everything, I personally don't find the way secure enough. In any case, security aspects should not be ignored when creating keys. Of course, this varies from use case to use case and requirements may differ.

It doesn't create only 1 file, but 1 file for each thread. On my computer works.
Now I understand, sorry my fault. I have overseen one line and was confused Tongue now I tried on my computer and of course it works. This is a nice one, it gives not only a slight change but a very good performance boost on my side Smiley
I have modified to create 10 million addresses to make the difference more clear.
Code:
$ time python3 citb0in_multicore_secrets.py 
Quote
real   0m38,349s
user   5m47,453s
sys   0m16,060s

Code:
$ time python3 citb0in_multicore_secrets_splitsave.py 
Quote
real   0m25,835s
user   5m57,795s
sys   0m14,681s

10 million addresses generated in less than 26 seconds. Rate = 387.071 (Python).

==> this is a performance boost of additional + 32.7 % on my computer. Crazy!  Tongue Roll Eyes Cool thank you for pointing out @arulbero. Great!

As AlexanderCurl pointed out correctly, using C++ is much much faster and unbeatable so far. However, I still persist Smiley i would love to see how our current code would behave with cupy or similar interfaces and thus the code would run in the much faster GPU. Then we could compare with C++ implementations like those shown of AlexanderCurl or VanitySearch, BitCrack, etc.
legendary
Activity: 1932
Merit: 2077
To speed up slightly this code (with multi core), you can write on different files:

Code:
[...]
  np.savetxt('addresses_1M_multicore_secrets'+ str(i) +'.txt', thread_addresses, fmt='%s')
  
  return
[...]

This will finish faster, yes. But it will create only one single output file which contains only 62,500 addresses. My example have all 1 million addresses listed. I'm kinda puzzled Smiley what exactly you mean, can you explain, please?

If num_cores = 10, this code saves 100k addresses x 10 files. It doesn't create only 1 file, but 1 file for each thread. On my computer works.
hero member
Activity: 630
Merit: 731
Bitcoin g33k
To speed up slightly this code (with multi core), you can write on different files:

Code:
[...]
  np.savetxt('addresses_1M_multicore_secrets'+ str(i) +'.txt', thread_addresses, fmt='%s')
  
  return
[...]

This will finish faster, yes. But it will create only one single output file which contains only 62,500 addresses. My example have all 1 million addresses listed. I'm kinda puzzled Smiley what exactly you mean, can you explain, please?

Your code doesn't store private keys, are you sure you want to have only a list of addresses without their private keys?

this code is obviously made simple. My own python program saves the keys as well of course. But for the sake of easiness I want to keep the code simple so everyone is able to test and compare.
legendary
Activity: 1568
Merit: 6660
bitcoincleanup.com / bitmixlist.org
@AlexanderCurl, regarding sequential key generation:

Have you tried adding a large prime number, like 0xdeadbeef (just an example, I don't even know if that's prime)?

Sure, you'll have to check for overflow more frequently - fortunately, that's just a matter of doing a Greater-Than comparison followed by a subtraction - but a sufficiently large increment should make the keys look pseudorandom as far as the bits are concerned.

Have no idea what you are talking about. On my computer i use VanitySearch code on a daily basis for all types of programs.(random, sequential, whatever)

Whoops, now that I checked again, it turns out that I was referring to a post written by @citb0in. Sorry for the misunderstanding.

...

Well, so far so good. I showed this just to summarize things up. Afterwards I modified my code according to your suggestion so the private keys are not generated randomly but instead they are sequentially generated from a pre-defined starting point. Here's the modified version:

Code:
#!/usr/bin/env python3
# 2022/Dec/26, [b]citb0in_seq.py[/b]
import concurrent.futures
import os
import numpy as np
import secp256k1 as ice

# how many cores to use
num_cores = 1
#num_cores = os.cpu_count()

# Number of addresses to generate
num_addresses = 1000000

# Starting point (decimal/integer) for private key
starting_point = 123456789

# Define a worker function that generates a batch of addresses and returns them
def worker(start, end, starting_point):
  # Initialize the private key to the starting point
  private_key = starting_point

  # Initialize the list to hold to private keys
  private_keys = []

  # Generate a batch of private keys sequentially
  for i in range(start, end):
    # Increment the private key
    private_key += 1

    # Add the private key to the list
    private_keys.append(private_key)

  # Use secp256k1 to convert the private keys to addresses
  thread_addresses = np.array([ice.privatekey_to_address(2, True, dec) for dec in private_keys])

  return thread_addresses
  #return (thread_addresses, private_keys, start_int)

# Use a ProcessPoolExecutor to generate the addresses in parallel
with concurrent.futures.ProcessPoolExecutor() as executor:
  # Divide the addresses evenly among the available CPU cores
  addresses_per_core = num_addresses // num_cores

  # Submit a task for each batch of addresses to the executor
  tasks = []
  for i in range(num_cores):
    start = i * addresses_per_core
    end = (i+1) * addresses_per_core
    tasks.append(executor.submit(worker, start, end, starting_point))

  # Wait for the tasks to complete and retrieve the results
  addresses = []
  for task in concurrent.futures.as_completed(tasks):
    addresses.extend(task.result())

# Write the addresses to a file
np.savetxt('addresses_1M_seq_singlecore.txt', addresses, fmt='%s')

...

To save eyes from being burnt by screens at 11PM, I will paste the relevant part of the code here:


Code:
# Starting point (decimal/integer) for private key
starting_point = 123456789

# Define a worker function that generates a batch of addresses and returns them
def worker(start, end, starting_point):
  # Initialize the private key to the starting point
  private_key = starting_point

  # Initialize the list to hold to private keys
  private_keys = []

  # Generate a batch of private keys sequentially
  for i in range(start, end):
    # Increment the private key
    private_key += 1 #TODO why not use a large prime number instead of 1? 1 is not random at all, but a few dozen bits varying at once can be made to look random to others.

legendary
Activity: 1932
Merit: 2077
here's the updated complete code variant with using multicore functionality and the secrets library for enhanded speed:

Code:
#!/usr/bin/env python3
# 2023/Jan/01, citb0in_multicore_secrets.py
import concurrent.futures
import os
import numpy as np
import secrets
import secp256k1 as ice

# how many cores to use
#num_cores = 1
num_cores = os.cpu_count()

# Set the number of addresses to generate
num_addresses = 1000000

# Define a worker function that generates a batch of addresses and returns them
def worker(start, end):
  # Generate a NumPy array of random private keys using "secrets" library
  private_keys = np.array([secrets.randbelow(2**256) for _ in range(start, end)])

  # Use secp256k1 to convert the private keys to addresses
  thread_addresses = np.array([ice.privatekey_to_address(2, True, dec) for dec in private_keys])

  return thread_addresses

# Use a ProcessPoolExecutor to generate the addresses in parallel
with concurrent.futures.ProcessPoolExecutor() as executor:
  # Divide the addresses evenly among the available CPU cores
  addresses_per_core = num_addresses // num_cores

  # Submit a task for each batch of addresses to the executor
  tasks = []
  for i in range(num_cores):
    start = i * addresses_per_core
    end = (i+1) * addresses_per_core
    tasks.append(executor.submit(worker, start, end))

  # Wait for the tasks to complete and retrieve the results
  addresses = []
  for task in concurrent.futures.as_completed(tasks):
    addresses.extend(task.result())

# Write the addresses to a file
np.savetxt('addresses_1M_multicore_secrets.txt', addresses, fmt='%s')


To speed up slightly this code (with multi core), you can write on different files:

Code:
#!/usr/bin/env python3
# 2023/Jan/01, citb0in_multicore_secrets.py
import concurrent.futures
import os
import numpy as np
import secrets
import secp256k1 as ice

# how many cores to use
#num_cores = 1
num_cores = os.cpu_count()

# Set the number of addresses to generate
num_addresses = 1000000

# Define a worker function that generates a batch of addresses and returns them
def worker(start, end, i):
  # Generate a NumPy array of random private keys using "secrets" library
  private_keys = np.array([secrets.randbelow(2**256) for _ in range(start, end)])

  # Use secp256k1 to convert the private keys to addresses
  thread_addresses = np.array([ice.privatekey_to_address(2, True, dec) for dec in private_keys])
  np.savetxt('addresses_1M_multicore_secrets'+ str(i) +'.txt', thread_addresses, fmt='%s')
 
  return

# Use a ProcessPoolExecutor to generate the addresses in parallel
with concurrent.futures.ProcessPoolExecutor() as executor:
  # Divide the addresses evenly among the available CPU cores
  addresses_per_core = num_addresses // num_cores
 
  # Submit a task for each batch of addresses to the executor
  tasks = []
  for i in range(num_cores):
    start = i * addresses_per_core
    end = (i+1) * addresses_per_core
    tasks.append(executor.submit(worker, start, end, i))

Your code doesn't store private keys, are you sure you want to have only a list of addresses without their private keys?
Pages:
Jump to: