diff --git a/pattern.c b/pattern.c index 79e2091..30b489e 100644 --- a/pattern.c +++ b/pattern.c @@ -505,10 +505,11 @@ void vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern) { unsigned char key_buf[512], *pend; + unsigned char script_buf[2048]; char addr_buf[64], addr2_buf[64]; char privkey_buf[VG_PROTKEY_MAX_B58]; const char *keytype = "Privkey"; - int len; + int len, script_len; int isscript = (vcp->vc_format == VCF_SCRIPT); EC_POINT *ppnt; @@ -531,10 +532,20 @@ vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern) vg_encode_address(ppnt, EC_KEY_get0_group(pkey), vcp->vc_pubkeytype, addr_buf); - if (isscript) - vg_encode_script_address(ppnt, - EC_KEY_get0_group(pkey), + if (isscript) { + EC_POINT_point2oct(EC_KEY_get0_group(pkey), ppnt, + POINT_CONVERSION_UNCOMPRESSED, + vcp->vc_multisig, + 65, + NULL); + script_len = vg_encode_multisig_script( + vcp->vc_m_multisig, + vcp->vc_n_multisig, + vcp->vc_multisig, + script_buf); + vg_encode_script_address(script_buf, script_len, vcp->vc_addrtype, addr2_buf); + } if (vcp->vc_key_protect_pass) { len = vg_protect_encode_privkey(privkey_buf, @@ -574,8 +585,11 @@ vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern) } if (!vcp->vc_result_file || (vcp->vc_verbose > 0)) { - if (isscript) + if (isscript) { + printf("Script: "); + dumphex(script_buf, script_len); printf("P2SHAddress: %s\n", addr2_buf); + } printf("Address: %s\n" "%s: %s\n", addr_buf, keytype, privkey_buf); @@ -591,8 +605,11 @@ vg_output_match_console(vg_context_t *vcp, EC_KEY *pkey, const char *pattern) fprintf(fp, "Pattern: %s\n" , pattern); - if (isscript) + if (isscript) { + fprintf(fp, "Script: "); + fdumphex(fp, script_buf, script_len); fprintf(fp, "P2SHAddress: %s\n", addr2_buf); + } fprintf(fp, "Address: %s\n" "%s: %s\n", @@ -1397,6 +1414,10 @@ vg_prefix_context_add_patterns(vg_context_t *vcp, ats = "testnet"; bw = "\"m\" or \"n\""; break; + case 196: + ats = "testnet script"; + bw = "\"2M\" or \"2N\""; + break; case 52: ats = "namecoin"; bw = "\"M\" or \"N\""; diff --git a/pattern.h b/pattern.h index b440751..0fb10b9 100644 --- a/pattern.h +++ b/pattern.h @@ -103,6 +103,9 @@ struct _vg_context_s { enum vg_format vc_format; int vc_pubkeytype; EC_POINT *vc_pubkey_base; + int vc_m_multisig; + int vc_n_multisig; + unsigned char *vc_multisig; int vc_halt; vg_exec_context_t *vc_threads; diff --git a/util.c b/util.c index 165f82c..0873449 100644 --- a/util.c +++ b/util.c @@ -66,7 +66,7 @@ fdumphex(FILE *fp, const unsigned char *src, size_t len) for (i = 0; i < len; i++) { fprintf(fp, "%02x", src[i]); } - printf("\n"); + fprintf(fp, "\n"); } void @@ -239,49 +239,62 @@ void vg_encode_address(const EC_POINT *ppoint, const EC_GROUP *pgroup, int addrtype, char *result) { - unsigned char eckey_buf[128], *pend; + unsigned char eckey_buf[128]; unsigned char binres[21] = {0,}; unsigned char hash1[32]; - pend = eckey_buf; - EC_POINT_point2oct(pgroup, ppoint, POINT_CONVERSION_UNCOMPRESSED, eckey_buf, sizeof(eckey_buf), NULL); - pend = eckey_buf + 0x41; + binres[0] = addrtype; - SHA256(eckey_buf, pend - eckey_buf, hash1); + SHA256(eckey_buf, 65, hash1); RIPEMD160(hash1, sizeof(hash1), &binres[1]); vg_b58_encode_check(binres, sizeof(binres), result); } +static int +vg_compare_key(const void *key1, const void *key2) { + return memcmp(key1, key2, 66); +} + +int +vg_encode_multisig_script(const int m, const int n, + const unsigned char *multisig, + unsigned char *script_buf) +{ + int i=0; + + script_buf[i++] = 0x50+m; // OP_ + int k; for (k=0; k 1) { + qsort(script_buf+1, n, 66, vg_compare_key); + } + script_buf[i++] = 0x50+n; // OP_ + script_buf[i++] = 0xae; // OP_CHECKMULTISIG + + return i; +} + void -vg_encode_script_address(const EC_POINT *ppoint, const EC_GROUP *pgroup, +vg_encode_script_address(const unsigned char *script_buf, + const int script_len, int addrtype, char *result) { - unsigned char script_buf[69]; - unsigned char *eckey_buf = script_buf + 2; unsigned char binres[21] = {0,}; unsigned char hash1[32]; - script_buf[ 0] = 0x51; // OP_1 - script_buf[ 1] = 0x41; // pubkey length - // gap for pubkey - script_buf[67] = 0x51; // OP_1 - script_buf[68] = 0xae; // OP_CHECKMULTISIG - - EC_POINT_point2oct(pgroup, - ppoint, - POINT_CONVERSION_UNCOMPRESSED, - eckey_buf, - 65, - NULL); binres[0] = addrtype; - SHA256(script_buf, 69, hash1); + SHA256(script_buf, script_len, hash1); RIPEMD160(hash1, sizeof(hash1), &binres[1]); vg_b58_encode_check(binres, sizeof(binres), result); diff --git a/util.h b/util.h index 5969b87..76c8e2f 100644 --- a/util.h +++ b/util.h @@ -38,8 +38,11 @@ extern int vg_b58_decode_check(const char *input, void *buf, size_t len); extern void vg_encode_address(const EC_POINT *ppoint, const EC_GROUP *pgroup, int addrtype, char *result); -extern void vg_encode_script_address(const EC_POINT *ppoint, - const EC_GROUP *pgroup, +extern int vg_encode_multisig_script(const int m, const int n, + const unsigned char *multisig, + unsigned char *script_buf); +extern void vg_encode_script_address(const unsigned char *script_buf, + const int script_len, int addrtype, char *result); extern void vg_encode_privkey(const EC_KEY *pkey, int addrtype, char *result); extern int vg_set_privkey(const BIGNUM *bnpriv, EC_KEY *pkey); diff --git a/vanitygen.c b/vanitygen.c index 9d88121..20ae908 100644 --- a/vanitygen.c +++ b/vanitygen.c @@ -42,12 +42,10 @@ const char *version = VANITYGEN_VERSION; void * vg_thread_loop(void *arg) { - unsigned char hash_buf[128]; - unsigned char *eckey_buf; + unsigned char hash_buf[2048]; unsigned char hash1[32]; - int i, c, len, output_interval; - int hash_len; + int i, c, hash_len, output_interval; const BN_ULONG rekey_max = 10000000; BN_ULONG npoints, rekey_at, nbatch; @@ -103,20 +101,6 @@ vg_thread_loop(void *arg) output_interval = 1000; gettimeofday(&tvstart, NULL); - if (vcp->vc_format == VCF_SCRIPT) { - hash_buf[ 0] = 0x51; // OP_1 - hash_buf[ 1] = 0x41; // pubkey length - // gap for pubkey - hash_buf[67] = 0x51; // OP_1 - hash_buf[68] = 0xae; // OP_CHECKMULTISIG - eckey_buf = hash_buf + 2; - hash_len = 69; - - } else { - eckey_buf = hash_buf; - hash_len = 65; - } - while (!vcp->vc_halt) { if (++npoints >= rekey_at) { vg_exec_context_upgrade_lock(vxcp); @@ -192,14 +176,27 @@ vg_thread_loop(void *arg) EC_POINTs_make_affine(pgroup, nbatch, ppnt, vxcp->vxc_bnctx); for (i = 0; i < nbatch; i++, vxcp->vxc_delta++) { - /* Hash the public key */ - len = EC_POINT_point2oct(pgroup, ppnt[i], - POINT_CONVERSION_UNCOMPRESSED, - eckey_buf, - 65, - vxcp->vxc_bnctx); - assert(len == 65); + if (vcp->vc_format == VCF_SCRIPT) { + EC_POINT_point2oct(pgroup, ppnt[i], + POINT_CONVERSION_UNCOMPRESSED, + vcp->vc_multisig, + 65, + vxcp->vxc_bnctx); + hash_len = vg_encode_multisig_script( + vcp->vc_m_multisig, + vcp->vc_n_multisig, + vcp->vc_multisig, + hash_buf); + } else { + hash_len = EC_POINT_point2oct(pgroup, ppnt[i], + POINT_CONVERSION_UNCOMPRESSED, + hash_buf, + 65, + vxcp->vxc_bnctx); + assert(hash_len == 65); + } + /* Hash the public key or script */ SHA256(hash_buf, hash_len, hash1); RIPEMD160(hash1, sizeof(hash1), &vxcp->vxc_binres[1]); @@ -314,7 +311,8 @@ usage(const char *name) "-N Generate namecoin address\n" "-T Generate bitcoin testnet address\n" "-X Generate address with the given version\n" -"-F Generate address with the given format (pubkey or script)\n" +"-m Generate multisig address with of n keys\n" +"-M Specify the other n-1 public keys for multisig address\n" "-P Specify base public key for piecewise key generation\n" "-e Encrypt private keys, prompt for password\n" "-E Encrypt private keys with (UNSAFE)\n" @@ -327,6 +325,7 @@ version, name); } #define MAX_FILE 4 +#define MAX_MULTISIG 20 int main(int argc, char **argv) @@ -353,6 +352,9 @@ main(int argc, char **argv) int nthreads = 0; vg_context_t *vcp = NULL; EC_POINT *pubkey_base = NULL; + int m_multisig = 1; + int n_multisig = 1; + unsigned char multisig[65*MAX_MULTISIG]; FILE *pattfp[MAX_FILE], *fp; int pattfpi[MAX_FILE]; @@ -361,7 +363,7 @@ main(int argc, char **argv) int i; - while ((opt = getopt(argc, argv, "vqnrik1eE:P:NTX:F:t:h?f:o:s:")) != -1) { + while ((opt = getopt(argc, argv, "vqnrik1eE:P:m:M:NTX:t:h?f:o:s:")) != -1) { switch (opt) { case 'v': verbose = 2; @@ -399,16 +401,6 @@ main(int argc, char **argv) privtype = 128 + addrtype; scriptaddrtype = addrtype; break; - case 'F': - if (!strcmp(optarg, "script")) - format = VCF_SCRIPT; - else - if (strcmp(optarg, "pubkey")) { - fprintf(stderr, - "Invalid format '%s'\n", optarg); - return 1; - } - break; case 'P': { if (pubkey_base != NULL) { fprintf(stderr, @@ -427,6 +419,39 @@ main(int argc, char **argv) } break; } + case 'm': + m_multisig = atoi(optarg); + if (m_multisig == 0) { + fprintf(stderr, + "Invalid multisig m '%s'\n", optarg); + return 1; + } + format = VCF_SCRIPT; + break; + case 'M': { + if (n_multisig >= MAX_MULTISIG) { + fprintf(stderr, + "Maximum number of multisig pubkeys exceeded\n"); + return 1; + } + EC_KEY *pkey = vg_exec_context_new_key(); + EC_POINT *pubkey = EC_POINT_hex2point( + EC_KEY_get0_group(pkey), + optarg, NULL, NULL); + EC_POINT_point2oct(EC_KEY_get0_group(pkey), pubkey, + POINT_CONVERSION_UNCOMPRESSED, + multisig + 65*n_multisig++, + 65, + NULL); + EC_KEY_free(pkey); + if (pubkey == NULL) { + fprintf(stderr, + "Invalid multisig pubkey\n"); + return 1; + } + format = VCF_SCRIPT; + break; + } case 'e': prompt_password = 1; @@ -514,6 +539,12 @@ main(int argc, char **argv) return 1; } addrtype = scriptaddrtype; + + if (n_multisig < m_multisig) { + fprintf(stderr, + "Not enough multisig pubkeys specified\n"); + return 1; + } } if (seedfile) { @@ -551,6 +582,9 @@ main(int argc, char **argv) vcp->vc_format = format; vcp->vc_pubkeytype = pubkeytype; vcp->vc_pubkey_base = pubkey_base; + vcp->vc_m_multisig = m_multisig; + vcp->vc_n_multisig = n_multisig; + vcp->vc_multisig = multisig; vcp->vc_output_match = vg_output_match_console; vcp->vc_output_timing = vg_output_timing_console;