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, colormap, config): """Create a deterministic wallet address manager given a color map <colormap> and a configuration <config>. Note address manager configuration is in the key "hdwam". """ self.config = config self.testnet = config.get('testnet', False) self.colormap = colormap self.addresses = [] # initialize the wallet manager if this is the first time # this will generate a master key. params = config.get('hdwam', None) if params is None: params = self.init_new_wallet() # master key is stored in a separate config entry self.master_key = config['hdw_master_key'] master = hashlib.sha512(self.master_key.decode('hex')).digest() self.pycoin_wallet = BIP32Node(netcode='BTC', chain_code=master[32:], secret_exponent=from_bytes_32( master[:32])) self.genesis_color_sets = params['genesis_color_sets'] self.color_set_states = params['color_set_states'] # import the genesis addresses for i, color_desc_list in enumerate(self.genesis_color_sets): addr = self.get_genesis_address(i) addr.color_set = ColorSet(self.colormap, color_desc_list) self.addresses.append(addr) # now import the specific color addresses for color_set_st in self.color_set_states: color_desc_list = color_set_st['color_set'] max_index = color_set_st['max_index'] color_set = ColorSet(self.colormap, color_desc_list) params = { 'testnet': self.testnet, 'pycoin_wallet': self.pycoin_wallet, 'color_set': color_set } for index in xrange(max_index + 1): params['index'] = index self.addresses.append(BIP0032AddressRecord(**params)) # import the one-off addresses from the config for addr_params in config.get('addresses', []): addr_params['testnet'] = self.testnet addr_params['color_set'] = ColorSet(self.colormap, addr_params['color_set']) address = LooseAddressRecord(**addr_params) self.addresses.append(address)
def nano_get_pub_wallet(path, netcode='BTC'): """ get bip32 wallet with derivable public master key :param path: derivation path :param netcode: BTC for main net :return: wallet string in xpub.... format """ _, depth = parse_bip32_path(path) public_key, _, chain_code = nano_get_key( path, key_part_pub_key + key_part_chaincode) wallet = BIP32Node(netcode, chain_code, depth, public_pair=Key.from_sec(public_key, netcode).public_pair()) return wallet.wallet_key(as_private=False)
def nano_get_priv_wallet(path, netcode='BTC'): """ get bip32 wallet with derivable private master key :param path: derivation path :param netcode: BTC for main net :return: wallet string in xprv.... format """ _, depth = parse_bip32_path(path) _, private_key, chain_code = nano_get_key( path, key_part_priv_key + key_part_chaincode) bip32_wallet = BIP32Node( netcode, chain_code, depth, secret_exponent=encoding.from_bytes_32(private_key)) return bip32_wallet.wallet_key(as_private=True)
def test_key_limits(self): nc = 'BTC' cc = b'000102030405060708090a0b0c0d0e0f' order = generator_secp256k1.order() for k in -1, 0, order, order + 1: self.assertRaises(InvalidSecretExponentError, Key, secret_exponent=k) self.assertRaises(InvalidSecretExponentError, BIP32Node, nc, cc, secret_exponent=k) for i in range(1, 512): Key(secret_exponent=i) BIP32Node(nc, cc, secret_exponent=i)
def start_second(self): self.ui = Ui_second() self.ui.setupUi(self.second) master_node = BIP32Node(self.netcode, self.chain_code, secret_exponent=int(self.secret_exponent, 16)) master_pub_node = master_node.public_copy() privkey_tree_item = PrivKeyTreeWidgetItem(key_type='Private Key', level='m', key=master_node) pubkey_tree_item = PubKeyTreeWidgetItem(key_type='Public Key', level='M', key=master_pub_node) self.privkeychain.append(privkey_tree_item) self.pubkeychain.append(pubkey_tree_item) self.ui.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.ui.treeWidget.customContextMenuRequested.connect( self.show_context_menu) self.ui.treeWidget.addTopLevelItems( [privkey_tree_item, pubkey_tree_item]) self.second.show()
def test_bitcoind_cosigning(dev, bitcoind, start_sign, end_sign, import_ms_wallet, clear_ms, explora, try_sign, need_keypress, addr_style): # Make a P2SH wallet with local bitcoind as a co-signer (and simulator) # - send an receive various # - following text of <https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md> # - the constructed multisig walelt will only work for a single pubkey on core side # - before starting this test, have some funds already deposited to bitcoind testnet wallet from pycoin.encoding import sec_to_public_pair from binascii import a2b_hex import re if addr_style == 'legacy': addr_fmt = AF_P2SH elif addr_style == 'p2sh-segwit': addr_fmt = AF_P2WSH_P2SH elif addr_style == 'bech32': addr_fmt = AF_P2WSH try: addr, = bitcoind.getaddressesbylabel("sim-cosign").keys() except: addr = bitcoind.getnewaddress("sim-cosign") info = bitcoind.getaddressinfo(addr) #pprint(info) assert info['address'] == addr bc_xfp = swab32(int(info['hdmasterfingerprint'], 16)) bc_deriv = info['hdkeypath'] # example: "m/0'/0'/3'" bc_pubkey = info['pubkey'] # 02f75ae81199559c4aa... pp = sec_to_public_pair(a2b_hex(bc_pubkey)) # No means to export XPUB from bitcoind! Still. In 2019. # - this fake will only work for for one pubkey value, the first/topmost node = BIP32Node('XTN', b'\x23'*32, depth=len(bc_deriv.split('/'))-1, parent_fingerprint=a2b_hex('%08x' % bc_xfp), public_pair=pp) keys = [ (bc_xfp, None, node), (1130956047, None, BIP32Node.from_hwif('tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n')), # simulator: m/45' ] M,N=2,2 clear_ms() import_ms_wallet(M, N, keys=keys, accept=1, name="core-cosign") cc_deriv = "m/45'/55" cc_pubkey = B2A(BIP32Node.from_hwif(simulator_fixed_xprv).subkey_for_path(cc_deriv[2:]).sec()) # NOTE: bitcoind doesn't seem to implement pubkey sorting. We have to do it. resp = bitcoind.addmultisigaddress(M, list(sorted([cc_pubkey, bc_pubkey])), 'shared-addr-'+addr_style, addr_style) ms_addr = resp['address'] bc_redeem = a2b_hex(resp['redeemScript']) assert bc_redeem[0] == 0x52 def mapper(cosigner_idx): return list(str2ipath(cc_deriv if cosigner_idx else bc_deriv)) scr, pubkeys, xfp_paths = make_redeem(M, keys, mapper) assert scr == bc_redeem # check Coldcard calcs right address to match got_addr = dev.send_recv(CCProtocolPacker.show_p2sh_address( M, xfp_paths, scr, addr_fmt=addr_fmt), timeout=None) assert got_addr == ms_addr time.sleep(.1) need_keypress('x') # clear screen / start over print(f"Will be signing an input from {ms_addr}") if xfp2str(bc_xfp) in ('5380D0ED', 'EDD08053'): # my own expected values assert ms_addr in ( '2NDT3ymKZc8iMfbWqsNd1kmZckcuhixT5U4', '2N1hZJ5mazTX524GQTPKkCT4UFZn5Fqwdz6', 'tb1qpcv2rkc003p5v8lrglrr6lhz2jg8g4qa9vgtrgkt0p5rteae5xtqn6njw9') # Need some UTXO to sign # # - but bitcoind can't give me that (using listunspent) because it's only a watched addr?? # did_fund = False while 1: rr = explora('address', ms_addr, 'utxo') pprint(rr) avail = [] amt = 0 for i in rr: txn = i['txid'] vout = i['vout'] avail.append( (txn, vout) ) amt += i['value'] # just use first UTXO available; save other for later tests break else: # doesn't need to confirm, but does need to reach public testnet/blockstream assert not amt and not avail if not did_fund: print(f"Sending some XTN to {ms_addr} (wait)") bitcoind.sendtoaddress(ms_addr, 0.0001, 'fund testing') did_fund = True else: print(f"Still waiting ...") time.sleep(2) if amt: break ret_addr = bitcoind.getrawchangeaddress() ''' If you get insufficent funds, even tho we provide the UTXO (!!), do this: bitcoin-cli importaddress "2NDT3ymKZc8iMfbWqsNd1kmZckcuhixT5U4" true true Better method: always fund addresses for testing here from same wallet (ie. got from non-multisig to multisig on same bitcoin-qt instance). -> Now doing that, automated, above. ''' resp = bitcoind.walletcreatefundedpsbt([dict(txid=t, vout=o) for t,o in avail], [{ret_addr: amt/1E8}], 0, {'subtractFeeFromOutputs': [0], 'includeWatching': True}, True) assert resp['changepos'] == -1 psbt = b64decode(resp['psbt']) open('debug/funded.psbt', 'wb').write(psbt) # patch up the PSBT a little ... bitcoind doesn't know the path for the CC's key ex = BasicPSBT().parse(psbt) cxpk = a2b_hex(cc_pubkey) for i in ex.inputs: assert cxpk in i.bip32_paths, 'input not to be signed by CC?' i.bip32_paths[cxpk] = pack('<3I', keys[1][0], *str2ipath(cc_deriv)) psbt = ex.as_bytes() open('debug/patched.psbt', 'wb').write(psbt) _, updated = try_sign(psbt, finalize=False) open('debug/cc-updated.psbt', 'wb').write(updated) # have bitcoind do the rest of the signing rr = bitcoind.walletprocesspsbt(b64encode(updated).decode('ascii')) pprint(rr) open('debug/bc-processed.psbt', 'wt').write(rr['psbt']) assert rr['complete'] # finalize and send rr = bitcoind.finalizepsbt(rr['psbt'], True) open('debug/bc-final-txn.txn', 'wt').write(rr['hex']) assert rr['complete'] txn_id = bitcoind.sendrawtransaction(rr['hex']) print(txn_id)
def Wallet(*args, **kwargs): return BIP32Node(*args, **kwargs)