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))
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))
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))
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))
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], }