/*
* This file is part of the VanitySearch distribution (https://github.com/JeanLucPons/VanitySearch).
* Copyright (c) 2019 Jean Luc PONS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "Vanity.h"
#include "Base58.h"
#include "Bech32.h"
#include "hash/sha256.h"
#include "hash/sha512.h"
#include "IntGroup.h"
#include "Wildcard.h"
#include "Timer.h"
#include "hash/ripemd160.h"
#include
#include
#include
#ifndef WIN64
#include
#endif
using namespace std;
Point Gn[CPU_GRP_SIZE / 2];
Point _2Gn;
// ----------------------------------------------------------------------------
VanitySearch::VanitySearch(Secp256K1 *secp, vector &inputPrefixes,string seed,int searchMode,
bool useGpu, bool stop, string outputFile, bool useSSE, uint32_t maxFound,
uint64_t rekey, bool caseSensitive, Point &startPubKey, bool paranoiacSeed)
:inputPrefixes(inputPrefixes) {
this->secp = secp;
this->searchMode = searchMode;
this->useGpu = useGpu;
this->stopWhenFound = stop;
this->outputFile = outputFile;
this->useSSE = useSSE;
this->nbGPUThread = 0;
this->maxFound = maxFound;
this->rekey = rekey;
this->searchType = -1;
this->startPubKey = startPubKey;
this->hasPattern = false;
this->caseSensitive = caseSensitive;
this->startPubKeySpecified = !startPubKey.isZero();
lastRekey = 0;
prefixes.clear();
// Create a 65536 items lookup table
PREFIX_TABLE_ITEM t;
t.found = true;
t.items = NULL;
for(int i=0;i<65536;i++)
prefixes.push_back(t);
// Check is inputPrefixes contains wildcard character
for (int i = 0; i < (int)inputPrefixes.size() && !hasPattern; i++) {
hasPattern = ((inputPrefixes[i].find('*') != std::string::npos) ||
(inputPrefixes[i].find('?') != std::string::npos) );
}
if (!hasPattern) {
// No wildcard used, standard search
// Insert prefixes
bool loadingProgress = (inputPrefixes.size() > 1000);
if (loadingProgress)
printf("[Building lookup16 0.0%%]\r");
nbPrefix = 0;
onlyFull = true;
for (int i = 0; i < (int)inputPrefixes.size(); i++) {
PREFIX_ITEM it;
std::vector itPrefixes;
if (!caseSensitive) {
// For caseunsensitive search, loop through all possible combination
// and fill up lookup table
vector subList;
enumCaseUnsentivePrefix(inputPrefixes[i], subList);
bool *found = new bool;
*found = false;
for (int j = 0; j < (int)subList.size(); j++) {
if (initPrefix(subList[j], &it)) {
it.found = found;
it.prefix = strdup(it.prefix); // We need to allocate here, subList will be destroyed
itPrefixes.push_back(it);
}
}
if (itPrefixes.size() > 0) {
// Compute difficulty for case unsensitive search
// Not obvious to perform the right calculation here using standard double
// Improvement are welcome
// Get the min difficulty and divide by the number of item having the same difficulty
// Should give good result when difficulty is large enough
double dMin = itPrefixes[0].difficulty;
int nbMin = 1;
for (int j = 1; j < (int)itPrefixes.size(); j++) {
if (itPrefixes[j].difficulty == dMin) {
nbMin++;
} else if (itPrefixes[j].difficulty < dMin) {
dMin = itPrefixes[j].difficulty;
nbMin = 1;
}
}
dMin /= (double)nbMin;
// Updates
for (int j = 0; j < (int)itPrefixes.size(); j++)
itPrefixes[j].difficulty = dMin;
}
} else {
if (initPrefix(inputPrefixes[i], &it)) {
bool *found = new bool;
*found = false;
it.found = found;
itPrefixes.push_back(it);
}
}
if (itPrefixes.size() > 0) {
// Add the item to all correspoding prefixes in the lookup table
for (int j = 0; j < (int)itPrefixes.size(); j++) {
prefix_t p = itPrefixes[j].sPrefix;
if (prefixes[p].items == NULL) {
prefixes[p].items = new vector();
prefixes[p].found = false;
usedPrefix.push_back(p);
}
(*prefixes[p].items).push_back(itPrefixes[j]);
}
onlyFull &= it.isFull;
nbPrefix++;
}
if (loadingProgress && i % 1000 == 0)
printf("[Building lookup16 %5.1f%%]\r", (((double)i) / (double)(inputPrefixes.size() - 1)) * 100.0);
}
if (loadingProgress)
printf("\n");
//dumpPrefixes();
if (!caseSensitive && searchType == BECH32) {
printf("Error, case unsensitive search with BECH32 not allowed.\n");
exit(1);
}
if (nbPrefix == 0) {
printf("VanitySearch: nothing to search !\n");
exit(1);
}
// Second level lookup
uint32_t unique_sPrefix = 0;
uint32_t minI = 0xFFFFFFFF;
uint32_t maxI = 0;
for (int i = 0; i < (int)prefixes.size(); i++) {
if (prefixes[i].items) {
LPREFIX lit;
lit.sPrefix = i;
if (prefixes[i].items) {
for (int j = 0; j < (int)prefixes[i].items->size(); j++) {
lit.lPrefixes.push_back((*prefixes[i].items)[j].lPrefix);
}
}
sort(lit.lPrefixes.begin(), lit.lPrefixes.end());
usedPrefixL.push_back(lit);
if ((uint32_t)lit.lPrefixes.size() > maxI) maxI = (uint32_t)lit.lPrefixes.size();
if ((uint32_t)lit.lPrefixes.size() < minI) minI = (uint32_t)lit.lPrefixes.size();
unique_sPrefix++;
}
if (loadingProgress)
printf("[Building lookup32 %.1f%%]\r", ((double)i*100.0) / (double)prefixes.size());
}
if (loadingProgress)
printf("\n");
_difficulty = getDiffuclty();
string seachInfo = string(searchModes[searchMode]) + (startPubKeySpecified ? ", with public key" : "");
if (nbPrefix == 1) {
if (!caseSensitive) {
// Case unsensitive search
printf("Difficulty: %.0f\n", _difficulty);
printf("Search: %s [%s, Case unsensitive] (Lookup size %d)\n", inputPrefixes[0].c_str(), seachInfo.c_str(), unique_sPrefix);
} else {
printf("Difficulty: %.0f\n", _difficulty);
printf("Search: %s [%s]\n", inputPrefixes[0].c_str(), seachInfo.c_str());
}
} else {
if (onlyFull) {
printf("Search: %d addresses (Lookup size %d,[%d,%d]) [%s]\n", nbPrefix, unique_sPrefix, minI, maxI, seachInfo.c_str());
} else {
printf("Search: %d prefixes (Lookup size %d) [%s]\n", nbPrefix, unique_sPrefix, seachInfo.c_str());
}
}
} else {
// Wild card search
switch (inputPrefixes[0].data()[0]) {
case '1':
searchType = P2PKH;
break;
case '3':
searchType = P2SH;
break;
case 'b':
case 'B':
searchType = BECH32;
break;
default:
printf("Invalid start character 1,3 or b, expected");
exit(1);
}
string searchInfo = string(searchModes[searchMode]) + (startPubKeySpecified ? ", with public key" : "");
if (inputPrefixes.size() == 1) {
printf("Search: %s [%s]\n", inputPrefixes[0].c_str(), searchInfo.c_str());
} else {
printf("Search: %d patterns [%s]\n", (int)inputPrefixes.size(), searchInfo.c_str());
}
patternFound = (bool *)malloc(inputPrefixes.size()*sizeof(bool));
memset(patternFound,0, inputPrefixes.size() * sizeof(bool));
}
// Compute Generator table G[n] = (n+1)*G
Point g = secp->G;
Gn[0] = g;
g = secp->DoubleDirect(g);
Gn[1] = g;
for (int i = 2; i < CPU_GRP_SIZE/2; i++) {
g = secp->AddDirect(g,secp->G);
Gn[i] = g;
}
// _2Gn = CPU_GRP_SIZE*G
_2Gn = secp->DoubleDirect(Gn[CPU_GRP_SIZE/2-1]);
// Constant for endomorphism
// if a is a nth primitive root of unity, a^-1 is also a nth primitive root.
// beta^3 = 1 mod p implies also beta^2 = beta^-1 mop (by multiplying both side by beta^-1)
// (beta^3 = 1 mod p), beta2 = beta^-1 = beta^2
// (lambda^3 = 1 mod n), lamba2 = lamba^-1 = lamba^2
beta.SetBase16("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee");
lambda.SetBase16("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72");
beta2.SetBase16("851695d49a83f8ef919bb86153cbcb16630fb68aed0a766a3ec693d68e6afa40");
lambda2.SetBase16("ac9c52b33fa3cf1f5ad9e3fd77ed9ba4a880b9fc8ec739c2e0cfc810b51283ce");
// Seed
if (seed.length() == 0) {
// Default seed
seed = Timer::getSeed(32);
}
if (paranoiacSeed) {
seed += Timer::getSeed(32);
}
// Protect seed against "seed search attack" using pbkdf2_hmac_sha512
string salt = "VanitySearch";
unsigned char hseed[64];
pbkdf2_hmac_sha512(hseed, 64, (const uint8_t *)seed.c_str(), seed.length(),
(const uint8_t *)salt.c_str(), salt.length(),
2048);
startKey.SetInt32(0);
sha256(hseed, 64, (unsigned char *)startKey.bits64);
char *ctimeBuff;
time_t now = time(NULL);
ctimeBuff = ctime(&now);
printf("Start %s", ctimeBuff);
if (rekey > 0) {
printf("Base Key: Randomly changed every %.0f Mkeys\n",(double)rekey);
} else {
printf("Base Key: %s\n", startKey.GetBase16().c_str());
}
}
// ----------------------------------------------------------------------------
bool VanitySearch::isSingularPrefix(std::string pref) {
// check is the given prefix contains only 1
bool only1 = true;
int i=0;
while (only1 && i < (int)pref.length()) {
only1 = pref.data()[i] == '1';
i++;
}
return only1;
}
// ----------------------------------------------------------------------------
bool VanitySearch::initPrefix(std::string &prefix,PREFIX_ITEM *it) {
std::vector result;
string dummy1 = prefix;
int nbDigit = 0;
bool wrong = false;
if (prefix.length() < 2) {
printf("Ignoring prefix \"%s\" (too short)\n",prefix.c_str());
return false;
}
int aType = -1;
switch (prefix.data()[0]) {
case '1':
aType = P2PKH;
break;
case '3':
aType = P2SH;
break;
case 'b':
case 'B':
std::transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
if(strncmp(prefix.c_str(), "bc1q", 4) == 0)
aType = BECH32;
break;
}
if (aType==-1) {
printf("Ignoring prefix \"%s\" (must start with 1 or 3 or bc1q)\n", prefix.c_str());
return false;
}
if (searchType == -1) searchType = aType;
if (aType != searchType) {
printf("Ignoring prefix \"%s\" (P2PKH, P2SH or BECH32 allowed at once)\n", prefix.c_str());
return false;
}
if (aType == BECH32) {
// BECH32
uint8_t witprog[40];
size_t witprog_len;
int witver;
const char* hrp = "bc";
int ret = segwit_addr_decode(&witver, witprog, &witprog_len, hrp, prefix.c_str());
// Try to attack a full address ?
if (ret && witprog_len==20) {
// mamma mia !
it->difficulty = pow(2, 160);
it->isFull = true;
memcpy(it->hash160, witprog, 20);
it->sPrefix = *(prefix_t *)(it->hash160);
it->lPrefix = *(prefixl_t *)(it->hash160);
it->prefix = (char *)prefix.c_str();
it->prefixLength = (int)prefix.length();
return true;
}
if (prefix.length() < 5) {
printf("Ignoring prefix \"%s\" (too short, length<5 )\n", prefix.c_str());
return false;
}
if (prefix.length() >= 36) {
printf("Ignoring prefix \"%s\" (too long, length>36 )\n", prefix.c_str());
return false;
}
uint8_t data[64];
memset(data,0,64);
size_t data_length;
if(!bech32_decode_nocheck(data,&data_length,prefix.c_str()+4)) {
printf("Ignoring prefix \"%s\" (Only \"023456789acdefghjklmnpqrstuvwxyz\" allowed)\n", prefix.c_str());
return false;
}
// Difficulty
it->sPrefix = *(prefix_t *)data;
it->difficulty = pow(2, 5*(prefix.length()-4));
it->isFull = false;
it->lPrefix = 0;
it->prefix = (char *)prefix.c_str();
it->prefixLength = (int)prefix.length();
return true;
} else {
// P2PKH/P2SH
wrong = !DecodeBase58(prefix, result);
if (wrong) {
if (caseSensitive)
printf("Ignoring prefix \"%s\" (0, I, O and l not allowed)\n", prefix.c_str());
return false;
}
// Try to attack a full address ?
if (result.size() > 21) {
// mamma mia !
//if (!secp.CheckPudAddress(prefix)) {
// printf("Warning, \"%s\" (address checksum may never match)\n", prefix.c_str());
//}
it->difficulty = pow(2, 160);
it->isFull = true;
memcpy(it->hash160, result.data() + 1, 20);
it->sPrefix = *(prefix_t *)(it->hash160);
it->lPrefix = *(prefixl_t *)(it->hash160);
it->prefix = (char *)prefix.c_str();
it->prefixLength = (int)prefix.length();
return true;
}
// Prefix containing only '1'
if (isSingularPrefix(prefix)) {
if (prefix.length() > 21) {
printf("Ignoring prefix \"%s\" (Too much 1)\n", prefix.c_str());
return false;
}
// Difficulty
it->difficulty = pow(256, prefix.length() - 1);
it->isFull = false;
it->sPrefix = 0;
it->lPrefix = 0;
it->prefix = (char *)prefix.c_str();
it->prefixLength = (int)prefix.length();
return true;
}
// Search for highest hash160 16bit prefix (most probable)
while (result.size() < 25) {
DecodeBase58(dummy1, result);
if (result.size() < 25) {
dummy1.append("1");
nbDigit++;
}
}
if (searchType == P2SH) {
if (result.data()[0] != 5) {
if(caseSensitive)
printf("Ignoring prefix \"%s\" (Unreachable, 31h1 to 3R2c only)\n", prefix.c_str());
return false;
}
}
if (result.size() != 25) {
printf("Ignoring prefix \"%s\" (Invalid size)\n", prefix.c_str());
return false;
}
//printf("VanitySearch: Found prefix %s\n",GetHex(result).c_str() );
it->sPrefix = *(prefix_t *)(result.data() + 1);
dummy1.append("1");
DecodeBase58(dummy1, result);
if (result.size() == 25) {
//printf("VanitySearch: Found prefix %s\n", GetHex(result).c_str());
it->sPrefix = *(prefix_t *)(result.data() + 1);
nbDigit++;
}
// Difficulty
it->difficulty = pow(2, 192) / pow(58, nbDigit);
it->isFull = false;
it->lPrefix = 0;
it->prefix = (char *)prefix.c_str();
it->prefixLength = (int)prefix.length();
return true;
}
}
// ----------------------------------------------------------------------------
void VanitySearch::dumpPrefixes() {
for (int i = 0; i < 0xFFFF; i++) {
if (prefixes[i].items) {
printf("%04X\n", i);
for (int j = 0; j < (int)prefixes[i].items->size(); j++) {
printf(" %d\n", (*prefixes[i].items)[j].sPrefix);
printf(" %g\n", (*prefixes[i].items)[j].difficulty);
printf(" %s\n", (*prefixes[i].items)[j].prefix);
}
}
}
}
// ----------------------------------------------------------------------------
void VanitySearch::enumCaseUnsentivePrefix(std::string s, std::vector &list) {
char letter[64];
int letterpos[64];
int nbLetter = 0;
int length = (int)s.length();
for (int i = 1; i < length; i++) {
char c = s.data()[i];
if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ) {
letter[nbLetter] = tolower(c);
letterpos[nbLetter] = i;
nbLetter++;
}
}
int total = 1 << nbLetter;
for (int i = 0; i < total; i++) {
char tmp[64];
strcpy(tmp, s.c_str());
for (int j = 0; j < nbLetter; j++) {
int mask = 1 << j;
if (mask&i) tmp[letterpos[j]] = toupper(letter[j]);
else tmp[letterpos[j]] = letter[j];
}
list.push_back(string(tmp));
}
}
// ----------------------------------------------------------------------------
double VanitySearch::getDiffuclty() {
double min = pow(2,160);
if (onlyFull)
return min;
for (int i = 0; i < (int)usedPrefix.size(); i++) {
int p = usedPrefix[i];
if (prefixes[p].items) {
for (int j = 0; j < (int)prefixes[p].items->size(); j++) {
if (!*((*prefixes[p].items)[j].found)) {
if ((*prefixes[p].items)[j].difficulty < min)
min = (*prefixes[p].items)[j].difficulty;
}
}
}
}
return min;
}
double log1(double x) {
// Use taylor series to approximate log(1-x)
return -x - (x*x)/2.0 - (x*x*x)/3.0 - (x*x*x*x)/4.0;
}
string VanitySearch::GetExpectedTime(double keyRate,double keyCount) {
char tmp[128];
string ret;
if(hasPattern)
return "";
double P = 1.0/ _difficulty;
// pow(1-P,keyCount) is the probality of failure after keyCount tries
double cP = 1.0 - pow(1-P,keyCount);
sprintf(tmp,"[Prob %.1f%%]",cP*100.0);
ret = string(tmp);
double desiredP = 0.5;
while(desiredP=0.99) desiredP = 0.99;
double k = log(1.0-desiredP)/log(1.0-P);
if (isinf(k)) {
// Try taylor
k = log(1.0 - desiredP)/log1(P);
}
double dTime = (k-keyCount)/keyRate; // Time to perform k tries
if(dTime<0) dTime = 0;
double nbDay = dTime / 86400.0;
if (nbDay >= 1) {
double nbYear = nbDay/365.0;
if (nbYear > 1) {
if(nbYear<5)
sprintf(tmp, "[%.f%% in %.1fy]", desiredP*100.0, nbYear);
else
sprintf(tmp, "[%.f%% in %gy]", desiredP*100.0, nbYear);
} else {
sprintf(tmp, "[%.f%% in %.1fd]", desiredP*100.0, nbDay);
}
} else {
int iTime = (int)dTime;
int nbHour = (int)((iTime % 86400) / 3600);
int nbMin = (int)(((iTime % 86400) % 3600) / 60);
int nbSec = (int)(iTime % 60);
sprintf(tmp, "[%.f%% in %02d:%02d:%02d]", desiredP*100.0, nbHour, nbMin, nbSec);
}
return ret + string(tmp);
}
// ----------------------------------------------------------------------------
void VanitySearch::output(string addr,string pAddr,string pAddrHex) {
#ifdef WIN64
WaitForSingleObject(ghMutex,INFINITE);
#else
pthread_mutex_lock(&ghMutex);
#endif
FILE *f = stdout;
bool needToClose = false;
if (outputFile.length() > 0) {
f = fopen(outputFile.c_str(), "a");
if (f == NULL) {
printf("Cannot open %s for writing\n", outputFile.c_str());
f = stdout;
} else {
needToClose = true;
}
}
if(!needToClose)
printf("\n");
fprintf(f, "PubAddress: %s\n", addr.c_str());
if (startPubKeySpecified) {
fprintf(f, "PartialPriv: %s\n", pAddr.c_str());
} else {
switch (searchType) {
case P2PKH:
fprintf(f, "Priv (WIF): p2pkh:%s\n", pAddr.c_str());
break;
case P2SH:
fprintf(f, "Priv (WIF): p2wpkh-p2sh:%s\n", pAddr.c_str());
break;
case BECH32:
fprintf(f, "Priv (WIF): p2wpkh:%s\n", pAddr.c_str());
break;
}
fprintf(f, "Priv (HEX): 0x%s\n", pAddrHex.c_str());
}
if(needToClose)
fclose(f);
#ifdef WIN64
ReleaseMutex(ghMutex);
#else
pthread_mutex_unlock(&ghMutex);
#endif
}
// ----------------------------------------------------------------------------
void VanitySearch::updateFound() {
// Check if all prefixes has been found
// Needed only if stopWhenFound is asked
if (stopWhenFound) {
if (hasPattern) {
bool allFound = true;
for (int i = 0; i < (int)inputPrefixes.size(); i++) {
allFound &= patternFound[i];
}
endOfSearch = allFound;
} else {
bool allFound = true;
for (int i = 0; i < (int)usedPrefix.size(); i++) {
bool iFound = true;
prefix_t p = usedPrefix[i];
if (!prefixes[p].found) {
if (prefixes[p].items) {
for (int j = 0; j < (int)prefixes[p].items->size(); j++) {
iFound &= *((*prefixes[p].items)[j].found);
}
}
prefixes[usedPrefix[i]].found = iFound;
}
allFound &= iFound;
}
endOfSearch = allFound;
// Update difficulty to the next most probable item
_difficulty = getDiffuclty();
}
}
}
// ----------------------------------------------------------------------------
bool VanitySearch::checkPrivKey(string addr, Int &key, int32_t incr, int endomorphism, bool mode) {
Int k(&key);
Point sp = startPubKey;
if (incr < 0) {
k.Add((uint64_t)(-incr));
k.Neg();
k.Add(&secp->order);
if (startPubKeySpecified) sp.y.ModNeg();
} else {
k.Add((uint64_t)incr);
}
// Endomorphisms
switch (endomorphism) {
case 1:
k.ModMulK1order(&lambda);
if(startPubKeySpecified) sp.x.ModMulK1(&beta);
break;
case 2:
k.ModMulK1order(&lambda2);
if (startPubKeySpecified) sp.x.ModMulK1(&beta2);
break;
}
// Check addresses
Point p = secp->ComputePublicKey(&k);
if (startPubKeySpecified) p = secp->AddDirect(p, sp);
string chkAddr = secp->GetAddress(searchType, mode, p);
if (chkAddr != addr) {
//Key may be the opposite one (negative zero or compressed key)
k.Neg();
k.Add(&secp->order);
p = secp->ComputePublicKey(&k);
if (startPubKeySpecified) {
sp.y.ModNeg();
p = secp->AddDirect(p, sp);
}
string chkAddr = secp->GetAddress(searchType, mode, p);
if (chkAddr != addr) {
printf("\nWarning, wrong private key generated !\n");
printf(" Addr :%s\n", addr.c_str());
printf(" Check:%s\n", chkAddr.c_str());
printf(" Endo:%d incr:%d comp:%d\n", endomorphism, incr, mode);
return false;
}
}
output(addr, secp->GetPrivAddress(mode ,k), k.GetBase16());
return true;
}
void VanitySearch::checkAddrSSE(uint8_t *h1, uint8_t *h2, uint8_t *h3, uint8_t *h4,
int32_t incr1, int32_t incr2, int32_t incr3, int32_t incr4,
Int &key, int endomorphism, bool mode) {
vector addr = secp->GetAddress(searchType, mode, h1,h2,h3,h4);
for (int i = 0; i < (int)inputPrefixes.size(); i++) {
if (Wildcard::match(addr[0].c_str(), inputPrefixes[i].c_str(), caseSensitive)) {
// Found it !
//*((*pi)[i].found) = true;
if (checkPrivKey(addr[0], key, incr1, endomorphism, mode)) {
nbFoundKey++;
patternFound[i] = true;
updateFound();
}
}
if (Wildcard::match(addr[1].c_str(), inputPrefixes[i].c_str(), caseSensitive)) {
// Found it !
//*((*pi)[i].found) = true;
if (checkPrivKey(addr[1], key, incr2, endomorphism, mode)) {
nbFoundKey++;
patternFound[i] = true;
updateFound();
}
}
if (Wildcard::match(addr[2].c_str(), inputPrefixes[i].c_str(), caseSensitive)) {
// Found it !
//*((*pi)[i].found) = true;
if (checkPrivKey(addr[2], key, incr3, endomorphism, mode)) {
nbFoundKey++;
patternFound[i] = true;
updateFound();
}
}
if (Wildcard::match(addr[3].c_str(), inputPrefixes[i].c_str(), caseSensitive)) {
// Found it !
//*((*pi)[i].found) = true;
if (checkPrivKey(addr[3], key, incr4, endomorphism, mode)) {
nbFoundKey++;
patternFound[i] = true;
updateFound();
}
}
}
}
void VanitySearch::checkAddr(int prefIdx, uint8_t *hash160, Int &key, int32_t incr, int endomorphism, bool mode) {
if (hasPattern) {
// Wildcard search
string addr = secp->GetAddress(searchType, mode, hash160);
for (int i = 0; i < (int)inputPrefixes.size(); i++) {
if (Wildcard::match(addr.c_str(), inputPrefixes[i].c_str(), caseSensitive)) {
// Found it !
//*((*pi)[i].found) = true;
if (checkPrivKey(addr, key, incr, endomorphism, mode)) {
nbFoundKey++;
patternFound[i] = true;
updateFound();
}
}
}
return;
}
vector *pi = prefixes[prefIdx].items;
if (onlyFull) {
// Full addresses
for (int i = 0; i < (int)pi->size(); i++) {
if (stopWhenFound && *((*pi)[i].found))
continue;
if (ripemd160_comp_hash((*pi)[i].hash160, hash160)) {
// Found it !
*((*pi)[i].found) = true;
// You believe it ?
if (checkPrivKey(secp->GetAddress(searchType, mode, hash160), key, incr, endomorphism, mode)) {
nbFoundKey++;
updateFound();
}
}
}
} else {
char a[64];
string addr = secp->GetAddress(searchType, mode, hash160);
for (int i = 0; i < (int)pi->size(); i++) {
if (stopWhenFound && *((*pi)[i].found))
continue;
strncpy(a, addr.c_str(), (*pi)[i].prefixLength);
a[(*pi)[i].prefixLength] = 0;
if (strcmp((*pi)[i].prefix, a) == 0) {
// Found it !
*((*pi)[i].found) = true;
if (checkPrivKey(addr, key, incr, endomorphism, mode)) {
nbFoundKey++;
updateFound();
}
}
}
}
}
// ----------------------------------------------------------------------------
#ifdef WIN64
DWORD WINAPI _FindKey(LPVOID lpParam) {
#else
void *_FindKey(void *lpParam) {
#endif
TH_PARAM *p = (TH_PARAM *)lpParam;
p->obj->FindKeyCPU(p);
return 0;
}
#ifdef WIN64
DWORD WINAPI _FindKeyGPU(LPVOID lpParam) {
#else
void *_FindKeyGPU(void *lpParam) {
#endif
TH_PARAM *p = (TH_PARAM *)lpParam;
p->obj->FindKeyGPU(p);
return 0;
}
// ----------------------------------------------------------------------------
void VanitySearch::checkAddresses(bool compressed, Int key, int i, Point p1) {
unsigned char h0[20];
Point pte1[1];
Point pte2[1];
// Point
secp->GetHash160(searchType,compressed, p1, h0);
prefix_t pr0 = *(prefix_t *)h0;
if (hasPattern || prefixes[pr0].items)
checkAddr(pr0, h0, key, i, 0, compressed);
// Endomorphism #1
pte1[0].x.ModMulK1(&p1.x, &beta);
pte1[0].y.Set(&p1.y);
secp->GetHash160(searchType, compressed, pte1[0], h0);
pr0 = *(prefix_t *)h0;
if (hasPattern || prefixes[pr0].items)
checkAddr(pr0, h0, key, i, 1, compressed);
// Endomorphism #2
pte2[0].x.ModMulK1(&p1.x, &beta2);
pte2[0].y.Set(&p1.y);
secp->GetHash160(searchType, compressed, pte2[0], h0);
pr0 = *(prefix_t *)h0;
if (hasPattern || prefixes[pr0].items)
checkAddr(pr0, h0, key, i, 2, compressed);
// Curve symetrie
// if (x,y) = k*G, then (x, -y) is -k*G
p1.y.ModNeg();
secp->GetHash160(searchType, compressed, p1, h0);
pr0 = *(prefix_t *)h0;
if (hasPattern || prefixes[pr0].items)
checkAddr(pr0, h0, key, -i, 0, compressed);
// Endomorphism #1
pte1[0].y.ModNeg();
secp->GetHash160(searchType, compressed, pte1[0], h0);
pr0 = *(prefix_t *)h0;
if (hasPattern || prefixes[pr0].items)
checkAddr(pr0, h0, key, -i, 1, compressed);
// Endomorphism #2
pte2[0].y.ModNeg();
secp->GetHash160(searchType, compressed, pte2[0], h0);
pr0 = *(prefix_t *)h0;
if (hasPattern || prefixes[pr0].items)
checkAddr(pr0, h0, key, -i, 2, compressed);
}
// ----------------------------------------------------------------------------
void VanitySearch::checkAddressesSSE(bool compressed,Int key, int i, Point p1, Point p2, Point p3, Point p4) {
unsigned char h0[20];
unsigned char h1[20];
unsigned char h2[20];
unsigned char h3[20];
Point pte1[4];
Point pte2[4];
prefix_t pr0;
prefix_t pr1;
prefix_t pr2;
prefix_t pr3;
// Point -------------------------------------------------------------------------
secp->GetHash160(searchType, compressed, p1, p2, p3, p4, h0, h1, h2, h3);
if (!hasPattern) {
pr0 = *(prefix_t *)h0;
pr1 = *(prefix_t *)h1;
pr2 = *(prefix_t *)h2;
pr3 = *(prefix_t *)h3;
if (prefixes[pr0].items)
checkAddr(pr0, h0, key, i, 0, compressed);
if (prefixes[pr1].items)
checkAddr(pr1, h1, key, i + 1, 0, compressed);
if (prefixes[pr2].items)
checkAddr(pr2, h2, key, i + 2, 0, compressed);
if (prefixes[pr3].items)
checkAddr(pr3, h3, key, i + 3, 0, compressed);
} else {
checkAddrSSE(h0,h1,h2,h3,i,i+1,i+2,i+3,key,0,compressed);
}
// Endomorphism #1
// if (x, y) = k * G, then (beta*x, y) = lambda*k*G
pte1[0].x.ModMulK1(&p1.x, &beta);
pte1[0].y.Set(&p1.y);
pte1[1].x.ModMulK1(&p2.x, &beta);
pte1[1].y.Set(&p2.y);
pte1[2].x.ModMulK1(&p3.x, &beta);
pte1[2].y.Set(&p3.y);
pte1[3].x.ModMulK1(&p4.x, &beta);
pte1[3].y.Set(&p4.y);
secp->GetHash160(searchType, compressed, pte1[0], pte1[1], pte1[2], pte1[3], h0, h1, h2, h3);
if (!hasPattern) {
pr0 = *(prefix_t *)h0;
pr1 = *(prefix_t *)h1;
pr2 = *(prefix_t *)h2;
pr3 = *(prefix_t *)h3;
if (prefixes[pr0].items)
checkAddr(pr0, h0, key, i, 1, compressed);
if (prefixes[pr1].items)
checkAddr(pr1, h1, key, (i + 1), 1, compressed);
if (prefixes[pr2].items)
checkAddr(pr2, h2, key, (i + 2), 1, compressed);
if (prefixes[pr3].items)
checkAddr(pr3, h3, key, (i + 3), 1, compressed);
} else {
checkAddrSSE(h0, h1, h2, h3, i, i + 1, i + 2, i + 3, key, 1, compressed);
}
// Endomorphism #2
// if (x, y) = k * G, then (beta2*x, y) = lambda2*k*G
pte2[0].x.ModMulK1(&p1.x, &beta2);
pte2[0].y.Set(&p1.y);
pte2[1].x.ModMulK1(&p2.x, &beta2);
pte2[1].y.Set(&p2.y);
pte2[2].x.ModMulK1(&p3.x, &beta2);
pte2[2].y.Set(&p3.y);
pte2[3].x.ModMulK1(&p4.x, &beta2);
pte2[3].y.Set(&p4.y);
secp->GetHash160(searchType, compressed, pte2[0], pte2[1], pte2[2], pte2[3], h0, h1, h2, h3);
if (!hasPattern) {
pr0 = *(prefix_t *)h0;
pr1 = *(prefix_t *)h1;
pr2 = *(prefix_t *)h2;
pr3 = *(prefix_t *)h3;
if (prefixes[pr0].items)
checkAddr(pr0, h0, key, i, 2, compressed);
if (prefixes[pr1].items)
checkAddr(pr1, h1, key, (i + 1), 2, compressed);
if (prefixes[pr2].items)
checkAddr(pr2, h2, key, (i + 2), 2, compressed);
if (prefixes[pr3].items)
checkAddr(pr3, h3, key, (i + 3), 2, compressed);
} else {
checkAddrSSE(h0, h1, h2, h3, i, i + 1, i + 2, i + 3, key, 2, compressed);
}
// Curve symetrie -------------------------------------------------------------------------
// if (x,y) = k*G, then (x, -y) is -k*G
p1.y.ModNeg();
p2.y.ModNeg();
p3.y.ModNeg();
p4.y.ModNeg();
secp->GetHash160(searchType, compressed, p1, p2, p3, p4, h0, h1, h2, h3);
if (!hasPattern) {
pr0 = *(prefix_t *)h0;
pr1 = *(prefix_t *)h1;
pr2 = *(prefix_t *)h2;
pr3 = *(prefix_t *)h3;
if (prefixes[pr0].items)
checkAddr(pr0, h0, key, -i, 0, compressed);
if (prefixes[pr1].items)
checkAddr(pr1, h1, key, -(i + 1), 0, compressed);
if (prefixes[pr2].items)
checkAddr(pr2, h2, key, -(i + 2), 0, compressed);
if (prefixes[pr3].items)
checkAddr(pr3, h3, key, -(i + 3), 0, compressed);
} else {
checkAddrSSE(h0, h1, h2, h3, -i, -(i + 1), -(i + 2), -(i + 3), key, 0, compressed);
}
// Endomorphism #1
// if (x, y) = k * G, then (beta*x, y) = lambda*k*G
pte1[0].y.ModNeg();
pte1[1].y.ModNeg();
pte1[2].y.ModNeg();
pte1[3].y.ModNeg();
secp->GetHash160(searchType, compressed, pte1[0], pte1[1], pte1[2], pte1[3], h0, h1, h2, h3);
if (!hasPattern) {
pr0 = *(prefix_t *)h0;
pr1 = *(prefix_t *)h1;
pr2 = *(prefix_t *)h2;
pr3 = *(prefix_t *)h3;
if (prefixes[pr0].items)
checkAddr(pr0, h0, key, -i, 1, compressed);
if (prefixes[pr1].items)
checkAddr(pr1, h1, key, -(i + 1), 1, compressed);
if (prefixes[pr2].items)
checkAddr(pr2, h2, key, -(i + 2), 1, compressed);
if (prefixes[pr3].items)
checkAddr(pr3, h3, key, -(i + 3), 1, compressed);
} else {
checkAddrSSE(h0, h1, h2, h3, -i, -(i + 1), -(i + 2), -(i + 3), key, 1, compressed);
}
// Endomorphism #2
// if (x, y) = k * G, then (beta2*x, y) = lambda2*k*G
pte2[0].y.ModNeg();
pte2[1].y.ModNeg();
pte2[2].y.ModNeg();
pte2[3].y.ModNeg();
secp->GetHash160(searchType, compressed, pte2[0], pte2[1], pte2[2], pte2[3], h0, h1, h2, h3);
if (!hasPattern) {
pr0 = *(prefix_t *)h0;
pr1 = *(prefix_t *)h1;
pr2 = *(prefix_t *)h2;
pr3 = *(prefix_t *)h3;
if (prefixes[pr0].items)
checkAddr(pr0, h0, key, -i, 2, compressed);
if (prefixes[pr1].items)
checkAddr(pr1, h1, key, -(i + 1), 2, compressed);
if (prefixes[pr2].items)
checkAddr(pr2, h2, key, -(i + 2), 2, compressed);
if (prefixes[pr3].items)
checkAddr(pr3, h3, key, -(i + 3), 2, compressed);
} else {
checkAddrSSE(h0, h1, h2, h3, -i, -(i + 1), -(i + 2), -(i + 3), key, 2, compressed);
}
}
// ----------------------------------------------------------------------------
void VanitySearch::getCPUStartingKey(int thId,Int& key,Point& startP) {
if (rekey > 0) {
key.Rand(256);
} else {
key.Set(&startKey);
Int off((int64_t)thId);
off.ShiftL(64);
key.Add(&off);
}
Int km(&key);
km.Add((uint64_t)CPU_GRP_SIZE / 2);
startP = secp->ComputePublicKey(&km);
if(startPubKeySpecified)
startP = secp->AddDirect(startP,startPubKey);
}
void VanitySearch::FindKeyCPU(TH_PARAM *ph) {
// Global init
int thId = ph->threadId;
counters[thId] = 0;
// CPU Thread
IntGroup *grp = new IntGroup(CPU_GRP_SIZE/2+1);
// Group Init
Int key;
Point startP;
getCPUStartingKey(thId,key,startP);
Int dx[CPU_GRP_SIZE/2+1];
Point pts[CPU_GRP_SIZE];
Int dy;
Int dyn;
Int _s;
Int _p;
Point pp;
Point pn;
grp->Set(dx);
ph->hasStarted = true;
ph->rekeyRequest = false;
while (!endOfSearch) {
if (ph->rekeyRequest) {
getCPUStartingKey(thId, key, startP);
ph->rekeyRequest = false;
}
// Fill group
int i;
int hLength = (CPU_GRP_SIZE / 2 - 1);
for (i = 0; i < hLength; i++) {
dx[i].ModSub(&Gn[i].x, &startP.x);
}
dx[i].ModSub(&Gn[i].x, &startP.x); // For the first point
dx[i+1].ModSub(&_2Gn.x, &startP.x); // For the next center point
// Grouped ModInv
grp->ModInv();
// We use the fact that P + i*G and P - i*G has the same deltax, so the same inverse
// We compute key in the positive and negative way from the center of the group
// center point
pts[CPU_GRP_SIZE/2] = startP;
for (i = 0; iisRunning = false;
}
// ----------------------------------------------------------------------------
void VanitySearch::getGPUStartingKeys(int thId, int groupSize, int nbThread, Int *keys, Point *p) {
for (int i = 0; i < nbThread; i++) {
if (rekey > 0) {
keys[i].Rand(256);
} else {
keys[i].Set(&startKey);
Int offT((uint64_t)i);
offT.ShiftL(80);
Int offG((uint64_t)thId);
offG.ShiftL(112);
keys[i].Add(&offT);
keys[i].Add(&offG);
}
Int k(keys + i);
// Starting key is at the middle of the group
k.Add((uint64_t)(groupSize / 2));
p[i] = secp->ComputePublicKey(&k);
if (startPubKeySpecified)
p[i] = secp->AddDirect(p[i], startPubKey);
}
}
void VanitySearch::FindKeyGPU(TH_PARAM *ph) {
bool ok = true;
#ifdef WITHGPU
// Global init
int thId = ph->threadId;
GPUEngine g(ph->gridSizeX,ph->gridSizeY, ph->gpuId, maxFound, (rekey!=0));
int nbThread = g.GetNbThread();
Point *p = new Point[nbThread];
Int *keys = new Int[nbThread];
vector- found;
printf("GPU: %s\n",g.deviceName.c_str());
counters[thId] = 0;
getGPUStartingKeys(thId, g.GetGroupSize(), nbThread, keys, p);
g.SetSearchMode(searchMode);
g.SetSearchType(searchType);
if (onlyFull) {
g.SetPrefix(usedPrefixL,nbPrefix);
} else {
if(hasPattern)
g.SetPattern(inputPrefixes[0].c_str());
else
g.SetPrefix(usedPrefix);
}
getGPUStartingKeys(thId, g.GetGroupSize(), nbThread, keys, p);
ok = g.SetKeys(p);
ph->rekeyRequest = false;
ph->hasStarted = true;
// GPU Thread
while (ok && !endOfSearch) {
if (ph->rekeyRequest) {
getGPUStartingKeys(thId, g.GetGroupSize(), nbThread, keys, p);
ok = g.SetKeys(p);
ph->rekeyRequest = false;
}
// Call kernel
ok = g.Launch(found);
for(int i=0;i<(int)found.size() && !endOfSearch;i++) {
ITEM it = found[i];
checkAddr(*(prefix_t *)(it.hash), it.hash, keys[it.thId], it.incr, it.endo, it.mode);
}
if (ok) {
for (int i = 0; i < nbThread; i++) {
keys[i].Add((uint64_t)STEP_SIZE);
}
counters[thId] += 6ULL * STEP_SIZE * nbThread; // Point + endo1 + endo2 + symetrics
}
}
delete[] keys;
delete[] p;
#else
ph->hasStarted = true;
printf("GPU code not compiled, use -DWITHGPU when compiling.\n");
#endif
ph->isRunning = false;
}
// ----------------------------------------------------------------------------
bool VanitySearch::isAlive(TH_PARAM *p) {
bool isAlive = true;
int total = nbCPUThread + nbGPUThread;
for(int i=0;i gpuId,std::vector gridSize) {
double t0;
double t1;
endOfSearch = false;
nbCPUThread = nbThread;
nbGPUThread = (useGpu?(int)gpuId.size():0);
nbFoundKey = 0;
memset(counters,0,sizeof(counters));
printf("Number of CPU thread: %d\n", nbCPUThread);
TH_PARAM *params = (TH_PARAM *)malloc((nbCPUThread + nbGPUThread) * sizeof(TH_PARAM));
memset(params,0,(nbCPUThread + nbGPUThread) * sizeof(TH_PARAM));
// Launch CPU threads
for (int i = 0; i < nbCPUThread; i++) {
params[i].obj = this;
params[i].threadId = i;
params[i].isRunning = true;
#ifdef WIN64
DWORD thread_id;
CreateThread(NULL, 0, _FindKey, (void*)(params+i), 0, &thread_id);
ghMutex = CreateMutex(NULL, FALSE, NULL);
#else
pthread_t thread_id;
pthread_create(&thread_id, NULL, &_FindKey, (void*)(params+i));
ghMutex = PTHREAD_MUTEX_INITIALIZER;
#endif
}
// Launch GPU threads
for (int i = 0; i < nbGPUThread; i++) {
params[nbCPUThread+i].obj = this;
params[nbCPUThread+i].threadId = 0x80L+i;
params[nbCPUThread+i].isRunning = true;
params[nbCPUThread+i].gpuId = gpuId[i];
params[nbCPUThread+i].gridSizeX = gridSize[2*i];
params[nbCPUThread+i].gridSizeY = gridSize[2*i+1];
#ifdef WIN64
DWORD thread_id;
CreateThread(NULL, 0, _FindKeyGPU, (void*)(params+(nbCPUThread+i)), 0, &thread_id);
#else
pthread_t thread_id;
pthread_create(&thread_id, NULL, &_FindKeyGPU, (void*)(params+(nbCPUThread+i)));
#endif
}
#ifndef WIN64
setvbuf(stdout, NULL, _IONBF, 0);
#endif
uint64_t lastCount = 0;
uint64_t gpuCount = 0;
uint64_t lastGPUCount = 0;
// Key rate smoothing filter
#define FILTER_SIZE 8
double lastkeyRate[FILTER_SIZE];
double lastGpukeyRate[FILTER_SIZE];
uint32_t filterPos = 0;
double keyRate = 0.0;
double gpuKeyRate = 0.0;
memset(lastkeyRate,0,sizeof(lastkeyRate));
memset(lastGpukeyRate,0,sizeof(lastkeyRate));
// Wait that all threads have started
while (!hasStarted(params)) {
Timer::SleepMillis(500);
}
t0 = Timer::get_tick();
startTime = t0;
while (isAlive(params)) {
int delay = 2000;
while (isAlive(params) && delay>0) {
Timer::SleepMillis(500);
delay -= 500;
}
gpuCount = getGPUCount();
uint64_t count = getCPUCount() + gpuCount;
t1 = Timer::get_tick();
keyRate = (double)(count - lastCount) / (t1 - t0);
gpuKeyRate = (double)(gpuCount - lastGPUCount) / (t1 - t0);
lastkeyRate[filterPos%FILTER_SIZE] = keyRate;
lastGpukeyRate[filterPos%FILTER_SIZE] = gpuKeyRate;
filterPos++;
// KeyRate smoothing
double avgKeyRate = 0.0;
double avgGpuKeyRate = 0.0;
uint32_t nbSample;
for (nbSample = 0; (nbSample < FILTER_SIZE) && (nbSample < filterPos); nbSample++) {
avgKeyRate += lastkeyRate[nbSample];
avgGpuKeyRate += lastGpukeyRate[nbSample];
}
avgKeyRate /= (double)(nbSample);
avgGpuKeyRate /= (double)(nbSample);
if (isAlive(params)) {
printf("\r[%.2f Mkey/s][GPU %.2f Mkey/s][Total 2^%.2f]%s[Found %d] ",
avgKeyRate / 1000000.0, avgGpuKeyRate / 1000000.0,
log2((double)count), GetExpectedTime(avgKeyRate, (double)count).c_str(),nbFoundKey);
}
if (rekey > 0) {
if ((count - lastRekey) > (1000000 * rekey)) {
// Rekey request
rekeyRequest(params);
lastRekey = count;
}
}
lastCount = count;
lastGPUCount = gpuCount;
t0 = t1;
}
free(params);
}
// ----------------------------------------------------------------------------
string VanitySearch::GetHex(vector &buffer) {
string ret;
char tmp[128];
for (int i = 0; i < (int)buffer.size(); i++) {
sprintf(tmp,"%02X",buffer[i]);
ret.append(tmp);
}
return ret;
}