I want to generate a BIP32 version number for Namecoin and other altcoins

4

1

I want to make a BIP32 wallet for Namecoin and other alt coins. I just need to figure out how to generate a a public and private version number so that the BIP32 extended key prefix is something that makes sense like "nprv" for a Namecoin private key and "npub" for a public key.

Been struggling with this one

BigJayMan

Posted 2014-07-09T00:26:54.887

Reputation: 41

Answers

4

I can not tell you really how you generate them, but to partially answer your question, here is a list of known Bitcoin and Altcoin prefixes, including Litecoin and Darkcoin:

EXT_SECRET_KEY, EXT_PUBLIC_KEY # Network                 : Prefixes
----------------------------------------------------------------------
0x0488ADE4,     0x0488B21E     # BTC  Bitcoin    mainnet : xprv / xpub
0x04358394,     0x043587CF     # BTC  Bitcoin    testnet : tprv / tpub
0x019D9CFE,     0x019DA462     # LTC  Litecoin   mainnet : Ltpv / Ltub
0x0436EF7D,     0x0436F6E1     # LTC  Litecoin   testnet : ttpv / ttub
0x02FE52F8,     0x02FE52CC     # DRK  Darkcoin   mainnet : drkv / drkp
0x3A8061A0,     0x3A805837     # DRK  Darkcoin   testnet : DRKV / DRKP
0x0488ADE4,     0x0488B21E     # VIA  Viacoin    mainnet : xprv / xpub
0x04358394,     0x043587CF     # VIA  Viacoin    testnet : tprv / tpub
0x02FAC398,     0x02FACAFD     # DOGE Dogecoin   mainnet : dgpv / dgub
0x0488ADE4,     0x0488B21E     # VTC  Vertcoin   mainnet : vtcv / vtcp
0x02CFBF60,     0x02CFBEDE     # BC   Blackcoin  mainnet : bcpv / bcpb
0x03A04DB7,     0x03A04D8B     # MEC  Megacoin   mainnet : mecv / mecp
0x0488ADE4,     0x0488B21E     # MYR  Myriadcoin mainnet : myrv / myrp
0x0488ADE4,     0x0488B21E     # UNO  Unobtanium mainnet : unov / unop
0x037A6460,     0x037A689A     # JBS  Jumbucks   mainnet : jprv / jpub
0x0488ADE4,     0x0488B21E     # MZC  Mazacoin   mainnet : xprv / xpub

I've taken them from the pycoin api implementation which supports HD wallets. https://github.com/richardkiss/pycoin/blob/master/pycoin/networks.py

Afr

Posted 2014-07-09T00:26:54.887

Reputation: 1 267

1

Rather old question I realize, but I thought I'd answer it anyways.

The xpub/xprv/etc. strings are simply big numbers represented in base-58, as opposed to the base-10 we're all familiar with. If we wanted to take a "normal" 5-digit number and prepend, say, 889 to the front, we'd simply add 88900000 to the 5-digit number:

     12345
+ 88900000
----------
  88912345

Working in base-58 is essentially the same, but as always the devil's in the details because we have to convert from a big base-58 number to a big base-256 number (the "base" of an array of bytes, which is what we're looking for).

What follows is a Python 3 script which can be run from the command line, e.g. to prepend the string xpub to 74 bytes of data (the length of a BIP32 extended key, not including the 4 trailing check bytes), the bytes to prepend before base-58-encoding are:

$ python3 prepend_bytes.py xpub 74
0x04, 0x88, 0xb2, 0x1e

#!/usr/bin/env python3

from math import log, ceil
import sys


# lookup tables to convert integers in the range [0, 58) to base-58 digits and back
int_to_b58_digit = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
b58_digit_to_int = { b58:i for i,b58 in enumerate(int_to_b58_digit) }

# convert a (long) integer to its base-58 representation string
def int_to_base58_str(int_rep):
    base58_str = ''
    while int_rep:
        int_rep, remainder = divmod(int_rep, 58)
        base58_str = int_to_b58_digit[remainder] + base58_str
    return base58_str


def prepended_bytes(prepended_b58_digits, b256_digit_count):

    # ones are a special case in base58check format;
    # count and remove them, they are added back in later
    ones = 0
    for b58 in prepended_b58_digits:
        if b58 != '1':
            break
        ones += 1
        prepended_b58_digits = prepended_b58_digits[1:]
    if not prepended_b58_digits:  # if they're all 1's
        return ones * b'\0'

    # calc the # of base58 digits required for b256_digit_count bytes of "real" data
    # (not including the prepended base58 digits)
    b58_digit_count = ceil(b256_digit_count * log(256) / log(58))

    do_overflow_check = True
    while True:
        # calc the minimum integral value which starts with the desired digits in base-58
        min_int = 0
        for b58 in prepended_b58_digits:
            min_int *= 58
            min_int += b58_digit_to_int[b58]
        #
        # left-shift (mult. by a power of 58) to be just left of the "real" base-58 data
        min_int *= 58 ** b58_digit_count

        # uncomment to sanity-check that min_int is correct
        #print(" min_int:", ones * '1' + int_to_base58_str(min_int))

        # right-shift by multiples of 8 bits (base-256) to retrieve only the
        # most-significant bytes which are to the left of the "real" base-256 data
        min_int >>= b256_digit_count * 8
        # right-shifing effectively rounds min_int down, but we
        # need it rounded up, so add one to round it up instead
        min_int += 1

        # because min_int has been rounded up above, it's possible that adding it to "real"
        # data could cause an overflow in base-58 making the prepended_b58_digits increment
        # by one; if this could happen, left-shift prepended_b58_digits and repeat
        if do_overflow_check:
            max_real_data_int = (1 << b256_digit_count*8) - 1
            max_base58_str = int_to_base58_str((min_int << b256_digit_count*8) + max_real_data_int)
            if not max_base58_str.startswith(prepended_b58_digits):
                prepended_b58_digits += '1'
                do_overflow_check = False  # it doesn't matter if the '1' appended above overflows to '2'

                # uncomment to confirm that the max possible value
                # wouldn't have the desired prepended base-58 digits
                #print("overflow:", ones * '1' + max_base58_str)

                continue

        # prepend any ones according to base58check rules, and convert min_int to a byte string
        return ones * b'\0' + min_int.to_bytes(length= (min_int.bit_length() + 7) // 8, byteorder='big')


if __name__ == '__main__':
    if len(sys.argv) != 3:
        sys.exit('usage: {} <STRING-TO-PREPEND> <DATA-BYTE-LEN (excluding 4-byte checksum)>'.format(sys.argv[0]))

    result = prepended_bytes(sys.argv[1], int(sys.argv[2]) + 4)  # add 4 for the checksum
    print(', '.join('{:#04x}'.format(i) for i in result))

Christopher Gurnee

Posted 2014-07-09T00:26:54.887

Reputation: 2 263