def test_duplicate_xfp(N, offer_ms_import, need_keypress, test_ms_show_addr): # it's legit to have duplicate XFP values! Not hard to make either! # new wallet will all having same XFP, but different xpubs pk = BIP32Node.from_wallet_key(simulator_fixed_xprv) keys = [(simulator_fixed_xfp, pk, pk.subkey(45, is_hardened=True, as_private=False))] lst = [keys[0][-1]] for idx in range(N-1): h = BIP32Node.from_hwif(pk.hwif(as_private=True)) # deepcopy h._chain_code = b'chain code is 32 bytes: %08d' % idx subkey = h.subkey(45, is_hardened=True, as_private=False) lst.append(subkey) xfp = unpack("<I", pk.fingerprint())[0] keys.append( (xfp, h, subkey) ) #print(lst) # bare, no fingerprints # - no xfps # - no meta data config = '\n'.join(sk.hwif(as_private=False) for sk in lst) title, story = offer_ms_import(config) assert f'Policy: {N} of {N}\n' in story need_keypress('y') test_ms_show_addr(N, keys)
def sign_transaction(self, tx, master_password, path=''): """ Args: tx: hex transaction to sign master_password: master password for BIP32 wallets. Can be either a master_secret or a wif path (Optional[str]): optional path to the leaf address of the BIP32 wallet. This allows us to retrieve private key for the leaf address if one was used to construct the transaction. Returns: signed transaction .. note:: Only BIP32 hierarchical deterministic wallets are currently supported. """ netcode = 'XTN' if self.testnet else 'BTC' # TODO review # check if its a wif try: BIP32Node.from_text(master_password) return bitcoin.signall(tx, master_password) except (AttributeError, EncodingError): # if its not get the wif from the master secret return bitcoin.signall(tx, BIP32Node.from_master_secret(master_password, netcode=netcode).subkey_for_path(path).wif())
def sign_transaction(self, tx, master_password, path=''): """ Args: tx: hex transaction to sign master_password: master password for BIP32 wallets. Can be either a master_secret or a wif path (Optional[str]): optional path to the leaf address of the BIP32 wallet. This allows us to retrieve private key for the leaf address if one was used to construct the transaction. Returns: signed transaction .. note:: Only BIP32 hierarchical deterministic wallets are currently supported. """ netcode = 'XTN' if self.testnet else 'BTC' # TODO review # check if its a wif try: BIP32Node.from_text(master_password) return bitcoin.signall(tx, master_password) except (AttributeError, EncodingError): # if its not get the wif from the master secret return bitcoin.signall( tx, BIP32Node.from_master_secret( master_password, netcode=netcode).subkey_for_path(path).wif())
def test_refill_main_wallet(spool_regtest, rpconn): src_wallet_passowrd = uuid1().hex.encode('utf-8') src_wallet = BIP32Node.from_master_secret(src_wallet_passowrd, netcode='XTN') dest_wallet_passowrd = uuid1().hex.encode('utf-8') dest_wallet = BIP32Node.from_master_secret(dest_wallet_passowrd, netcode='XTN') src_address = src_wallet.bitcoin_address() dest_address = dest_wallet.bitcoin_address() rpconn.importaddress(src_address) rpconn.importaddress(dest_address) rpconn.sendtoaddress(src_address, 1) rpconn.generate(1) txid = spool_regtest.refill_main_wallet( ('', src_address), dest_address, 1, 1, src_wallet_passowrd, min_confirmations=1, ) rpconn.generate(1) raw_txid = rpconn.getrawtransaction(txid) raw_tx = rpconn.decoderawtransaction(raw_txid) values = (vout['value'] * 100000000 for vout in raw_tx['vout'] if vout['scriptPubKey']['addresses'].pop() == dest_address) assert spool_regtest.FEE in values assert spool_regtest.TOKEN in values assert (rpconn.getreceivedbyaddress(dest_address) * 100000000 == spool_regtest.FEE + spool_regtest.TOKEN)
def test_bip32(self): master = "tpubD9q6vq9zdP3gbhpjs7n2TRvT7h4PeBhxg1Kv9jEc1XAss7429VenxvQTsJaZhzTk54gnsHRpgeeNMbm1QTag4Wf1QpQ3gy221GDuUCxgfeZ" key = BIP32Node.from_hwif(master) self.assertEqual( b2x(key.sec()), "022f6b9339309e89efb41ecabae60e1d40b7809596c68c03b05deb5a694e33cd26" ) self.assertEqual( key.subkey_for_path("0").hwif(), "tpubDAtJthHcm9MJwmHp4r2UwSTmiDYZWHbQUMqySJ1koGxQpRNSaJdyL2Ab8wwtMm5DsMMk3v68299LQE6KhT8XPQWzxPLK5TbTHKtnrmjV8Gg" ) self.assertEqual( key.subkey_for_path("0/0").hwif(), "tpubDDfqpEKGqEVa5FbdLtwezc6Xgn81teTFFVA69ZfJBHp4UYmUmhqVZMmqXeJBDahvySZrPjpwMy4gKfNfrxuFHmzo1r6srB4MrsDKWbwEw3d" ) key = BIP32Node.from_master_secret( x("000102030405060708090a0b0c0d0e0f"), "XTN") self.assertEqual( key.subkey_for_path("0'/1/2'/2/1000000000").hwif(), "tpubDHNy3kAG39ThyiwwsgoKY4iRenXDRtce8qdCFJZXPMCJg5dsCUHayp84raLTpvyiNA9sXPob5rgqkKvkN8S7MMyXbnEhGJMW64Cf4vFAoaF" )
def test_streams(self): m0 = BIP32Node.from_master_secret("foo bar baz".encode("utf8")) pm0 = m0.public_copy() self.assertEqual(m0.wallet_key(), pm0.wallet_key()) m1 = m0.subkey() pm1 = pm0.subkey() for i in range(4): m = m1.subkey(i=i) pm = pm1.subkey(i=i) self.assertEqual(m.wallet_key(), pm.wallet_key()) self.assertEqual(m.bitcoin_address(), pm.bitcoin_address()) m2 = BIP32Node.from_wallet_key(m.wallet_key(as_private=True)) m3 = m2.public_copy() self.assertEqual(m.wallet_key(as_private=True), m2.wallet_key(as_private=True)) self.assertEqual(m.wallet_key(), m3.wallet_key()) print(m.wallet_key(as_private=True)) for j in range(2): k = m.subkey(i=j) k2 = BIP32Node.from_wallet_key(k.wallet_key(as_private=True)) k3 = BIP32Node.from_wallet_key(k.wallet_key()) k4 = k.public_copy() self.assertEqual(k.wallet_key(as_private=True), k2.wallet_key(as_private=True)) self.assertEqual(k.wallet_key(), k2.wallet_key()) self.assertEqual(k.wallet_key(), k3.wallet_key()) self.assertEqual(k.wallet_key(), k4.wallet_key()) print(" %s %s" % (k.bitcoin_address(), k.wif()))
def test_b9p_vectors(dev, set_seed_words, need_keypress, vector, pw='RoZert'[::-1].upper()): # Test all BIP39 vectors. Slow. _, words, cooked, xprv = vector seed = Mnemonic.to_seed(words, passphrase=pw) assert seed == a2b_hex(cooked) set_seed_words(words) dev.send_recv(CCProtocolPacker.bip39_passphrase(pw), timeout=None) need_keypress('y') xpub = None while xpub == None: time.sleep(0.050) xpub = dev.send_recv(CCProtocolPacker.get_passphrase_done(), timeout=None) # check our math (ignore testnet vs. mainnet) got = BIP32Node.from_wallet_key(xpub) exp = BIP32Node.from_wallet_key(xprv) assert got.public_pair() == exp.public_pair()
def init_wallet(self, identifier, passphrase): netcode = "XTN" if self.testnet else "BTC" data = self.get_wallet(identifier) primary_seed = Mnemonic.to_seed(data['primary_mnemonic'], passphrase) primary_private_key = BIP32Node.from_master_secret(primary_seed, netcode=netcode) backup_public_key = BIP32Node.from_hwif(data['backup_public_key'][0]) checksum = self.create_checksum(primary_private_key) if checksum != data['checksum']: raise Exception("Checksum [%s] does not match expected checksum [%s], most likely due to incorrect password" % (checksum, data['checksum'])) blocktrail_public_keys = data['blocktrail_public_keys'] key_index = data['key_index'] return Wallet( client=self, identifier=identifier, primary_mnemonic=data['primary_mnemonic'], primary_private_key=primary_private_key, backup_public_key=backup_public_key, blocktrail_public_keys=blocktrail_public_keys, key_index=key_index, testnet=self.testnet )
def pycoinWallet(public_key=None, public=True, testnet=settings.BTC_TESTNET, password=None): netcode = 'XTN' if testnet else 'BTC' if public: return BIP32Node.from_wallet_key(public_key) else: assert not password is None return BIP32Node.from_master_secret(password, netcode=netcode)
def test_export_electrum(mode, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, microsd_path): # lightly test electrum wallet export goto_home() pick_menu_item('Advanced') pick_menu_item('MicroSD Card') pick_menu_item('Export Wallet') pick_menu_item('Electrum Wallet') time.sleep(0.1) title, story = cap_story() assert 'This saves a skeleton Electrum wallet' in story need_keypress('y') time.sleep(0.1) pick_menu_item(mode) time.sleep(0.1) title, story = cap_story() assert 'wallet file written' in story fname = story.split('\n')[-1] need_keypress('y') path = microsd_path(fname) with open(path, 'rt') as fp: obj = json.load(fp) ks = obj['keystore'] assert ks['ckcc_xfp'] == simulator_fixed_xfp assert ks['hw_type'] == 'coldcard' assert ks['type'] == 'hardware' deriv = ks['derivation'] assert deriv.startswith('m/') assert int(deriv.split("/")[1][:-1]) in {44, 84, 49} # weak xpub = ks['xpub'] assert xpub[1:4] == 'pub' if xpub[0] in 'tx': # no slip132 here got = BIP32Node.from_wallet_key(xpub) expect = BIP32Node.from_wallet_key( simulator_fixed_xprv).subkey_for_path(deriv[2:]) assert got.sec() == expect.sec() os.unlink(path)
def test_public(sim_execfile): "verify contents of public 'dump' file" from pycoin.key.BIP32Node import BIP32Node from pycoin.contrib.segwit_addr import encode as sw_encode from pycoin.contrib.segwit_addr import decode as sw_decode from pycoin.encoding import a2b_hashed_base58, hash160 pub = sim_execfile('devtest/dump_public.py') assert 'Error' not in pub #print(pub) pub, dev = pub.split('#DEBUG#', 1) assert 'pub' in pub assert 'prv' not in pub assert 'prv' in dev lines = [i.strip() for i in pub.split('\n')] for ln in lines: if ln[1:4] == 'pub': node_pub = BIP32Node.from_wallet_key(ln) break node_prv = BIP32Node.from_wallet_key(dev.strip()) # pub and private are linked assert node_prv.hwif(as_private=False) == node_pub.hwif() # check every path we derived count = 0 for ln in lines: if ln[0:1] == 'm' and '=>' in ln: subpath, result = ln.split(' => ', 1) sk = node_prv.subkey_for_path(subpath[2:]) if result[0:2] in {'tp', 'xp'}: expect = BIP32Node.from_wallet_key(result) assert sk.hwif(as_private=False) == result elif result[0] in '1mn': assert result == sk.address(False) elif result[0:3] in {'bc1', 'tb1'}: h20 = sk.hash160() assert result == sw_encode(result[0:2], 0, h20) elif result[0] in '23': h20 = hash160(b'\x00\x14' + sk.hash160()) assert h20 == a2b_hashed_base58(result)[1:] else: raise ValueError(result) count += 1 print("OK: %s" % ln) assert count > 12
def doit(given_addr, path=None, addr_fmt=None, script=None): if not script: try: # prefer using xpub if we can mk = BIP32Node.from_wallet_key(master_xpub) sk = mk.subkey_for_path(path[2:]) except PublicPrivateMismatchError: mk = BIP32Node.from_wallet_key(simulator_fixed_xprv) sk = mk.subkey_for_path(path[2:]) if addr_fmt == AF_CLASSIC: # easy assert sk.address() == given_addr elif addr_fmt & AFC_PUBKEY: pkh = sk.hash160(use_uncompressed=False) if addr_fmt == AF_P2WPKH: hrp, data = bech32_decode(given_addr) decoded = convertbits(data[1:], 5, 8, False) assert hrp in {'tb', 'bc' } assert bytes(decoded[-20:]) == pkh else: assert addr_fmt == AF_P2WPKH_P2SH assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert len(expect) == 20 assert hash160(b'\x00\x14' + pkh) == expect elif addr_fmt & AFC_SCRIPT: assert script, 'need a redeem/witness script' if addr_fmt == AF_P2SH: assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert hash160(script) == expect elif addr_fmt == AF_P2WSH: hrp, data = bech32_decode(given_addr) assert hrp in {'tb', 'bc' } decoded = convertbits(data[1:], 5, 8, False) assert bytes(decoded[-32:]) == sha256(script).digest() elif addr_fmt == AF_P2WSH_P2SH: assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert hash160(b'\x00\x20' + sha256(script).digest()) == expect else: raise pytest.fail(f'not ready for {addr_fmt:x} yet') else: raise ValueError(addr_fmt) return sk if not script else None
def sign_transaction(self, tx, master_password, path=''): # master_password can be either a master_secret or a wif netcode = 'XTN' if self.testnet else 'BTC' # check if its a wif try: BIP32Node.from_text(master_password) return pybitcointools.signall(tx, master_password) except EncodingError: # if its not get the wif from the master secret return pybitcointools.signall(tx, BIP32Node.from_master_secret(master_password, netcode=netcode).subkey_for_path(path).wif())
def pycoinPassword(user, password): # check if password is a wif. It it is return it witouth appending the email try: BIP32Node.from_text(password) return password except EncodingError: # backwards compatibility for collision of public wallets with same password if user.date_joined > datetime(2014, 12, 15).replace(tzinfo=pytz.UTC) \ and not user == util.mainAdminUser(): return password + user.email return password
def treegen(value, entropy=False): if entropy: # this method also takes a netcode parameter, but we don't care # what network pycoin thinks this node is, because we only use it # for key derivation. return BIP32Node.from_master_secret(unhexlify(value)) else: # this method will infer a network from the header bytes. We # don't care right now for the same reason as above, but we will # if Gem's API stops returning 'xpub' as the pubkey header bytes # because if pycoin doesn't recognize a header it will error. return BIP32Node.from_hwif(value)
def doit(M, addr_fmt=None, do_import=True): passwords = ['Me', 'Myself', 'And I', ''] if 0: # WORKING, but slow .. and it's constant data keys = [] for pw in passwords: xfp = set_bip39_pw(pw) sk = dev.send_recv(CCProtocolPacker.get_xpub("m/45'")) node = BIP32Node.from_wallet_key(sk) keys.append((xfp, None, node)) assert len(set(x for x,_,_ in keys)) == 4, keys pprint(keys) else: # Much, FASTER! assert dev.is_simulator keys = [(3503269483, None, BIP32Node.from_hwif('tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9')), (2389277556, None, BIP32Node.from_hwif('tpubD97nVL37v5tWyMf9ofh5rznwhh1593WMRg6FT4o6MRJkKWANtwAMHYLrcJFsFmPfYbY1TE1LLQ4KBb84LBPt1ubvFwoosvMkcWJtMwvXgSc')), (3190206587, None, BIP32Node.from_hwif('tpubD9ArfXowvGHnuECKdGXVKDMfZVGdephVWg8fWGWStH3VKHzT4ph3A4ZcgXWqFu1F5xGTfxncmrnf3sLC86dup2a8Kx7z3xQ3AgeNTQeFxPa')), (1130956047, None, BIP32Node.from_hwif('tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n')), ] if do_import: # render as a file for import config = f"name: Myself-{M}\npolicy: {M} / 4\n\n" if addr_fmt: config += f'format: {addr_fmt.upper()}\n' config += '\n'.join('%s: %s' % (xfp2str(xfp), sk.hwif()) for xfp, _, sk in keys) #print(config) title, story = offer_ms_import(config) #print(story) # dont care if update or create; accept it. time.sleep(.1) need_keypress('y') def select_wallet(idx): # select to specific pw xfp = set_bip39_pw(passwords[idx]) assert xfp == keys[idx][0] return (keys, select_wallet)
def doit(M, N, unique=0): keys = [] for i in range(N-1): pk = BIP32Node.from_master_secret(b'CSW is a fraud %d - %d' % (i, unique), 'XTN') xfp = unpack("<I", pk.fingerprint())[0] sub = pk.subkey(45, is_hardened=True, as_private=True) keys.append((xfp, pk, sub)) pk = BIP32Node.from_wallet_key(simulator_fixed_xprv) keys.append((simulator_fixed_xfp, pk, pk.subkey(45, is_hardened=True, as_private=True))) return keys
def sign_transaction(self, tx, master_password, path=''): # master_password can be either a master_secret or a wif netcode = 'XTN' if self.testnet else 'BTC' # check if its a wif try: BIP32Node.from_text(master_password) return pybitcointools.signall(tx, master_password) except EncodingError: # if its not get the wif from the master secret return pybitcointools.signall( tx, BIP32Node.from_master_secret( master_password, netcode=netcode).subkey_for_path(path).wif())
def test_refill_fuel(spool_regtest, rpconn): src_wallet_passowrd = uuid1().hex.encode('utf-8') src_wallet = BIP32Node.from_master_secret(src_wallet_passowrd, netcode='XTN') dest_wallet_passowrd = uuid1().hex.encode('utf-8') dest_wallet = BIP32Node.from_master_secret(dest_wallet_passowrd, netcode='XTN') src_address = src_wallet.bitcoin_address() dest_address = dest_wallet.bitcoin_address() rpconn.importaddress(src_address) rpconn.importaddress(dest_address) rpconn.sendtoaddress(src_address, spool_regtest.FEE/100000000) rpconn.sendtoaddress(src_address, spool_regtest.FEE/100000000) rpconn.sendtoaddress( src_address, spool_regtest.TOKEN/100000000) rpconn.generate(1) txid = spool_regtest.refill( ('', src_address), dest_address, 1, 1, src_wallet_passowrd, min_confirmations=1, ) rpconn.generate(1) raw_txid = rpconn.getrawtransaction(txid) raw_tx = rpconn.decoderawtransaction(raw_txid) values = (vout['value'] * 100000000 for vout in raw_tx['vout'] if vout['scriptPubKey']['addresses'].pop() == dest_address) values = [] asm = None for vout in raw_tx['vout']: try: addr = vout['scriptPubKey']['addresses'].pop() except KeyError: asm = vout['scriptPubKey']['asm'] else: if addr == dest_address: values.append(vout['value'] * 100000000) assert spool_regtest.FEE in values assert spool_regtest.TOKEN in values assert asm.split(' ')[0] == 'OP_RETURN' assert asm.split(' ')[1] == '4153435249424553504f4f4c30314655454c' assert spool_regtest.FEE in values assert spool_regtest.TOKEN in values assert (rpconn.getreceivedbyaddress(dest_address) * 100000000 == spool_regtest.FEE + spool_regtest.TOKEN)
def search(target_xfp): k = BIP32Node.from_hwif( "tprv8ZgxMBicQKsPeXJHL3vPPgTAEqQ5P2FD9qDeCQT4Cp1EMY5QkwMPWFxHdxHrxZhhcVRJ2m7BNWTz9Xre68y7mX5vCdMJ5qXMUfnrZ2si2X4" ) pid = os.getpid() target_xfp = h2b_rev(target_xfp) # test by going -33 here. #sec_exp = k._secret_exponent - 33 sec_exp = k._secret_exponent + (pid * int(1e40)) i = 0 last_xfp = None while 1: i += 1 sec_exp += 1 public_pair = ecdsa.public_pair_for_secret_exponent( ecdsa.generator_secp256k1, sec_exp) xfp = public_pair_to_hash160_sec(public_pair, compressed=True)[:4] if i <= 5: # checking code (slow) b = BIP32Node(netcode='BTC', chain_code=bytes(32), secret_exponent=sec_exp) chk = b.fingerprint() assert b._secret_exponent == sec_exp assert xfp == chk, (xfp, chk) assert xfp != last_xfp, 'repeat xfp!' last_xfp = xfp if xfp == target_xfp: print(f"\n\nFOUND: sec_exp = {sec_exp}\n") b = BIP32Node(netcode='BTC', chain_code=bytes(32), secret_exponent=sec_exp) chk = b.fingerprint() assert b._secret_exponent == sec_exp assert xfp == chk, (xfp, chk) print(b.hwif(), end='\n\n') return if not (i % 27): print(' %6d %9d' % (pid, i), end='\r')
def __init__(self, pkey=None, secret=None): if secret is not None: pkey = format( BIP32Node.from_master_secret( secret.encode('utf-8')).secret_exponent(), "064x") elif pkey is None: try: pkey = format( BIP32Node.from_master_secret( urandom(4096)).secret_exponent(), '064x') except NotImplementedError as e: raise ValueError('No randomness source found: %s' % e) self.keypair = Key(secret_exponent=int(pkey, 16))
def create_key(asset, netcode="BTC"): secure_random_data = os.urandom(32) key = BIP32Node.from_master_secret(secure_random_data, netcode=netcode) return { "asset": asset, "pubkey": b2h(key.sec()), "wif": key.wif(), "address": key.address(), }
def from_text(class_, text, is_compressed=True): """ This function will accept a BIP0032 wallet string, a WIF, or a bitcoin address. The "is_compressed" parameter is ignored unless a public address is passed in. """ data = a2b_hashed_base58(text) netcode, key_type = netcode_and_type_for_data(data) data = data[1:] if key_type in ("pub32", "prv32"): # TODO: fix this... it doesn't belong here from pycoin.key.BIP32Node import BIP32Node return BIP32Node.from_wallet_key(text) if key_type == 'wif': is_compressed = (len(data) > 32) if is_compressed: data = data[:-1] return Key( secret_exponent=from_bytes_32(data), prefer_uncompressed=not is_compressed, netcode=netcode) if key_type == 'address': return Key(hash160=data, is_compressed=is_compressed, netcode=netcode) raise EncodingError("unknown text: %s" % text)
def test_create_sign_push_transaction(transactions, rpcconn): alice = BIP32Node.from_master_secret(b"alice-secret", netcode="XTN").bitcoin_address() bob = BIP32Node.from_master_secret(b"bob-secret", netcode="XTN").bitcoin_address() rpcconn.importaddress(alice) rpcconn.importaddress(bob) rpcconn.sendtoaddress(alice, 3) rpcconn.generate(1) raw_tx = transactions.create(alice, (bob, 200000000), min_confirmations=1) assert raw_tx signed_tx = transactions.sign(raw_tx, b"alice-secret") assert signed_tx bob_before = rpcconn.getreceivedbyaddress(bob) pushed_tx = transactions.push(signed_tx) assert pushed_tx rpcconn.generate(1) # pack the transaction into a block assert rpcconn.getreceivedbyaddress(bob) - bob_before == 2
def test_get_addresses(rpconn, piece_hashes, spool_regtest, transactions): from spool import Spool from spool.spoolex import BlockchainSpider sender_password = uuid1().hex.encode('utf-8') sender_wallet = BIP32Node.from_master_secret(sender_password, netcode='XTN') sender_address = sender_wallet.bitcoin_address() rpconn.importaddress(sender_address) rpconn.sendtoaddress(sender_address, Spool.FEE/100000000) rpconn.sendtoaddress(sender_address, Spool.TOKEN/100000000) rpconn.sendtoaddress(sender_address, Spool.TOKEN/100000000) rpconn.sendtoaddress(sender_address, Spool.TOKEN/100000000) rpconn.generate(1) receiver_address = rpconn.getnewaddress() # TODO do not rely on Spool txid = spool_regtest.transfer( ('', sender_address), receiver_address, piece_hashes, sender_password, 5, min_confirmations=1, ) decoded_raw_transfer_tx = transactions.get(txid) addresses = BlockchainSpider._get_addresses(decoded_raw_transfer_tx) assert len(addresses) == 3 assert addresses[0] == sender_address assert addresses[1] == receiver_address assert addresses[2] == piece_hashes[0]
def master_xpub(dev): r = dev.send_recv(CCProtocolPacker.get_xpub('m'), timeout=None, encrypt=1) assert r[1:4] == 'pub', r if r[0:4] == dev.master_xpub[0:4]: assert r == dev.master_xpub elif dev.master_xpub: # testnet vs. mainnet difference from pycoin.key.BIP32Node import BIP32Node a = BIP32Node.from_wallet_key(r) b = BIP32Node.from_wallet_key(dev.master_xpub) assert a.secret_exponent() == b.secret_exponent() return r
def doit(given_addr, path, addr_fmt): mk = BIP32Node.from_wallet_key(master_xpub) sk = mk.subkey_for_path(path[2:]) if addr_fmt == AF_CLASSIC: # easy assert sk.address() == given_addr elif addr_fmt & AFC_PUBKEY: pkh = sk.hash160(use_uncompressed=False) if addr_fmt == AF_P2WPKH: hrp, data = bech32_decode(given_addr) decoded = convertbits(data[1:], 5, 8, False) assert hrp in {'tb', 'bc'} assert bytes(decoded[-20:]) == pkh else: assert addr_fmt == AF_P2WPKH_P2SH assert given_addr[0] in '23' expect = a2b_hashed_base58(given_addr)[1:] assert len(expect) == 20 assert hash160(b'\x00\x14' + pkh) == expect elif addr_fmt & AFC_SCRIPT: raise pytest.fail('multisig/p2sh addr not handled') else: raise ValueError(addr_fmt)
def test_sign_msg_microsd_good(sign_on_microsd, master_xpub, msg, path, addr_vs_path, addr_fmt=AF_CLASSIC): # cases we expect to work sig, addr = sign_on_microsd(msg, path) raw = b64decode(sig) assert 40 <= len(raw) <= 65 if addr_fmt != AF_CLASSIC: # TODO # - need bech32 decoder here # - pycoin can't do signature decode if addr_fmt & AFC_BECH32: assert '1' in addr return if path is None: path = 'm' if "'" not in path and 'p' not in path: # check expected addr was used mk = BIP32Node.from_wallet_key(master_xpub) sk = mk.subkey_for_path(path[2:]) addr_vs_path(addr, path, addr_fmt) # verify signature assert verify_message(sk, sig, message=msg) == True else: # just verify signature assert verify_message(addr, sig, message=msg) == True
def create_bip32node(self, slug, random_bytes): bip32_node = BIP32Node.from_master_secret(random_bytes) bip32_text = bip32_node.as_text(as_private=True) c = self._exec_sql( "insert into BIP32Key (slug, as_text) values (?, ?)", slug, bip32_text) return self.bip32node_for_slug(slug)
def test_import_prv(goto_home, pick_menu_item, cap_story, need_keypress, unit_test, cap_menu, word_menu_entry, get_secrets, microsd_path, multiple_runs, reset_seed_words): unit_test('devtest/clear_seed.py') fname = 'test-%d.txt' % os.getpid() path = microsd_path(fname) from pycoin.key.BIP32Node import BIP32Node node = BIP32Node.from_master_secret(os.urandom(32)) open(path, 'wt').write(node.hwif(as_private=True)+'\n') print("Created: %s" % path) m = cap_menu() assert m[0] == 'New Wallet' pick_menu_item('Import Existing') pick_menu_item('Import XPRV') title, body = cap_story() assert 'Select file' in body need_keypress('y'); time.sleep(.01) pick_menu_item(fname) unit_test('devtest/abort_ux.py') v = get_secrets() assert v['xpub'] == node.hwif() assert v['xprv'] == node.hwif(as_private=True) reset_seed_words()
def generate_xpriv(self, root_seed) -> str: BIP32Node = self._network.ui._bip32node_class master_key = BIP32Node.from_master_secret(root_seed) account_key = master_key.subkey_for_path( f"44p/{self._currency_number}p/0p") account_xpriv = self._get_priv(account_key) return account_xpriv
def from_text(class_, text, is_compressed=True): """ This function will accept a BIP0032 wallet string, a WIF, or a bitcoin address. The "is_compressed" parameter is ignored unless a public address is passed in. """ data = a2b_hashed_base58(text) netcode, key_type, length = netcode_and_type_for_data(data) data = data[1:] if key_type in ("pub32", "prv32"): # TODO: fix this... it doesn't belong here from pycoin.key.BIP32Node import BIP32Node return BIP32Node.from_wallet_key(text) if key_type == 'wif': is_compressed = (len(data) > 32) if is_compressed: data = data[:-1] return Key( secret_exponent=from_bytes_32(data), prefer_uncompressed=not is_compressed, netcode=netcode) if key_type == 'address': return Key(hash160=data, is_compressed=is_compressed, netcode=netcode) raise EncodingError("unknown text: %s" % text)
def __init__(self, puzzle_scripts, generator, bip32_prv_prefix=None, bip32_pub_prefix=None, wif_prefix=None, sec_prefix=None, address_prefix=None, pay_to_script_prefix=None, bech32_hrp=None): self._script_info = puzzle_scripts self._key_class = Key.make_subclass(ui_context=self, generator=generator) self._electrum_class = ElectrumWallet.make_subclass( ui_context=self, generator=generator) self._bip32node_class = BIP32Node.make_subclass(ui_context=self, generator=generator) self._parsers = [ WIFParser(generator, wif_prefix, address_prefix, self._key_class), ElectrumParser(generator, self._electrum_class), BIP32Parser(generator, bip32_prv_prefix, bip32_pub_prefix, self._bip32node_class), Hash160Parser(address_prefix, self._key_class), SECParser(generator, sec_prefix, self._key_class), AddressParser(puzzle_scripts, address_prefix, pay_to_script_prefix, bech32_hrp) ] self._bip32_prv_prefix = bip32_prv_prefix self._bip32_pub_prefix = bip32_pub_prefix self._wif_prefix = wif_prefix self._sec_prefix = sec_prefix self._address_prefix = address_prefix self._pay_to_script_prefix = pay_to_script_prefix self._bech32_hrp = bech32_hrp
def mitm_verify(self, sig, expected_xpub): # First try with Pycoin try: from pycoin.key.BIP32Node import BIP32Node from pycoin.contrib.msg_signing import verify_message from pycoin.encoding import from_bytes_32 from base64 import b64encode mk = BIP32Node.from_wallet_key(expected_xpub) return verify_message(mk, b64encode(sig), msg_hash=from_bytes_32(self.session_key)) except ImportError: pass # If Pycoin is not available, do it using ecdsa from ecdsa import BadSignatureError, SECP256k1, VerifyingKey pubkey, chaincode = decode_xpub(expected_xpub) vk = VerifyingKey.from_string(get_pubkey_string(pubkey), curve=SECP256k1) try: ok = vk.verify_digest(sig[1:], self.session_key) except BadSignatureError: ok = False return ok
def __init__(self, private={}, public={}, private_seeds={}): # It is possible to distinguish between private and public seeds # based on the string content. Consider modifying this function # to take merely one dict of seeds. Trees should still be stored # separately. self.trees = {} self.private_trees = {} self.public_trees = {} def treegen(value, entropy=False): if entropy: # this method also takes a netcode parameter, but we don't care # what network pycoin thinks this node is, because we only use it # for key derivation. return BIP32Node.from_master_secret(unhexlify(value)) else: # this method will infer a network from the header bytes. We # don't care right now for the same reason as above, but we will # if Gem's API stops returning 'xpub' as the pubkey header bytes # because if pycoin doesn't recognize a header it will error. return BIP32Node.from_hwif(value) for name, seed in iteritems(private): tree = treegen(seed) self.private_trees[name] = self.trees[name] = tree for name, seed in iteritems(private_seeds): tree = treegen(seed, True) self.private_trees[name] = self.trees[name] = tree for name, seed in iteritems(public): tree = BIP32Node.from_hwif(seed) self.public_trees[name] = self.trees[name] = tree
def test_stub_menu(sim_execfile, goto_address_explorer, need_keypress, cap_menu, mk_common_derivations, parse_display_screen, validate_address): # For a given wallet, ensure the explorer shows the correct stub addresses node_prv = BIP32Node.from_wallet_key( sim_execfile('devtest/dump_private.py').strip()) common_derivs = mk_common_derivations(node_prv.netcode()) # capture menu address stubs goto_address_explorer() need_keypress('4') time.sleep(.01) m = cap_menu() for idx, (path, addr_format) in enumerate(common_derivs): # derive index=0 address subpath = path.format(account=0, change=0, idx=0) # e.g. "m/44'/1'/0'/0/0" sk = node_prv.subkey_for_path(subpath[2:]) # capture full index=0 address from display screen & validate it goto_address_explorer(click_idx=idx) addr_dict = parse_display_screen(0, 10) if subpath not in addr_dict: raise Exception( 'Subpath ("%s") not found in address explorer display' % subpath) expected_addr = addr_dict[subpath] validate_address(expected_addr, sk) # validate that stub is correct [start, end] = m[idx].split('-') assert expected_addr.startswith(start) assert expected_addr.endswith(end)
def prefix_transforms_for_network(network): def _create_bip32(_): max_retries = 64 for _ in range(max_retries): try: return BIP32Node.from_master_secret(get_entropy(), netcode=network) except ValueError as e: continue # Probably a bug if we get here raise RuntimeError("can't create BIP32 key") return ( ("P:", lambda s: BIP32Node.from_master_secret(s.encode("utf8"), netcode=network)), ("H:", lambda s: BIP32Node.from_master_secret(h2b(s), netcode=network)), ("E:", lambda s: key_from_text(s)), ("create", _create_bip32), )
def bip32node_for_slug(self, slug): c = self._exec_sql("select id, as_text from BIP32Key where slug=?", slug) r = c.fetchone() if r is None: return None bip32_node = BIP32Node.from_hwif(r[1]) bip32_node.id = r[0] return bip32_node
def test_testnet(self): # WARNING: these values have not been verified independently. TODO: do so master = BIP32Node.from_master_secret(h2b("000102030405060708090a0b0c0d0e0f"), netcode='XTN') self.assertEqual( master.wallet_key(as_private=True), "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5kh" "qjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m") self.assertEqual(master.bitcoin_address(), "mkHGce7dctSxHgaWSSbmmrRWsZfzz7MxMk") self.assertEqual(master.wif(), "cVPXTF2TnozE1PenpP3x9huctiATZmp27T9Ue1d8nqLSExoPwfN5")
def create(self): network = "BTC" if not self.proxy.use_production: network = "XTN" entropy = "".join([chr(random.getrandbits(8)) for i in range(32)]) key = BIP32Node.from_master_secret(entropy, network) private = key.wallet_key(as_private=True).encode("utf-8") public = key.wallet_key(as_private=False).encode("utf-8") return {"xpub": public, "xprv": private}
def _create_bip32(_): max_retries = 64 for _ in range(max_retries): try: return BIP32Node.from_master_secret(get_entropy(), netcode=network) except ValueError as e: continue # Probably a bug if we get here raise RuntimeError("can't create BIP32 key")
def create_new_address(self): n = n_addresses.incr() bip32node = BIP32Node.from_hwif(xpub.get()) subkey = bip32node.subkey(0).subkey(n) # match electrum path new_address = subkey.address() addr_to_uid[new_address] = self.uid uid_to_addr[self.uid] = new_address all_addresses.add(new_address) return True
def _create(_): max_retries = 64 for _ in range(max_retries): try: return BIP32Node.from_master_secret(get_entropy(), netcode=args.network) except ValueError as e: continue # Probably a bug if we get here raise e
def cosign_spend_request(xprvkey_or_wallet, req_keys, inputs, xpub_check): """ Sign the inputs of a transaction, given the sighashs and subkey paths for each input Args: xprvkey_or_wallet = 111-char base58 encoded serialization of BIP32 wallet or pycoin.key.BIP32Node object (w/ private key) req_keys = dictionary: key is subpath ('a/b/c', but only 'a' for now as a string) value is tuple: (address, public pair) ... optional checking data inputs = list of by transaction input: (subpath, sighash_all value) Returns: list of 3-tuples: (der-encoded signature, sighash, subpath) """ # We need just these features from pycoin <https://github.com/richardkiss/pycoin> from pycoin import ecdsa from pycoin.key.BIP32Node import BIP32Node from pycoin.tx.script import der # We need a BIP32 "wallet" for the root of all keys. if isinstance(xprvkey_or_wallet, basestring): wallet = BIP32Node.from_wallet_key(xprvkey_or_wallet.strip()) else: wallet = xprvkey_or_wallet # Verify we are looking at the right extended private key check = wallet.hwif(as_private=False)[-len(xpub_check) :] if check != xpub_check: raise ValueError("This private key isn't the right one for xpub...%s" % xpub_check) # Make the right subkey for each inputs wallets = {} for sp, (addr_check, ppair) in req_keys.items(): w = wallet.subkey_for_path(sp) assert w.bitcoin_address() == addr_check assert w.public_pair() == tuple(ppair) wallets[sp] = w # Generate a signature for each input required sigs = [] SIGHASH_ALL = 1 order = ecdsa.generator_secp256k1.order() for sp, sighash in inputs: sighash_int = int(sighash, 16) r, s = ecdsa.sign(ecdsa.generator_secp256k1, wallets[sp].secret_exponent(), sighash_int) if s + s > order: s = order - s sig = der.sigencode_der(r, s) + chr(SIGHASH_ALL) sigs.append((sig.encode("hex"), sighash, sp)) return sigs
def test_vector_1(self): master = BIP32Node.from_master_secret(h2b("000102030405060708090a0b0c0d0e0f")) self.assertEqual(master.wallet_key(as_private=True), "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi") self.assertEqual(master.bitcoin_address(), "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma") self.assertEqual(master.wif(), "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW") self.assertEqual(master.wallet_key(), "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8") m0p = master.subkey(is_hardened=True) self.assertEqual(m0p.wallet_key(), "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw") self.assertEqual(m0p.wallet_key(as_private=True), "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7") self.assertEqual(master.subkey_for_path("0p").wallet_key(), m0p.wallet_key()) pub_mp0 = master.subkey(is_hardened=True, as_private=False) self.assertEqual(pub_mp0.wallet_key(), m0p.wallet_key()) self.assertEqual(master.subkey_for_path("0p.pub").wallet_key(), pub_mp0.wallet_key()) m0p1 = m0p.subkey(i=1) self.assertEqual(m0p1.wallet_key(), "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ") self.assertEqual(m0p1.wallet_key(as_private=True), "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs") self.assertEqual(master.subkey_for_path("0p/1").wallet_key(), m0p1.wallet_key()) pub_m0p1 = m0p.subkey(i=1, as_private=False) self.assertEqual(pub_m0p1.wallet_key(), m0p1.wallet_key()) self.assertEqual(master.subkey_for_path("0p/1.pub").wallet_key(), pub_m0p1.wallet_key()) m0p1_1_2p = m0p1.subkey(i=2, is_hardened=True) self.assertEqual(m0p1_1_2p.wallet_key(), "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5") self.assertEqual(m0p1_1_2p.wallet_key(as_private=True), "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM") self.assertEqual(master.subkey_for_path("0p/1/2p").wallet_key(), m0p1_1_2p.wallet_key()) pub_m0p1_1_2p = m0p1.subkey(i=2, as_private=False, is_hardened=True) self.assertEqual(pub_m0p1_1_2p.wallet_key(), m0p1_1_2p.wallet_key()) self.assertEqual(master.subkey_for_path("0p/1/2p.pub").wallet_key(), pub_m0p1_1_2p.wallet_key()) m0p1_1_2p_2 = m0p1_1_2p.subkey(i=2) self.assertEqual(m0p1_1_2p_2.wallet_key(), "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV") self.assertEqual(m0p1_1_2p_2.wallet_key(as_private=True), "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334") self.assertEqual(master.subkey_for_path("0p/1/2p/2").wallet_key(), m0p1_1_2p_2.wallet_key()) pub_m0p1_1_2p_2 = m0p1_1_2p.subkey(i=2, as_private=False) self.assertEqual(pub_m0p1_1_2p_2.wallet_key(), m0p1_1_2p_2.wallet_key()) self.assertEqual(master.subkey_for_path("0p/1/2p/2.pub").wallet_key(), pub_m0p1_1_2p_2.wallet_key()) m0p1_1_2p_2_1000000000 = m0p1_1_2p_2.subkey(i=1000000000) self.assertEqual(m0p1_1_2p_2_1000000000.wallet_key(), "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy") self.assertEqual(m0p1_1_2p_2_1000000000.wallet_key(as_private=True), "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76") self.assertEqual(master.subkey_for_path("0p/1/2p/2/1000000000").wallet_key(), m0p1_1_2p_2_1000000000.wallet_key()) pub_m0p1_1_2p_2_1000000000 = m0p1_1_2p_2.subkey(i=1000000000, as_private=False) self.assertEqual(pub_m0p1_1_2p_2_1000000000.wallet_key(), m0p1_1_2p_2_1000000000.wallet_key()) self.assertEqual(master.subkey_for_path("0p/1/2p/2/1000000000.pub").wallet_key(), pub_m0p1_1_2p_2_1000000000.wallet_key())
def test_repr(self): from pycoin.key import Key netcode = 'XTN' key = Key(secret_exponent=273, netcode=netcode) wallet = BIP32Node.from_master_secret(bytes(key.wif().encode('ascii')), netcode) address = wallet.address() pub_k = wallet.from_text(address) self.assertEqual(repr(pub_k), '<myb5gZNXePNf2E2ksrjnHRFCwyuvt7oEay>') wif = wallet.wif() priv_k = wallet.from_text(wif) self.assertEqual(repr(priv_k), 'private_for <03ad094b1dc9fdce5d3648ca359b4e210a89d049532fdd39d9ccdd8ca393ac82f4>')
def __init__(self, password, testnet=False): """ Create a BIP32 wallet. Addresses return by the wallet are of the form (path, address) :password: master secret for the wallet :testnet: testnet flag. Defaults to false :returns: instance of the Wallet """ netcode = 'XTN' if testnet else 'BTC' self.wallet = BIP32Node.from_master_secret(password, netcode=netcode) self.root_address = ('', self.wallet.address())
def __init__(self): conf = read_config_file("ew.conf") if conf['entropy']: entropy = binascii.unhexlify(conf['entropy']) else: mnemo = Mnemonic('english') entropy = mnemo.to_entropy(conf['passphrase']) print("entropy=" + entropy.hex()) master = BIP32Node.from_master_secret(entropy, 'BTC') print("master address=" + master.address()) # /m/4544288'/0'/0'/0/0 alias alias = master.subkey(i=EW_DERIVATION, is_hardened=True).subkey(i=0, is_hardened=True).subkey(i=0, is_hardened=True).subkey(i=0, is_hardened=False).subkey(i=0, is_hardened=False) self.address = alias.address() print("alias address=" + self.address) self.key = CBitcoinSecret(alias.wif())
def olsign(key, proposal, url, upload, html, output): if not url and not proposal: raise click.BadParameter( "Need a URL to fetch proposal from (--url), or the file itself (-i file.json)") # get the proposal JSON try: if url: proposal = requests.get(url).json() else: proposal = simplejson.load(proposal) except JSONDecodeError: raise click.UsageError("Does not contain valid JSON") # unwrap signature, checking it as we go proposal = check_sig_and_unwrap(proposal) click.echo(''' Co-signing as: {cosigner} Required xpubkey: ...{xpubkey_check} '''.format(**proposal)) # unpack their private key (to test if suitable) wallet = BIP32Node.from_wallet_key(key.read().strip()) check = wallet.hwif(as_private = False)[-8:] if check != proposal['xpubkey_check']: raise click.UsageError('This private key is not the one we need as this co-signer.') #pprint(proposal.keys()) # present a summary of what will happen if html: show_page(proposal) sigs = do_signing(wallet, proposal['req_keys'], proposal['inputs']) package = package_for_ck(wallet, proposal, sigs) if output: output.write(package) click.echo("Wrote result to: %s" % output.name) if upload: upload_to_ck(package) if not output and not upload: click.echo("JSON response:\n\n%s" % package)
def generate_revocation_addresses(config): key_path = config.key_path if config.key_path else '' output_handle = open(config.output_file, 'w') if config.output_file else sys.stdout try: key = BIP32Node.from_text(config.extended_public_key) except: print('The extended public (or private) key seems invalid.') sys.exit() key_path_batch = key.subkey_for_path(key_path) for i in range(config.number_of_addresses): subkey = key_path_batch.subkey(i) output_handle.write("{0}\n".format(subkey.address(config.use_uncompressed))) if output_handle is not sys.stdout: output_handle.close()
def __init__(self, w3, mnemonic, index): self.w3 = w3 master_key = BIP32Node.from_master_secret( Mnemonic("english").to_seed(mnemonic)) purpose_subtree = master_key.subkey(i=44, is_hardened=True) coin_type_subtree = purpose_subtree.subkey(i=60, is_hardened=True) account_subtree = coin_type_subtree.subkey(i=0, is_hardened=True) change_subtree = account_subtree.subkey(i=0) account = change_subtree.subkey(i=index) self.private_key = account.secret_exponent().to_bytes(32, 'big') public_key = ecdsa.SigningKey.from_string(string=self.private_key, curve=ecdsa.SECP256k1, hashfunc=hashlib.sha256).get_verifying_key() self.address = self.w3.toChecksumAddress( "0x" + self.w3.sha3(hexstr=public_key.to_string().hex())[12:].hex())
def __init__(self, password, testnet=False): """ Initializes a BIP32 wallet. Addresses returned by the wallet are of the form ``(path, address)``. Args: password (bytes): Master secret for the wallet. The password can also be passed as a string (``str``). testnet (bool): Wwether to use the bitcoin testnet or mainnet. Defaults to ``False``. """ netcode = 'XTN' if testnet else 'BTC' if isinstance(password, str): password = password.encode() self.wallet = BIP32Node.from_master_secret(password, netcode=netcode) self.root_address = ('', self.wallet.address())
def get_new_address(wallet_num): passphrase = sha256(b"%s" % random.randint(0, 2**30)).hexdigest() wallet = BIP32Node.from_master_secret(passphrase) ret = dump_node(wallet) children = [] # Now build up some random paths # Just go five deep path = "m" for depth in range(5): child_number = random.randint(0, 0x80000000) path = "%s/%s" % (path, child_number) prime = random.choice([True, False]) if prime: path += "'" children.append( {"path": path, "child": dump_node(wallet.subkey_for_path(path[2:]))}) ret['children'] = children return ret
def test_is_public_private_bip32_valid(self): WALLET_KEYS = ["foo", "1", "2", "3", "4", "5"] # not all networks support BIP32 yet for netcode in "BTC XTN DOGE".split(): for wk in WALLET_KEYS: wallet = BIP32Node.from_master_secret(wk.encode("utf8"), netcode=netcode) text = wallet.wallet_key(as_private=True) self.assertEqual(is_private_bip32_valid(text, allowable_netcodes=NETWORK_NAMES), netcode) self.assertEqual(is_public_bip32_valid(text, allowable_netcodes=NETWORK_NAMES), None) a = text[:-1] + chr(ord(text[-1])+1) self.assertEqual(is_private_bip32_valid(a, allowable_netcodes=NETWORK_NAMES), None) self.assertEqual(is_public_bip32_valid(a, allowable_netcodes=NETWORK_NAMES), None) text = wallet.wallet_key(as_private=False) self.assertEqual(is_private_bip32_valid(text, allowable_netcodes=NETWORK_NAMES), None) self.assertEqual(is_public_bip32_valid(text, allowable_netcodes=NETWORK_NAMES), netcode) a = text[:-1] + chr(ord(text[-1])+1) self.assertEqual(is_private_bip32_valid(a, allowable_netcodes=NETWORK_NAMES), None) self.assertEqual(is_public_bip32_valid(a, allowable_netcodes=NETWORK_NAMES), None)
def evaluate_key_input(self, txt): self.ext_key_widget.clear() self.subkey_widget.clear() txt = str(txt) if not txt: return self.invalid_key_label.setVisible(False) # Variable substitution. elif txt.startswith('$'): return try: key = BIP32Node.from_hwif(txt) except Exception as e: self.invalid_key_label.setVisible(True) else: self.invalid_key_label.setVisible(False) self.ext_key_widget.set_key(key) self.derive_child() finally: self.ext_key_widget.mapper.setCurrentIndex(0)
def test_vector_2(self): master = BIP32Node.from_master_secret(h2b("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542")) self.assertEqual(master.wallet_key(as_private=True), "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U") self.assertEqual(master.wallet_key(), "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") m0 = master.subkey() self.assertEqual(m0.wallet_key(), "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH") self.assertEqual(m0.wallet_key(as_private=True), "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt") pub_m0 = master.subkey(as_private=False) self.assertEqual(pub_m0.wallet_key(), m0.wallet_key()) m0_2147483647p = m0.subkey(i=2147483647, is_hardened=True) self.assertEqual(m0_2147483647p.wallet_key(), "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a") self.assertEqual(m0_2147483647p.wallet_key(as_private=True), "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9") pub_m0_2147483647p = m0.subkey(i=2147483647, is_hardened=True, as_private=False) self.assertEqual(pub_m0_2147483647p.wallet_key(), m0_2147483647p.wallet_key()) m0_2147483647p_1 = m0_2147483647p.subkey(i=1) self.assertEqual(m0_2147483647p_1.wallet_key(), "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon") self.assertEqual(m0_2147483647p_1.wallet_key(as_private=True), "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef") pub_m0_2147483647p_1 = m0_2147483647p.subkey(i=1, as_private=False) self.assertEqual(pub_m0_2147483647p_1.wallet_key(), m0_2147483647p_1.wallet_key()) pub_m0_2147483647p_1 = pub_m0_2147483647p.subkey(i=1, as_private=False) self.assertEqual(pub_m0_2147483647p_1.wallet_key(), m0_2147483647p_1.wallet_key()) m0_2147483647p_1_2147483646p = m0_2147483647p_1.subkey(i=2147483646, is_hardened=True) self.assertEqual(m0_2147483647p_1_2147483646p.wallet_key(), "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL") self.assertEqual(m0_2147483647p_1_2147483646p.wallet_key(as_private=True), "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc") pub_m0_2147483647p_1_2147483646p = m0_2147483647p_1.subkey(i=2147483646, as_private=False, is_hardened=True) self.assertEqual(pub_m0_2147483647p_1_2147483646p.wallet_key(), m0_2147483647p_1_2147483646p.wallet_key()) m0_2147483647p_1_2147483646p_2 = m0_2147483647p_1_2147483646p.subkey(i=2) self.assertEqual(m0_2147483647p_1_2147483646p_2.wif(), "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK") self.assertEqual(m0_2147483647p_1_2147483646p_2.wallet_key(), "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt") self.assertEqual(m0_2147483647p_1_2147483646p_2.wallet_key(as_private=True), "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j") pub_m0_2147483647p_1_2147483646p_2 = m0_2147483647p_1_2147483646p.subkey(i=2, as_private=False) self.assertEqual(pub_m0_2147483647p_1_2147483646p_2.wallet_key(), m0_2147483647p_1_2147483646p_2.wallet_key()) pub_m0_2147483647p_1_2147483646p_2 = pub_m0_2147483647p_1_2147483646p.subkey(i=2, as_private=False) self.assertEqual(pub_m0_2147483647p_1_2147483646p_2.wallet_key(), m0_2147483647p_1_2147483646p_2.wallet_key()) self.assertEqual(master.subkey_for_path("0/2147483647p/1/2147483646p/2").wallet_key(), m0_2147483647p_1_2147483646p_2.wallet_key()) self.assertEqual(master.subkey_for_path("0/2147483647p/1/2147483646p/2.pub").wallet_key(), pub_m0_2147483647p_1_2147483646p_2.wallet_key())