Example #1
0
def get_balances():
    user1 = get_current_user(quiet=True)
    user2 = ('bob', 'alice')[user1 == 'bob']
    tbal = 0
    from mmgen.obj import BTCAmt
    for user in (user1, user2):
        p = run_cmd('./mmgen-tool', '--{}'.format(user), 'getbalance',
                    'quiet=1')
        bal = BTCAmt(p.stdout.read())
        ustr = "{}'s balance:".format(user.capitalize())
        msg('{:<16} {}'.format(ustr, bal))
        tbal += bal
    msg('{:<16} {}'.format('Total balance:', tbal))
Example #2
0
def get_balances():
    user1 = get_current_user(quiet=True)
    if user1 == None:
        die(1, 'Regtest daemon not running')
    user2 = ('bob', 'alice')[user1 == 'bob']
    tbal = 0
    from mmgen.obj import BTCAmt
    for u in (user1, user2):
        p = start_cmd('python', 'mmgen-tool', '--{}'.format(u),
                      '--data-dir=' + g.data_dir, 'getbalance', 'quiet=1')
        bal = p.stdout.read().replace(' \b', '')  # hack
        if u == user1: user(user2)
        bal = BTCAmt(bal)
        ustr = "{}'s balance:".format(u.capitalize())
        msg('{:<16} {:12}'.format(ustr, bal))
        tbal += bal
    msg('{:<16} {:12}'.format('Total balance:', tbal))
Example #3
0
class BitcoinProtocol(MMGenObject):
    name = 'bitcoin'
    daemon_name = 'bitcoind'
    daemon_family = 'bitcoind'
    addr_ver_num = {'p2pkh': ('00', '1'), 'p2sh': ('05', '3')}
    wif_ver_num = {'std': '80'}
    mmtypes = ('L', 'C', 'S', 'B')
    dfl_mmtype = 'L'
    data_subdir = ''
    rpc_port = 8332
    secs_per_block = 600
    coin_amt = BTCAmt
    max_tx_fee = BTCAmt('0.003')
    daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Bitcoin') if g.platform == 'win' \
         else os.path.join(g.home_dir,'.bitcoin')
    daemon_data_subdir = ''
    sighash_type = 'ALL'
    block0 = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
    forks = [  # height, hash, name, replayable
        (478559,
         '00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148',
         'bch', False), (None, '', 'b2x', True)
    ]
    caps = ('rbf', 'segwit')
    mmcaps = ('key', 'addr', 'rpc', 'tx')
    base_coin = 'BTC'
    base_proto = 'Bitcoin'
    # From BIP173: witness version 'n' is stored as 'OP_n'. OP_0 is encoded as 0x00,
    # but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal).
    witness_vernum_hex = '00'
    witness_vernum = int(witness_vernum_hex, 16)
    bech32_hrp = 'bc'
    sign_mode = 'daemon'
    secp256k1_ge = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
    privkey_len = 32

    @classmethod
    def is_testnet(cls):
        return cls.__name__[-15:] == 'TestnetProtocol'

    @staticmethod
    def get_protocol_by_chain(chain):
        return CoinProtocol(g.coin, {
            'mainnet': False,
            'testnet': True,
            'regtest': True
        }[chain])

    @classmethod
    def cap(cls, s):
        return s in cls.caps

    @classmethod
    def preprocess_key(cls, hexpriv, pubkey_type):
        # Key must be non-zero and less than group order of secp256k1 curve
        if 0 < int(hexpriv, 16) < cls.secp256k1_ge:
            return hexpriv
        else:  # chance of this is less than 1 in 2^127
            pk = int(hexpriv, 16)
            if pk == 0:  # chance of this is 1 in 2^256
                ydie(3, 'Private key is zero!')
            elif pk == cls.secp256k1_ge:  # ditto
                ydie(3, 'Private key == secp256k1_ge!')
            else:
                ymsg(
                    'Warning: private key is greater than secp256k1 group order!:\n  {}'
                    .format(hexpriv))
                return '{:064x}'.format(pk % cls.secp256k1_ge).encode()

    @classmethod
    def hex2wif(cls, hexpriv, pubkey_type, compressed):  # PrivKey
        assert len(
            hexpriv
        ) == cls.privkey_len * 2, '{} bytes: incorrect private key length!'.format(
            len(hexpriv) // 2)
        assert pubkey_type in cls.wif_ver_num, '{!r}: invalid pubkey_type'.format(
            pubkey_type)
        return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv +
                              ('', '01')[bool(compressed)])

    @classmethod
    def wif2hex(cls, wif):
        key = _b58chk_decode(wif)
        pubkey_type = None
        for k, v in list(cls.wif_ver_num.items()):
            if key[:len(v)] == v:
                pubkey_type = k
                key = key[len(v):]
        assert pubkey_type, 'invalid WIF version number'
        if len(key) == 66:
            assert key[-2:] == '01', 'invalid compressed key suffix'
            compressed = True
        else:
            assert len(key) == 64, 'invalid key length'
            compressed = False
        assert 0 < int(key[:64], 16) < cls.secp256k1_ge, (
            "'{}': invalid WIF (produces key outside allowable range)".format(
                wif))
        return {
            'hex': key[:64],
            'pubkey_type': pubkey_type,
            'compressed': compressed
        }

    @classmethod
    def verify_addr(cls, addr, hex_width, return_dict=False):

        if 'B' in cls.mmtypes and addr[:len(cls.bech32_hrp)] == cls.bech32_hrp:
            ret = bech32.decode(cls.bech32_hrp, addr)
            if ret[0] != cls.witness_vernum:
                msg('{}: Invalid witness version number'.format(ret[0]))
            elif ret[1]:
                return {
                    'hex': bytes(ret[1]).hex(),
                    'format': 'bech32'
                } if return_dict else True
            return False

        for addr_fmt in cls.addr_ver_num:
            ver_num, pfx = cls.addr_ver_num[addr_fmt]
            if type(pfx) == tuple:
                if addr[0] not in pfx: continue
            elif addr[:len(pfx)] != pfx: continue
            addr_hex = _b58chk_decode(addr)
            if addr_hex[:len(ver_num)] != ver_num: continue
            return {
                'hex': addr_hex[len(ver_num):],
                'format': {
                    'p2pkh': 'p2pkh',
                    'p2sh': 'p2sh',
                    'p2sh2': 'p2sh',
                    'zcash_z': 'zcash_z',
                    'viewkey': 'viewkey'
                }[addr_fmt]
            } if return_dict else True

        return False

    @classmethod
    def pubhash2addr(cls, pubkey_hash, p2sh):
        assert len(
            pubkey_hash) == 40, '{}: invalid length for pubkey hash'.format(
                len(pubkey_hash))
        s = cls.addr_ver_num[('p2pkh', 'p2sh')[p2sh]][0] + pubkey_hash
        return _b58chk_encode(s)

    # Segwit:
    @classmethod
    def pubhex2redeem_script(cls, pubhex):
        # https://bitcoincore.org/en/segwit_wallet_dev/
        # The P2SH redeemScript is always 22 bytes. It starts with a OP_0, followed
        # by a canonical push of the keyhash (i.e. 0x0014{20-byte keyhash})
        return cls.witness_vernum_hex + '14' + hash160(pubhex)

    @classmethod
    def pubhex2segwitaddr(cls, pubhex):
        return cls.pubhash2addr(hash160(cls.pubhex2redeem_script(pubhex)),
                                p2sh=True)

    @classmethod
    def pubhash2bech32addr(cls, pubhash):
        d = list(bytes.fromhex(pubhash))
        return bech32.bech32_encode(cls.bech32_hrp, [cls.witness_vernum] +
                                    bech32.convertbits(d, 8, 5))
Example #4
0
class BitcoinProtocol(MMGenObject):
    name = 'bitcoin'
    daemon_name = 'bitcoind'
    addr_ver_num = {'p2pkh': ('00', '1'), 'p2sh': ('05', '3')}
    wif_ver_num = {'std': '80'}
    mmtypes = ('L', 'C', 'S', 'B')
    dfl_mmtype = 'L'
    data_subdir = ''
    rpc_port = 8332
    secs_per_block = 600
    coin_amt = BTCAmt
    max_tx_fee = BTCAmt('0.003')
    daemon_data_dir = os.path.join(os.getenv('APPDATA'),'Bitcoin') if g.platform == 'win' \
         else os.path.join(g.home_dir,'.bitcoin')
    daemon_data_subdir = ''
    sighash_type = 'ALL'
    block0 = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'
    forks = [  # height, hash, name, replayable
        (478559,
         '00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148',
         'bch', False), (None, '', 'b2x', True)
    ]
    caps = ('rbf', 'segwit')
    mmcaps = ('key', 'addr', 'rpc', 'tx')
    base_coin = 'BTC'
    # From BIP173: witness version 'n' is stored as 'OP_n'. OP_0 is encoded as 0x00,
    # but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal).
    witness_vernum_hex = '00'
    witness_vernum = int(witness_vernum_hex, 16)
    bech32_hrp = 'bc'

    @staticmethod
    def get_protocol_by_chain(chain):
        return CoinProtocol(g.coin, {
            'mainnet': False,
            'testnet': True,
            'regtest': True
        }[chain])

    @staticmethod
    def get_rpc_coin_amt_type():
        return (float, str)[g.daemon_version >= 120000]

    @classmethod
    def cap(cls, s):
        return s in cls.caps

    @classmethod
    def preprocess_key(cls, hexpriv, pubkey_type):
        return hexpriv

    @classmethod
    def hex2wif(cls, hexpriv, pubkey_type, compressed):
        return _b58chk_encode(cls.wif_ver_num[pubkey_type] + hexpriv +
                              ('', '01')[bool(compressed)])

    @classmethod
    def wif2hex(cls, wif):
        key = _b58chk_decode(wif)
        pubkey_type = None
        for k, v in cls.wif_ver_num.items():
            if key[:len(v)] == v:
                pubkey_type = k
                key = key[len(v):]
        assert pubkey_type, 'invalid WIF version number'
        if len(key) == 66:
            assert key[-2:] == '01', 'invalid compressed key suffix'
            compressed = True
        else:
            assert len(key) == 64, 'invalid key length'
            compressed = False
        return {
            'hex': key[:64],
            'pubkey_type': pubkey_type,
            'compressed': compressed
        }

    @classmethod
    def verify_addr(cls, addr, hex_width, return_dict=False):

        if 'B' in cls.mmtypes and addr[:len(cls.bech32_hrp)] == cls.bech32_hrp:
            ret = bech32.decode(cls.bech32_hrp, addr)
            if ret[0] != cls.witness_vernum:
                msg('{}: Invalid witness version number'.format(ret[0]))
            elif ret[1]:
                return {
                    'hex': ''.join(map(chr, ret[1])).encode('hex'),
                    'format': 'bech32'
                } if return_dict else True
            return False

        for addr_fmt in cls.addr_ver_num:
            ver_num, pfx = cls.addr_ver_num[addr_fmt]
            if type(pfx) == tuple:
                if addr[0] not in pfx: continue
            elif addr[:len(pfx)] != pfx: continue
            num = _b58tonum(addr)
            if num == False:
                if g.debug: Msg('Address cannot be converted to base 58')
                break
            addr_hex = '{:0{}x}'.format(num, len(ver_num) + hex_width + 8)
            #			pmsg(hex_width,len(addr_hex),addr_hex[:len(ver_num)],ver_num)
            if addr_hex[:len(ver_num)] != ver_num: continue
            if hash256(addr_hex[:-8])[:8] == addr_hex[-8:]:
                return {
                    'hex': addr_hex[len(ver_num):-8],
                    'format': {
                        'p2pkh': 'p2pkh',
                        'p2sh': 'p2sh',
                        'p2sh2': 'p2sh',
                        'zcash_z': 'zcash_z',
                        'viewkey': 'viewkey'
                    }[addr_fmt]
                } if return_dict else True
            else:
                if g.debug: Msg('Invalid checksum in address')
                break

        return False

    @classmethod
    def pubhash2addr(cls, pubkey_hash, p2sh):
        assert len(
            pubkey_hash) == 40, '{}: invalid length for pubkey hash'.format(
                len(pubkey_hash))
        s = cls.addr_ver_num[('p2pkh', 'p2sh')[p2sh]][0] + pubkey_hash
        lzeroes = (len(s) - len(
            s.lstrip('0'))) / 2  # non-zero only for ver num '00' (BTC p2pkh)
        return ('1' * lzeroes) + _b58chk_encode(s)

    # Segwit:
    @classmethod
    def pubhex2redeem_script(cls, pubhex):
        # https://bitcoincore.org/en/segwit_wallet_dev/
        # The P2SH redeemScript is always 22 bytes. It starts with a OP_0, followed
        # by a canonical push of the keyhash (i.e. 0x0014{20-byte keyhash})
        return cls.witness_vernum_hex + '14' + hash160(pubhex)

    @classmethod
    def pubhex2segwitaddr(cls, pubhex):
        return cls.pubhash2addr(hash160(cls.pubhex2redeem_script(pubhex)),
                                p2sh=True)

    @classmethod
    def pubhash2bech32addr(cls, pubhash):
        d = map(ord, pubhash.decode('hex'))
        return bech32.bech32_encode(cls.bech32_hrp, [cls.witness_vernum] +
                                    bech32.convertbits(d, 8, 5))
Example #5
0
class g(object):

    skip_segwit_active_check = bool(os.getenv('MMGEN_TEST_SUITE'))

    def die(ev=0, s=''):
        if s: sys.stderr.write(s + '\n')
        sys.exit(ev)

    # Variables - these might be altered at runtime:

    version = '0.9.299'
    release_date = 'July 2017'

    proj_name = 'MMGen'
    proj_url = 'https://github.com/mmgen/mmgen'
    prog_name = os.path.basename(sys.argv[0])
    author = 'Philemon'
    email = '<*****@*****.**>'
    Cdates = '2013-2017'
    keywords = 'Bitcoin, cryptocurrency, wallet, cold storage, offline, online, spending, open-source, command-line, Python, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MinGW, mswin'

    coin = 'BTC'
    coins = 'BTC', 'BCH'
    ports = {'BTC': (8332, 18332), 'BCH': (8442, 18442)}

    user_entropy = ''
    hash_preset = '3'
    usr_randchars = 30
    stdin_tty = bool(sys.stdin.isatty() or os.getenv('MMGEN_TEST_SUITE'))

    max_tx_fee = BTCAmt('0.01')
    tx_fee_adj = 1.0
    tx_confs = 3
    satoshi = BTCAmt('0.00000001')  # One bitcoin equals 100,000,000 satoshis
    seed_len = 256

    http_timeout = 60
    max_int = 0xffffffff

    # Constants - some of these might be overriden, but they don't change thereafter

    debug = False
    quiet = False
    no_license = False
    hold_protect = True
    color = (False, True)[sys.stdout.isatty()]
    force_256_color = False
    testnet = False
    regtest = False
    chain = None  # set by first call to bitcoin_connection()
    chains = 'mainnet', 'testnet', 'regtest'
    bitcoind_version = None  # set by first call to bitcoin_connection()
    rpc_host = ''
    rpc_port = 0
    rpc_user = ''
    rpc_password = ''
    testnet_name = 'testnet3'

    bob = False
    alice = False

    # test suite:
    bogus_wallet_data = ''
    traceback_cmd = 'scripts/traceback.py'

    for k in ('win', 'linux'):
        if sys.platform[:len(k)] == k:
            platform = k
            break
    else:
        die(1,
            "'%s': platform not supported by %s\n" % (sys.platform, proj_name))

    if os.getenv('HOME'):  # Linux or MSYS
        home_dir = os.getenv('HOME')
    elif platform == 'win':  # Windows native:
        die(
            1,
            '$HOME not set!  {} for Windows must be run in MSYS environment'.
            format(proj_name))
    else:
        die(2, '$HOME is not set!  Unable to determine home directory')

    data_dir_root, data_dir, cfg_file = None, None, None
    bitcoin_data_dir = os.path.join(os.getenv('APPDATA'),'Bitcoin') if platform == 'win' \
         else os.path.join(home_dir,'.bitcoin')

    # User opt sets global var:
    common_opts = ('color', 'no_license', 'rpc_host', 'rpc_port', 'testnet',
                   'rpc_user', 'rpc_password', 'bitcoin_data_dir',
                   'force_256_color', 'regtest', 'coin', 'bob', 'alice')
    required_opts = ('quiet', 'verbose', 'debug', 'outdir', 'echo_passphrase',
                     'passwd_file', 'stdout', 'show_hash_presets', 'label',
                     'keep_passphrase', 'keep_hash_preset', 'yes',
                     'brain_params', 'b16', 'usr_randchars', 'coin', 'bob',
                     'alice')
    incompatible_opts = (
        ('bob', 'alice'),
        ('quiet', 'verbose'),
        ('label', 'keep_label'),
        ('tx_id', 'info'),
        ('tx_id', 'terse_info'),
        ('aug1hf', 'rbf'),  # TODO: remove in 0.9.4
        ('batch', 'rescan'))
    cfg_file_opts = ('color', 'debug', 'hash_preset', 'http_timeout',
                     'no_license', 'rpc_host', 'rpc_port', 'quiet',
                     'tx_fee_adj', 'usr_randchars', 'testnet', 'rpc_user',
                     'rpc_password', 'bitcoin_data_dir', 'force_256_color',
                     'max_tx_fee', 'regtest')
    env_opts = ('MMGEN_BOGUS_WALLET_DATA', 'MMGEN_DEBUG', 'MMGEN_QUIET',
                'MMGEN_DISABLE_COLOR', 'MMGEN_FORCE_256_COLOR',
                'MMGEN_DISABLE_HOLD_PROTECT', 'MMGEN_MIN_URANDCHARS',
                'MMGEN_NO_LICENSE', 'MMGEN_RPC_HOST', 'MMGEN_TESTNET'
                'MMGEN_REGTEST')

    min_screen_width = 80
    minconf = 1

    # Global var sets user opt:
    global_sets_opt = [
        'minconf', 'seed_len', 'hash_preset', 'usr_randchars', 'debug',
        'quiet', 'tx_confs', 'tx_fee_adj', 'key_generator'
    ]

    mins_per_block = 9
    passwd_max_tries = 5

    max_urandchars = 80
    min_urandchars = 10

    seed_lens = 128, 192, 256

    mmenc_ext = 'mmenc'
    salt_len = 16
    aesctr_iv_len = 16
    hincog_chk_len = 8

    key_generators = 'python-ecdsa', 'secp256k1'  # '1','2'
    key_generator = 2  # secp256k1 is default

    hash_presets = {
        #   Scrypt params:
        #   ID    N   p  r
        # N is a power of two
        '1': [12, 8, 1],
        '2': [13, 8, 4],
        '3': [14, 8, 8],
        '4': [15, 8, 12],
        '5': [16, 8, 16],
        '6': [17, 8, 20],
        '7': [18, 8, 24],
    }