Skip to content

Commit

Permalink
Support for m-of-n multisig addresses with -m and -M parameter. Compa…
Browse files Browse the repository at this point in the history
…tible with Armory Lockboxes.
  • Loading branch information
bendelo committed Oct 25, 2017
1 parent cd1a728 commit 5b29a8b
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 67 deletions.
33 changes: 27 additions & 6 deletions pattern.c
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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",
Expand Down Expand Up @@ -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\"";
Expand Down
3 changes: 3 additions & 0 deletions pattern.h
Expand Up @@ -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;
Expand Down
57 changes: 35 additions & 22 deletions util.c
Expand Up @@ -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
Expand Down Expand Up @@ -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_<m>
int k; for (k=0; k<n; k++) {
script_buf[i++] = 0x41; // pubkey length
memcpy(script_buf+i, multisig + 65*k, 65);
i+=65;
}
/* Sort public keys to match Armory Lockboxes */
if (n > 1) {
qsort(script_buf+1, n, 66, vg_compare_key);
}
script_buf[i++] = 0x50+n; // OP_<n>
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);
Expand Down
7 changes: 5 additions & 2 deletions util.h
Expand Up @@ -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);
Expand Down
108 changes: 71 additions & 37 deletions vanitygen.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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]);

Expand Down Expand Up @@ -314,7 +311,8 @@ usage(const char *name)
"-N Generate namecoin address\n"
"-T Generate bitcoin testnet address\n"
"-X <version> Generate address with the given version\n"
"-F <format> Generate address with the given format (pubkey or script)\n"
"-m <m> Generate multisig address with <m> of n keys\n"
"-M <pubkey> Specify the other n-1 public keys for multisig address\n"
"-P <pubkey> Specify base public key for piecewise key generation\n"
"-e Encrypt private keys, prompt for password\n"
"-E <password> Encrypt private keys with <password> (UNSAFE)\n"
Expand All @@ -327,6 +325,7 @@ version, name);
}

#define MAX_FILE 4
#define MAX_MULTISIG 20

int
main(int argc, char **argv)
Expand All @@ -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];
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 5b29a8b

Please sign in to comment.