diff --git a/vanitygen.c b/vanitygen.c index db065cc..10dbbe5 100644 --- a/vanitygen.c +++ b/vanitygen.c @@ -26,11 +26,16 @@ #include #include +#include #include #include #include +#include + +const char *version = "0.3"; const int debug = 0; +int verbose = 0; static const char *b58_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -149,23 +154,109 @@ dumpbn(const BIGNUM *bn) } void -output_match(EC_KEY *pkey, int addrtype, int privtype) +output_timing(int cycle, int *total, struct timeval *last, double chance) +{ + struct timeval tvnow, tv; + long long rate; + double count, prob, time, targ; + char linebuf[80]; + char *unit; + int rem, p, i; + + const double targs[] = { 0.5, 0.75, 0.8, 0.9, 0.95, 1.0 }; + + /* Compute the rate */ + gettimeofday(&tvnow, NULL); + timersub(&tvnow, last, &tv); + memcpy(last, &tvnow, sizeof(*last)); + rate = tv.tv_usec + (1000000 * tv.tv_sec); + rate = (1000000ULL * cycle) / rate; + *total += cycle; + + rem = sizeof(linebuf); + p = snprintf(linebuf, rem, "[%lld K/s][total %d]", rate, *total); + assert(p > 0); + rem -= p; + if (rem < 0) + rem = 0; + + if (chance >= 1.0) { + count = *total; + prob = 1.0f - exp(-count/chance); + + p = snprintf(&linebuf[p], rem, "[Prob %.1f%%]", prob * 100); + assert(p > 0); + rem -= p; + if (rem < 0) + rem = 0; + p = sizeof(linebuf) - rem; + + for (i = 0; i < sizeof(targs)/sizeof(targs[0]); i++) { + targ = targs[i]; + if ((targ < 1.0) && (prob <= targ)) + break; + } + + if (targ < 1.0) { + time = ((-chance * log(1.0 - targ)) - count) / rate; + unit = "s"; + if (time > 60) { + time /= 60; + unit = "min"; + if (time > 60) { + time /= 60; + unit = "h"; + if (time > 24) { + time /= 24; + unit = "d"; + if (time > 365) { + time /= 365; + unit = "y"; + } + } + } + } + + p = snprintf(&linebuf[p], rem, "[%d%% in %.1f%s]", + (int) (100 * targ), time, unit); + assert(p > 0); + rem -= p; + if (rem < 0) + rem = 0; + } + } + + if (rem) { + memset(&linebuf[sizeof(linebuf)-rem], 0x20, rem); + linebuf[sizeof(linebuf)-1] = '\0'; + } + printf("\r%s", linebuf); + fflush(stdout); +} + +void +output_match(EC_KEY *pkey, const char *pattern, int addrtype, int privtype) { - unsigned char key_buf[512], *pend; char print_buf[512]; + + unsigned char key_buf[512], *pend; int len; assert(EC_KEY_check_key(pkey)); - /* Hexadecimal OpenSSL notation */ - pend = key_buf; - len = i2o_ECPublicKey(pkey, &pend); - printf("Pubkey (hex): "); - dumphex(key_buf, len); - pend = key_buf; - len = i2d_ECPrivateKey(pkey, &pend); - printf("Privkey (hex): "); - dumphex(key_buf, len); + printf("Pattern: %s\n", pattern); + + if (verbose) { + /* Hexadecimal OpenSSL notation */ + pend = key_buf; + len = i2o_ECPublicKey(pkey, &pend); + printf("Pubkey (hex) : "); + dumphex(key_buf, len); + pend = key_buf; + len = i2d_ECPrivateKey(pkey, &pend); + printf("Privkey (hex) : "); + dumphex(key_buf, len); + } /* Base-58 bitcoin notation public key hash */ encode_address(pkey, addrtype, print_buf); @@ -176,62 +267,34 @@ output_match(EC_KEY *pkey, int addrtype, int privtype) printf("Privkey: %s\n", print_buf); } - /* - * Search for a key for which the encoded address has a specific prefix. - * Uses bignum arithmetic to predetermine value ranges. - * Faster than regular expression searching. + * Find the bignum ranges that produce a given prefix. */ -void -generate_address_prefix(int addrtype, int privtype, const char *pfx) +int +get_prefix_ranges(int addrtype, const char *pfx, BIGNUM **result, + BN_CTX *bnctx) { - unsigned char eckey_buf[128]; - unsigned char hash1[32]; - unsigned char binres[25] = {0,}; - char *dbuf; - - int i, p, c, t; - int b58pow, b58ceil, b58top = 0; - - BN_ULONG npoints, rekey_at; - + int i, p, c; int zero_prefix = 0; int check_upper = 0; + int b58pow, b58ceil, b58top = 0; + int ret = 0; - BN_CTX *bnctx; - BIGNUM *bnap, *bnbp, *bntp; BIGNUM bntarg, bnceil, bnfloor; BIGNUM bnbase; - BIGNUM bnhigh, bnlow, bnhigh2, bnlow2; + BIGNUM *bnap, *bnbp, *bntp; + BIGNUM *bnhigh = NULL, *bnlow = NULL, *bnhigh2 = NULL, *bnlow2 = NULL; BIGNUM bntmp, bntmp2; - EC_KEY *pkey; - const EC_GROUP *pgroup; - const EC_POINT *pgen; - EC_POINT *ppnt = NULL; - - struct timeval tvstart, tvnow, tv; - - bnctx = BN_CTX_new(); - BN_init(&bntarg); BN_init(&bnceil); BN_init(&bnfloor); BN_init(&bnbase); - BN_init(&bnhigh); - BN_init(&bnlow); - BN_init(&bnhigh2); - BN_init(&bnlow2); BN_init(&bntmp); BN_init(&bntmp2); BN_set_word(&bnbase, 58); - /* - * Step 1: compute the integer boundaries for accepted addresses - * Results are stored in bnlow, bnhigh, bnlow2, bnhigh2. - */ - p = strlen(pfx); for (i = 0; i < p; i++) { @@ -240,16 +303,18 @@ generate_address_prefix(int addrtype, int privtype, const char *pfx) break; } if (c >= 58) { - printf("Invalid character '%c' in address\n", pfx[i]); - return; + printf("Invalid character '%c' in prefix '%s'\n", + pfx[i], pfx); + goto out; } if (i == zero_prefix) { if (c == 0) { /* Add another zero prefix */ zero_prefix++; if (zero_prefix > 19) { - printf("Prefix is too long\n"); - return; + printf("Prefix '%s' is too long\n", + pfx); + goto out; } continue; } @@ -272,11 +337,15 @@ generate_address_prefix(int addrtype, int privtype, const char *pfx) BN_sub(&bnceil, &bntmp, &bntmp2); BN_set_bit(&bnfloor, 192 - (zero_prefix * 8)); + bnlow = BN_new(); + bnhigh = BN_new(); + if (b58top) { /* * If a non-zero was given in the prefix, find the * numeric boundaries of the prefix. */ + BN_copy(&bntmp, &bnceil); bnap = &bntmp; bnbp = &bntmp2; @@ -295,55 +364,62 @@ generate_address_prefix(int addrtype, int privtype, const char *pfx) * Do not allow the prefix to constrain the * check value, this is ridiculous. */ - printf("Prefix is too long\n"); - return; + printf("Prefix '%s' is too long\n", pfx); + goto out; } BN_set_word(&bntmp2, b58pow - (p - zero_prefix)); BN_exp(&bntmp, &bnbase, &bntmp2, bnctx); - BN_mul(&bnlow, &bntmp, &bntarg, bnctx); - BN_set_word(&bnhigh, 1); - BN_sub(&bntmp2, &bntmp, &bnhigh); - BN_add(&bnhigh, &bnlow, &bntmp2); + BN_mul(bnlow, &bntmp, &bntarg, bnctx); + BN_set_word(bnhigh, 1); + BN_sub(&bntmp2, &bntmp, bnhigh); + BN_add(bnhigh, bnlow, &bntmp2); if (b58top <= b58ceil) { /* Fill out the upper range too */ check_upper = 1; - BN_mul(&bnlow2, &bnlow, &bnbase, bnctx); - BN_mul(&bntmp2, &bnhigh, &bnbase, bnctx); + bnlow2 = BN_new(); + bnhigh2 = BN_new(); + + BN_mul(bnlow2, bnlow, &bnbase, bnctx); + BN_mul(&bntmp2, bnhigh, &bnbase, bnctx); BN_set_word(&bntmp, 57); - BN_add(&bnhigh2, &bntmp2, &bntmp); + BN_add(bnhigh2, &bntmp2, &bntmp); /* * Addresses above the ceiling will have one * fewer "1" prefix in front than we require. */ - if (BN_cmp(&bnceil, &bnlow2) < 0) + if (BN_cmp(&bnceil, bnlow2) < 0) /* High prefix is above the ceiling */ check_upper = 0; - else if (BN_cmp(&bnceil, &bnhigh2) < 0) + else if (BN_cmp(&bnceil, bnhigh2) < 0) /* High prefix is partly above the ceiling */ - BN_copy(&bnhigh2, &bnceil); + BN_copy(bnhigh2, &bnceil); /* * Addresses below the floor will have another * "1" prefix in front instead of our target. */ - if (BN_cmp(&bnfloor, &bnhigh) >= 0) { + if (BN_cmp(&bnfloor, bnhigh) >= 0) { /* Low prefix is completely below the floor */ check_upper = 0; - BN_copy(&bnhigh, &bnhigh2); - BN_copy(&bnlow, &bnlow2); + BN_free(bnhigh); + bnhigh = bnhigh2; + bnhigh2 = NULL; + BN_free(bnlow); + bnlow = bnlow2; + bnlow2 = NULL; } - else if (BN_cmp(&bnfloor, &bnlow) > 0) { + else if (BN_cmp(&bnfloor, bnlow) > 0) { /* Low prefix is partly below the floor */ - BN_copy(&bnlow, &bnfloor); + BN_copy(bnlow, &bnfloor); } } } else { - BN_copy(&bnhigh, &bnceil); - BN_set_word(&bnlow, 0); + BN_copy(bnhigh, &bnceil); + BN_set_word(bnlow, 0); } /* Limit the prefix to the address type */ @@ -352,77 +428,731 @@ generate_address_prefix(int addrtype, int privtype, const char *pfx) BN_lshift(&bntmp2, &bntmp, 192); if (check_upper) { - if (BN_cmp(&bntmp2, &bnhigh2) > 0) + if (BN_cmp(&bntmp2, bnhigh2) > 0) check_upper = 0; - else if (BN_cmp(&bntmp2, &bnlow2) > 0) - BN_copy(&bnlow2, &bntmp2); + else if (BN_cmp(&bntmp2, bnlow2) > 0) + BN_copy(bnlow2, &bntmp2); } - if (BN_cmp(&bntmp2, &bnhigh) > 0) { + if (BN_cmp(&bntmp2, bnhigh) > 0) { if (!check_upper) { - printf("Address prefix not possible\n"); - return; + printf("Prefix '%s' not possible\n", pfx); + goto out; } check_upper = 0; - BN_copy(&bnhigh, &bnhigh2); - BN_copy(&bnlow, &bnlow2); + BN_free(bnhigh); + bnhigh = bnhigh2; + bnhigh2 = NULL; + BN_free(bnlow); + bnlow = bnlow2; + bnlow2 = NULL; } - else if (BN_cmp(&bntmp2, &bnlow) > 0) { - BN_copy(&bnlow, &bntmp2); + else if (BN_cmp(&bntmp2, bnlow) > 0) { + BN_copy(bnlow, &bntmp2); } BN_set_word(&bntmp, addrtype + 1); BN_lshift(&bntmp2, &bntmp, 192); if (check_upper) { - if (BN_cmp(&bntmp2, &bnlow2) < 0) + if (BN_cmp(&bntmp2, bnlow2) < 0) check_upper = 0; - else if (BN_cmp(&bntmp2, &bnhigh2) < 0) - BN_copy(&bnlow2, &bntmp2); + else if (BN_cmp(&bntmp2, bnhigh2) < 0) + BN_copy(bnlow2, &bntmp2); } - if (BN_cmp(&bntmp2, &bnlow) < 0) { + if (BN_cmp(&bntmp2, bnlow) < 0) { if (!check_upper) { - printf("Address prefix not possible\n"); - return; + printf("Prefix '%s' not possible\n", pfx); + goto out; } check_upper = 0; - BN_copy(&bnhigh, &bnhigh2); - BN_copy(&bnlow, &bnlow2); + BN_free(bnhigh); + bnhigh = bnhigh2; + bnhigh2 = NULL; + BN_free(bnlow); + bnlow = bnlow2; + bnlow2 = NULL; } - else if (BN_cmp(&bntmp2, &bnhigh) < 0) { - BN_copy(&bnhigh, &bntmp2); + else if (BN_cmp(&bntmp2, bnhigh) < 0) { + BN_copy(bnhigh, &bntmp2); } /* Address ranges are complete */ + assert(check_upper || ((bnlow2 == NULL) && (bnhigh2 == NULL))); + result[0] = bnlow; + result[1] = bnhigh; + result[2] = bnlow2; + result[3] = bnhigh2; + bnlow = NULL; + bnhigh = NULL; + bnlow2 = NULL; + bnhigh2 = NULL; + ret = 1; + +out: + BN_clear_free(&bntarg); + BN_clear_free(&bnceil); + BN_clear_free(&bnfloor); + BN_clear_free(&bnbase); + BN_clear_free(&bntmp); + BN_clear_free(&bntmp2); + if (bnhigh) + BN_free(bnhigh); + if (bnlow) + BN_free(bnlow); + if (bnhigh2) + BN_free(bnhigh2); + if (bnlow2) + BN_free(bnlow2); + + return ret; +} + +/* + * AVL tree implementation + */ + +typedef enum { CENT = 1, LEFT = 0, RIGHT = 2 } avl_balance_t; + +typedef struct _avl_item_s { + struct _avl_item_s *ai_left, *ai_right, *ai_up; + avl_balance_t ai_balance; +#ifndef NDEBUG + int ai_indexed; +#endif +} avl_item_t; + +typedef struct _avl_root_s { + avl_item_t *ar_root; +} avl_root_t; + +inline void +avl_root_init(avl_root_t *rootp) +{ + rootp->ar_root = NULL; +} + +inline int +avl_root_empty(avl_root_t *rootp) +{ + return (rootp->ar_root == NULL) ? 1 : 0; +} - if (debug) { - if (check_upper) { - printf("Upper Min: "); - dumpbn(&bnlow2); - printf("Upper Max: "); - dumpbn(&bnhigh2); +inline void +avl_item_init(avl_item_t *itemp) +{ + memset(itemp, 0, sizeof(*itemp)); + itemp->ai_balance = CENT; +} + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define avl_item_entry(ptr, type, member) \ + container_of(ptr, type, member) + + + +inline void +_avl_rotate_ll(avl_root_t *rootp, avl_item_t *itemp) +{ + avl_item_t *tmp; + tmp = itemp->ai_left; + itemp->ai_left = tmp->ai_right; + if (itemp->ai_left) + itemp->ai_left->ai_up = itemp; + tmp->ai_right = itemp; + + if (itemp->ai_up) { + if (itemp->ai_up->ai_left == itemp) { + itemp->ai_up->ai_left = tmp; + } else { + assert(itemp->ai_up->ai_right == itemp); + itemp->ai_up->ai_right = tmp; } - printf("Min: "); - dumpbn(&bnlow); - printf("Max: "); - dumpbn(&bnhigh); + } else { + rootp->ar_root = tmp; } + tmp->ai_up = itemp->ai_up; + itemp->ai_up = tmp; +} - /* Determine the probability of finding a match */ - BN_sub(&bntarg, &bnhigh, &bnlow); - if (check_upper) { - BN_sub(&bntmp, &bnhigh2, &bnlow2); - BN_add(&bntmp2, &bntarg, &bntmp); - BN_copy(&bntarg, &bntmp2); +inline void +_avl_rotate_lr(avl_root_t *rootp, avl_item_t *itemp) +{ + avl_item_t *rcp, *rlcp; + rcp = itemp->ai_left; + rlcp = rcp->ai_right; + if (itemp->ai_up) { + if (itemp == itemp->ai_up->ai_left) { + itemp->ai_up->ai_left = rlcp; + } else { + assert(itemp == itemp->ai_up->ai_right); + itemp->ai_up->ai_right = rlcp; + } + } else { + rootp->ar_root = rlcp; + } + rlcp->ai_up = itemp->ai_up; + rcp->ai_right = rlcp->ai_left; + if (rcp->ai_right) + rcp->ai_right->ai_up = rcp; + itemp->ai_left = rlcp->ai_right; + if (itemp->ai_left) + itemp->ai_left->ai_up = itemp; + rlcp->ai_left = rcp; + rlcp->ai_right = itemp; + rcp->ai_up = rlcp; + itemp->ai_up = rlcp; +} + +inline void +_avl_rotate_rr(avl_root_t *rootp, avl_item_t *itemp) +{ + avl_item_t *tmp; + tmp = itemp->ai_right; + itemp->ai_right = tmp->ai_left; + if (itemp->ai_right) + itemp->ai_right->ai_up = itemp; + tmp->ai_left = itemp; + + if (itemp->ai_up) { + if (itemp->ai_up->ai_right == itemp) { + itemp->ai_up->ai_right = tmp; + } else { + assert(itemp->ai_up->ai_left == itemp); + itemp->ai_up->ai_left = tmp; + } + } else { + rootp->ar_root = tmp; + } + tmp->ai_up = itemp->ai_up; + itemp->ai_up = tmp; +} + +inline void +_avl_rotate_rl(avl_root_t *rootp, avl_item_t *itemp) +{ + avl_item_t *rcp, *rlcp; + rcp = itemp->ai_right; + rlcp = rcp->ai_left; + if (itemp->ai_up) { + if (itemp == itemp->ai_up->ai_right) { + itemp->ai_up->ai_right = rlcp; + } else { + assert(itemp == itemp->ai_up->ai_left); + itemp->ai_up->ai_left = rlcp; + } + } else { + rootp->ar_root = rlcp; + } + rlcp->ai_up = itemp->ai_up; + rcp->ai_left = rlcp->ai_right; + if (rcp->ai_left) + rcp->ai_left->ai_up = rcp; + itemp->ai_right = rlcp->ai_left; + if (itemp->ai_right) + itemp->ai_right->ai_up = itemp; + rlcp->ai_right = rcp; + rlcp->ai_left = itemp; + rcp->ai_up = rlcp; + itemp->ai_up = rlcp; +} + +void +avl_delete_fix(avl_root_t *rootp, avl_item_t *itemp, avl_item_t *parentp) +{ + avl_item_t *childp; + + if ((parentp->ai_left == NULL) && + (parentp->ai_right == NULL)) { + assert(itemp == NULL); + parentp->ai_balance = CENT; + itemp = parentp; + parentp = itemp->ai_up; + } + + while (parentp) { + if (itemp == parentp->ai_right) { + itemp = parentp->ai_left; + if (parentp->ai_balance == LEFT) { + /* Parent was left-heavy, now worse */ + if (itemp->ai_balance == LEFT) { + /* If left child is also + * left-heavy, LL fixes it. */ + _avl_rotate_ll(rootp, parentp); + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + parentp = itemp; + } else if (itemp->ai_balance == CENT) { + _avl_rotate_ll(rootp, parentp); + itemp->ai_balance = RIGHT; + parentp->ai_balance = LEFT; + break; + } else { + childp = itemp->ai_right; + _avl_rotate_lr(rootp, parentp); + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + if (childp->ai_balance == RIGHT) + itemp->ai_balance = LEFT; + if (childp->ai_balance == LEFT) + parentp->ai_balance = RIGHT; + childp->ai_balance = CENT; + parentp = childp; + } + } else if (parentp->ai_balance == CENT) { + parentp->ai_balance = LEFT; + break; + } else { + parentp->ai_balance = CENT; + } + + } else { + itemp = parentp->ai_right; + if (parentp->ai_balance == RIGHT) { + if (itemp->ai_balance == RIGHT) { + _avl_rotate_rr(rootp, parentp); + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + parentp = itemp; + } else if (itemp->ai_balance == CENT) { + _avl_rotate_rr(rootp, parentp); + itemp->ai_balance = LEFT; + parentp->ai_balance = RIGHT; + break; + } else { + childp = itemp->ai_left; + _avl_rotate_rl(rootp, parentp); + + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + if (childp->ai_balance == RIGHT) + parentp->ai_balance = LEFT; + if (childp->ai_balance == LEFT) + itemp->ai_balance = RIGHT; + childp->ai_balance = CENT; + parentp = childp; + } + } else if (parentp->ai_balance == CENT) { + parentp->ai_balance = RIGHT; + break; + } else { + parentp->ai_balance = CENT; + } + } + + itemp = parentp; + parentp = itemp->ai_up; + } +} + +void +avl_insert_fix(avl_root_t *rootp, avl_item_t *itemp) +{ + avl_item_t *childp, *parentp = itemp->ai_up; + itemp->ai_left = itemp->ai_right = NULL; +#ifndef NDEBUG + assert(!itemp->ai_indexed); + itemp->ai_indexed = 1; +#endif + while (parentp) { + if (itemp == parentp->ai_left) { + if (parentp->ai_balance == LEFT) { + /* Parent was left-heavy, now worse */ + if (itemp->ai_balance == LEFT) { + /* If left child is also + * left-heavy, LL fixes it. */ + _avl_rotate_ll(rootp, parentp); + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + break; + } else { + assert(itemp->ai_balance != CENT); + childp = itemp->ai_right; + _avl_rotate_lr(rootp, parentp); + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + if (childp->ai_balance == RIGHT) + itemp->ai_balance = LEFT; + if (childp->ai_balance == LEFT) + parentp->ai_balance = RIGHT; + childp->ai_balance = CENT; + break; + } + } else if (parentp->ai_balance == CENT) { + parentp->ai_balance = LEFT; + } else { + parentp->ai_balance = CENT; + return; + } + } else { + if (parentp->ai_balance == RIGHT) { + if (itemp->ai_balance == RIGHT) { + _avl_rotate_rr(rootp, parentp); + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + break; + } else { + assert(itemp->ai_balance != CENT); + childp = itemp->ai_left; + _avl_rotate_rl(rootp, parentp); + itemp->ai_balance = CENT; + parentp->ai_balance = CENT; + if (childp->ai_balance == RIGHT) + parentp->ai_balance = LEFT; + if (childp->ai_balance == LEFT) + itemp->ai_balance = RIGHT; + childp->ai_balance = CENT; + break; + } + } else if (parentp->ai_balance == CENT) { + parentp->ai_balance = RIGHT; + } else { + parentp->ai_balance = CENT; + break; + } + } + + itemp = parentp; + parentp = itemp->ai_up; + } +} + +inline avl_item_t * +avl_next(avl_item_t *itemp) +{ + if (itemp->ai_right) { + itemp = itemp->ai_right; + while (itemp->ai_left) + itemp = itemp->ai_left; + return itemp; + } + + while (itemp->ai_up && (itemp == itemp->ai_up->ai_right)) + itemp = itemp->ai_up; + + if (!itemp->ai_up) + return NULL; + + return itemp->ai_up; +} + +void +avl_remove(avl_root_t *rootp, avl_item_t *itemp) +{ + avl_item_t *relocp, *replacep, *parentp = NULL; +#ifndef NDEBUG + assert(itemp->ai_indexed); + itemp->ai_indexed = 0; +#endif + /* If the item is directly replaceable, do it. */ + if ((itemp->ai_left == NULL) || (itemp->ai_right == NULL)) { + parentp = itemp->ai_up; + replacep = itemp->ai_left; + if (replacep == NULL) + replacep = itemp->ai_right; + if (replacep != NULL) + replacep->ai_up = parentp; + if (parentp == NULL) { + rootp->ar_root = replacep; + } else { + if (itemp == parentp->ai_left) + parentp->ai_left = replacep; + else + parentp->ai_right = replacep; + + avl_delete_fix(rootp, replacep, parentp); + } + return; + } + + /* + * Otherwise we do an indirect replacement with + * the item's leftmost right descendant. + */ + relocp = avl_next(itemp); + assert(relocp); + assert(relocp->ai_up != NULL); + assert(relocp->ai_left == NULL); + replacep = relocp->ai_right; + relocp->ai_left = itemp->ai_left; + if (relocp->ai_left != NULL) + relocp->ai_left->ai_up = relocp; + if (itemp->ai_up == NULL) + rootp->ar_root = relocp; + else { + if (itemp == itemp->ai_up->ai_left) + itemp->ai_up->ai_left = relocp; + else + itemp->ai_up->ai_right = relocp; + } + if (relocp == relocp->ai_up->ai_left) { + assert(relocp->ai_up != itemp); + relocp->ai_up->ai_left = replacep; + parentp = relocp->ai_up; + if (replacep != NULL) + replacep->ai_up = relocp->ai_up; + relocp->ai_right = itemp->ai_right; + } else { + assert(relocp->ai_up == itemp); + relocp->ai_right = replacep; + parentp = relocp; + } + if (relocp->ai_right != NULL) + relocp->ai_right->ai_up = relocp; + relocp->ai_up = itemp->ai_up; + relocp->ai_balance = itemp->ai_balance; + avl_delete_fix(rootp, replacep, parentp); +} + + + +/* + * Address prefix AVL tree node + */ + +typedef struct _vg_prefix_s { + avl_item_t vp_item; + struct _vg_prefix_s *vp_sibling; + const char *vp_pattern; + BIGNUM *vp_low; + BIGNUM *vp_high; +} vg_prefix_t; + +void +vg_prefix_free(vg_prefix_t *vp) +{ + if (vp->vp_low) + BN_free(vp->vp_low); + if (vp->vp_high) + BN_free(vp->vp_high); + free(vp); +} + +vg_prefix_t * +vg_prefix_avl_search(avl_root_t *rootp, BIGNUM *targ) +{ + vg_prefix_t *vp; + avl_item_t *itemp = rootp->ar_root; + + while (itemp) { + vp = avl_item_entry(itemp, vg_prefix_t, vp_item); + if (BN_cmp(vp->vp_low, targ) > 0) { + itemp = itemp->ai_left; + } else { + if (BN_cmp(vp->vp_high, targ) < 0) { + itemp = itemp->ai_right; + } else + return vp; + } } + return NULL; +} + +vg_prefix_t * +vg_prefix_avl_insert(avl_root_t *rootp, vg_prefix_t *vpnew) +{ + vg_prefix_t *vp; + avl_item_t *itemp = NULL; + avl_item_t **ptrp = &rootp->ar_root; + while (*ptrp) { + itemp = *ptrp; + vp = avl_item_entry(itemp, vg_prefix_t, vp_item); + if (BN_cmp(vp->vp_low, vpnew->vp_high) > 0) { + ptrp = &itemp->ai_left; + } else { + if (BN_cmp(vp->vp_high, vpnew->vp_low) < 0) { + ptrp = &itemp->ai_right; + } else + return vp; + } + } + vpnew->vp_item.ai_up = itemp; + itemp = &vpnew->vp_item; + *ptrp = itemp; + avl_insert_fix(rootp, itemp); + return NULL; +} + +vg_prefix_t * +vg_prefix_add(avl_root_t *rootp, const char *pattern, BIGNUM *low, BIGNUM *high) +{ + vg_prefix_t *vp; + vp = (vg_prefix_t *) malloc(sizeof(*vp)); + if (vp) { + avl_item_init(&vp->vp_item); + vp->vp_sibling = NULL; + vp->vp_pattern = pattern; + vp->vp_low = low; + vp->vp_high = high; + if (vg_prefix_avl_insert(rootp, vp) != NULL) { + vg_prefix_free(vp); + vp = NULL; + } + } + return vp; +} + +void +vg_prefix_delete(avl_root_t *rootp, vg_prefix_t *vp) +{ + avl_remove(rootp, &vp->vp_item); + if (vp->vp_sibling) { + avl_remove(rootp, &vp->vp_sibling->vp_item); + vg_prefix_free(vp->vp_sibling); + } + vg_prefix_free(vp); +} + + +/* + * Search for a key for which the encoded address has a specific prefix. + * Uses bignum arithmetic to predetermine value ranges. + * Faster than regular expression searching. + */ +void +generate_address_prefix(int addrtype, int privtype, + char ** const patterns, int npatterns) +{ + unsigned char eckey_buf[128]; + unsigned char hash1[32]; + unsigned char binres[25] = {0,}; + char *dbuf, *mostdifficult = NULL; + + int i, c, t, nranges, npfx; + + BN_ULONG npoints, rekey_at; + + BN_CTX *bnctx; + BIGNUM bntarg; + BIGNUM bnbase; + BIGNUM bndifficulty; + BIGNUM bnmostdifficult; + BIGNUM *ranges[4]; + BIGNUM bntmp, bntmp2; + + EC_KEY *pkey = NULL; + const EC_GROUP *pgroup; + const EC_POINT *pgen; + EC_POINT *ppnt = NULL; + + struct timeval tvstart; + + avl_root_t avlroot; + vg_prefix_t *vp, *vp2; + + double chance; + + avl_root_init(&avlroot); + + bnctx = BN_CTX_new(); + + BN_init(&bntarg); + BN_init(&bnbase); + BN_init(&bndifficulty); + BN_init(&bnmostdifficult); + BN_init(&bntmp); + BN_init(&bntmp2); + + BN_set_word(&bnbase, 58); + + /* + * Step 1: compute the integer boundaries for accepted addresses + */ + + nranges = 0; + npfx = 0; + for (c = 0; c < npatterns; c++) { + if (!get_prefix_ranges(addrtype, patterns[c], ranges, bnctx)) + continue; + + if (debug) { + if (ranges[2]) { + printf("Upper Min: "); + dumpbn(ranges[2]); + printf("Upper Max: "); + dumpbn(ranges[3]); + } + printf("Min: "); + dumpbn(ranges[0]); + printf("Max: "); + dumpbn(ranges[1]); + } + + vp = vg_prefix_add(&avlroot, patterns[c], + ranges[0], ranges[1]); + if (vp && ranges[2]) { + vp2 = vg_prefix_add(&avlroot, patterns[c], + ranges[2], ranges[3]); + if (vp2) { + nranges++; + vp->vp_sibling = vp2; + vp2->vp_sibling = vp; + } + } + + if (!vp) { + printf("Could not add prefix '%s': overlapping?\n", + patterns[c]); + continue; + } + nranges++; + npfx++; + + /* Determine the probability of finding a match */ + BN_sub(&bntarg, ranges[1], ranges[0]); + if (ranges[2]) { + BN_sub(&bntmp, ranges[3], ranges[2]); + BN_add(&bntmp2, &bntarg, &bntmp); + BN_copy(&bntarg, &bntmp2); + } + + if (BN_is_zero(&bnmostdifficult) || + (BN_cmp(&bnmostdifficult, &bntarg) > 0)) { + BN_copy(&bnmostdifficult, &bntarg); + mostdifficult = patterns[c]; + } + + BN_add(&bntmp, &bndifficulty, &bntarg); + BN_copy(&bndifficulty, &bntmp); + + if (verbose) { + BN_set_word(&bntmp, 0); + BN_set_bit(&bntmp, 192); + BN_div(&bntmp2, NULL, &bntmp, &bntarg, bnctx); + + dbuf = BN_bn2dec(&bntmp2); + printf("Prefix difficulty: %20s %s\n", + dbuf, patterns[c]); + OPENSSL_free(dbuf); + } + } + + if (!nranges) { + printf("No prefixes to search\n"); + goto out; + } + BN_set_word(&bntmp, 0); BN_set_bit(&bntmp, 192); - BN_div(&bntmp2, NULL, &bntmp, &bntarg, bnctx); + BN_div(&bntmp2, NULL, &bntmp, &bndifficulty, bnctx); + dbuf = BN_bn2dec(&bntmp2); - printf("Difficulty: %s\n", dbuf); + if (npfx > 1) + printf("Next match difficulty: %s (%d prefixes)\n", dbuf, npfx); + else + printf("Difficulty: %s\n", dbuf); + chance = atof(dbuf); OPENSSL_free(dbuf); + if (avl_root_empty(&avlroot)) { + printf("No prefix patterns to search\n"); + return; + } + /* * Step 2: Search for matching private keys * Generate a base private key, and start searching increments. @@ -480,12 +1210,9 @@ generate_address_prefix(int addrtype, int privtype, const char *pfx) BN_bin2bn(binres, sizeof(binres), &bntarg); - if ((check_upper && - (BN_cmp(&bnlow2, &bntarg) <= 0) && - (BN_cmp(&bnhigh2, &bntarg) > 0)) || - ((BN_cmp(&bnlow, &bntarg) <= 0) && - (BN_cmp(&bnhigh, &bntarg) > 0))) { + vp = vg_prefix_avl_search(&avlroot, &bntarg); + if (vp) { printf("\n"); if (npoints) { @@ -495,36 +1222,54 @@ generate_address_prefix(int addrtype, int privtype, const char *pfx) EC_KEY_get0_private_key(pkey), &bntmp); EC_KEY_set_private_key(pkey, &bntmp2); + EC_KEY_set_public_key(pkey, ppnt); + + /* Rekey immediately */ + rekey_at = 0; + npoints = 0; } - EC_KEY_set_public_key(pkey, ppnt); + output_match(pkey, vp->vp_pattern, addrtype, privtype); + + /* Subtract the range from the aggregate difficulty */ + BN_sub(&bntmp, vp->vp_high, vp->vp_low); + BN_sub(&bntmp2, &bndifficulty, &bntmp); + BN_copy(&bndifficulty, &bntmp2); + if (vp->vp_sibling) { + BN_sub(&bntmp, + vp->vp_sibling->vp_high, + vp->vp_sibling->vp_low); + BN_sub(&bntmp2, &bndifficulty, &bntmp); + BN_copy(&bndifficulty, &bntmp2); + } - output_match(pkey, addrtype, privtype); - break; + vg_prefix_delete(&avlroot, vp); + npfx--; + if (avl_root_empty(&avlroot)) + break; + + BN_set_word(&bntmp, 0); + BN_set_bit(&bntmp, 192); + BN_div(&bntmp2, NULL, &bntmp, &bndifficulty, bnctx); + + dbuf = BN_bn2dec(&bntmp2); + printf("Next match difficulty: %s (%d prefixes)\n", + dbuf, npfx); + chance = atof(dbuf); + OPENSSL_free(dbuf); } - if (++c >= 20000) { - long long rate; - gettimeofday(&tvnow, NULL); - timersub(&tvnow, &tvstart, &tv); - memcpy(&tvstart, &tvnow, sizeof(tvstart)); - rate = tv.tv_usec + (1000000 * tv.tv_sec); - rate = (1000000ULL * c) / rate; - t += c; + if (++c >= 20000) { + output_timing(c, &t, &tvstart, chance); c = 0; - printf("\r%lld K/s, total %d ", rate, t); - fflush(stdout); } } +out: BN_clear_free(&bntarg); - BN_clear_free(&bnceil); - BN_clear_free(&bnfloor); BN_clear_free(&bnbase); - BN_clear_free(&bnhigh); - BN_clear_free(&bnlow); - BN_clear_free(&bnhigh2); - BN_clear_free(&bnlow2); + BN_clear_free(&bndifficulty); + BN_clear_free(&bnmostdifficult); BN_clear_free(&bntmp); BN_clear_free(&bntmp2); BN_CTX_free(bnctx); @@ -539,14 +1284,15 @@ generate_address_prefix(int addrtype, int privtype, const char *pfx) * Equivalent behavior to the bitcoin vanity address patch. */ void -generate_address_regex(int addrtype, int privtype, const char *re) +generate_address_regex(int addrtype, int privtype, + char ** const patterns, int npatterns) { unsigned char eckey_buf[128]; unsigned char hash1[32], hash2[32]; unsigned char binres[25] = {0,}; char b58[40]; - int t, c, zpfx, p, d, re_vec[9]; + int i, t, c, zpfx, p, d, nres, re_vec[9]; BN_ULONG npoints, rekey_at; @@ -559,33 +1305,54 @@ generate_address_regex(int addrtype, int privtype, const char *re) const EC_POINT *pgen; EC_POINT *ppnt = NULL; - pcre *regex; - pcre_extra *regex_extra; + pcre **regex; + pcre_extra **regex_extra; + const char ** regex_pat; const char *pcre_errptr; int pcre_erroffset; - struct timeval tvstart, tvnow, tv; - - regex = pcre_compile(re, 0, &pcre_errptr, &pcre_erroffset, NULL); - if (!regex) { - const char *spaces = " "; - printf("%s\n", re); - while (pcre_erroffset > 16) { - printf("%s", spaces); - pcre_erroffset -= 16; + struct timeval tvstart; + + regex = (pcre**) malloc(npatterns * sizeof(pcre*)); + regex_extra = (pcre_extra**) malloc(npatterns * sizeof(pcre_extra*)); + regex_pat = (const char **) malloc(npatterns * sizeof(char*)); + + for (i = 0, nres = 0; i < npatterns; i++) { + regex[nres] = pcre_compile(patterns[i], 0, + &pcre_errptr, &pcre_erroffset, NULL); + if (!regex[nres]) { + const char *spaces = " "; + printf("%s\n", patterns[i]); + while (pcre_erroffset > 16) { + printf("%s", spaces); + pcre_erroffset -= 16; + } + if (pcre_erroffset > 0) + printf("%s", &spaces[16 - pcre_erroffset]); + printf("^\nRegex error: %s\n", pcre_errptr); + continue; } - if (pcre_erroffset > 0) - printf("%s", &spaces[16 - pcre_erroffset]); - printf("^\nRegex error: %s\n", pcre_errptr); - return; + regex_extra[nres] = pcre_study(regex[nres], 0, &pcre_errptr); + if (pcre_errptr) { + printf("Regex error: %s\n", pcre_errptr); + pcre_free(regex[nres]); + continue; + } + regex_pat[nres] = patterns[i]; + nres += 1; } - regex_extra = pcre_study(regex, 0, &pcre_errptr); - if (!regex_extra) { - printf("Regex error: %s\n", pcre_errptr); - pcre_free(regex); + + if (!nres) { + printf("No suitable regular expressions\n"); + free(regex); + free(regex_extra); + free(regex_pat); return; } + if (nres > 1) + printf("Regular expressions: %d\n", nres); + bnctx = BN_CTX_new(); BN_init(&bna); @@ -646,7 +1413,7 @@ generate_address_regex(int addrtype, int privtype, const char *re) /* Hash the hash and write the four byte check code */ SHA256(binres, 21, hash1); SHA256(hash1, sizeof(hash1), hash2); - memcpy(hash2, &binres[21], 4); + memcpy(&binres[21], hash2, 4); bn = &bna; bndiv = &bnb; @@ -669,50 +1436,62 @@ generate_address_regex(int addrtype, int privtype, const char *re) b58[--p] = b58_alphabet[0]; } - /* Run the regular expression on it */ - d = pcre_exec(regex, regex_extra, - &b58[p], sizeof(b58) - (p+1), 0, - 0, - re_vec, sizeof(re_vec)/sizeof(re_vec[0])); - - if (d > 0) { - printf("\n"); + /* + * Run the regular expressions on it + * SLOW, runs in linear time with the number of REs + */ + for (i = 0; i < nres; i++) { + d = pcre_exec(regex[i], regex_extra[i], + &b58[p], sizeof(b58) - (p+1), 0, + 0, + re_vec, sizeof(re_vec)/sizeof(re_vec[0])); + + if (d > 0) { + printf("\n"); + + if (npoints) { + BN_clear(&bntmp); + BN_set_word(&bntmp, npoints); + BN_add(&bntmp2, + EC_KEY_get0_private_key(pkey), + &bntmp); + EC_KEY_set_private_key(pkey, &bntmp2); + EC_KEY_set_public_key(pkey, ppnt); + + /* Rekey immediately */ + rekey_at = 0; + npoints = 0; + } - if (npoints) { - BN_clear(&bntmp); - BN_set_word(&bntmp, npoints); - BN_add(&bntmp2, - EC_KEY_get0_private_key(pkey), - &bntmp); - EC_KEY_set_private_key(pkey, &bntmp2); - } + output_match(pkey, regex_pat[i], + addrtype, privtype); - EC_KEY_set_public_key(pkey, ppnt); + pcre_free(regex[i]); + if (regex_extra[i]) + pcre_free(regex_extra[i]); + nres -= 1; + if (!nres) + goto out; + regex[i] = regex[nres]; + regex_extra[i] = regex_extra[nres]; + regex_pat[i] = regex_pat[nres]; - output_match(pkey, addrtype, privtype); - break; - } + printf("Regular expressions: %d\n", nres); + } - if (d != PCRE_ERROR_NOMATCH) { - printf("PCRE error: %d\n", d); - break; + else if (d != PCRE_ERROR_NOMATCH) { + printf("PCRE error: %d\n", d); + goto out; + } } if (++c >= 10000) { - long long rate; - gettimeofday(&tvnow, NULL); - timersub(&tvnow, &tvstart, &tv); - memcpy(&tvstart, &tvnow, sizeof(tvstart)); - rate = tv.tv_usec + (1000000 * tv.tv_sec); - rate = (1000000ULL * c) / rate; - t += c; - + output_timing(c, &t, &tvstart, 0.0); c = 0; - printf("\r%lld K/s, total %d ", rate, t); - fflush(stdout); } } +out: BN_clear_free(&bna); BN_clear_free(&bnb); BN_clear_free(&bnbase); @@ -722,15 +1501,93 @@ generate_address_regex(int addrtype, int privtype, const char *re) BN_CTX_free(bnctx); EC_KEY_free(pkey); EC_POINT_free(ppnt); - pcre_free(regex_extra); - pcre_free(regex); + + for (i = 0; i < nres; i++) { + if (regex_extra[i]) + pcre_free(regex_extra[i]); + pcre_free(regex[i]); + } + free(regex); + free(regex_extra); + free(regex_pat); +} + + +int +read_file(FILE *fp, char ***result, int *rescount) +{ + int ret = 1; + + char **patterns; + char *buf = NULL, *obuf, *pat; + const int blksize = 16*1024; + int nalloc = 16; + int npatterns = 0; + int count, pos; + + patterns = (char**) malloc(sizeof(char*) * nalloc); + count = 0; + pos = 0; + + while (1) { + obuf = buf; + buf = (char *) malloc(blksize); + if (!buf) { + ret = 0; + break; + } + if (pos < count) { + memcpy(buf, &obuf[pos], count - pos); + } + pos = count - pos; + count = fread(&buf[pos], 1, blksize - pos, fp); + if (count < 0) { + printf("Error reading file: %s\n", strerror(errno)); + ret = 0; + } + if (count <= 0) + break; + count += pos; + pat = buf; + + while (pos < count) { + if ((buf[pos] == '\r') || (buf[pos] == '\n')) { + buf[pos] = '\0'; + if (pat) { + if (npatterns == nalloc) { + nalloc *= 2; + patterns = (char**) + realloc(patterns, + sizeof(char*) * + nalloc); + } + patterns[npatterns] = pat; + npatterns++; + pat = NULL; + } + } + else if (!pat) { + pat = &buf[pos]; + } + pos++; + } + + pos = pat ? (pat - buf) : count; + } + + *result = patterns; + *rescount = npatterns; + + return ret; } + void usage(const char *name) { printf( -"Usage: %s [-rNT] \n" +"Vanitygen %s\n" +"Usage: %s [-vrNT] [-f |-] [...]\n" "Generates a bitcoin receiving address matching , and outputs the\n" "address and associated private key. The private key may be stored in a safe\n" "location or imported into a bitcoin client to spend any balance received on\n" @@ -738,10 +1595,14 @@ usage(const char *name) "By default, is interpreted as an exact prefix.\n" "\n" "Options:\n" +"-v Verbose output\n" "-r Use regular expression match instead of prefix\n" " (Feasibility of expression is not checked)\n" "-N Generate namecoin address\n" -"-T Generate bitcoin testnet address\n", name); +"-T Generate bitcoin testnet address\n" +"-f File containing list of patterns, one per line\n" +" (Use \"-\" as the file name for stdin)\n", +version, name); } int @@ -751,10 +1612,15 @@ main(int argc, char **argv) int privtype = 128; int regex = 0; int opt; - const char *pattern = argv[1]; + FILE *fp = NULL; + char **patterns; + int npatterns = 0; - while ((opt = getopt(argc, argv, "rNTh?")) != -1) { + while ((opt = getopt(argc, argv, "vrNTh?f:")) != -1) { switch (opt) { + case 'v': + verbose = 1; + break; case 'r': regex = 1; break; @@ -765,23 +1631,49 @@ main(int argc, char **argv) addrtype = 111; privtype = 239; break; + case 'f': + if (fp) { + printf("Multiple files specified\n"); + return 1; + } + if (!strcmp(optarg, "-")) { + fp = stdin; + } else { + fp = fopen(optarg, "r+"); + if (!fp) { + printf("Could not open %s: %s\n", + optarg, strerror(errno)); + return 1; + } + } + break; default: usage(argv[0]); return 1; } } - if (optind >= argc) { - usage(argv[0]); - return 1; - } - - pattern = argv[optind]; + if (fp) { + if (!read_file(fp, &patterns, &npatterns)) { + printf("Failed to load pattern file\n"); + return 1; + } + } else { + if (optind >= argc) { + usage(argv[0]); + return 1; + } + patterns = &argv[optind]; + npatterns = argc - optind; + } + if (regex) - generate_address_regex(addrtype, privtype, pattern); + generate_address_regex(addrtype, privtype, + patterns, npatterns); else - generate_address_prefix(addrtype, privtype, pattern); + generate_address_prefix(addrtype, privtype, + patterns, npatterns); return 0; }