def _do_test_bip32(self, seed: str, sequence): node = BIP32Node.from_rootseed(bfh(seed), xtype='standard') xprv, xpub = node.to_xprv(), node.to_xpub() self.assertEqual("m/", sequence[0:2]) sequence = sequence[2:] for n in sequence.split('/'): if n[-1] != "'": xpub2 = BIP32Node.from_xkey(xpub).subkey_at_public_derivation(n).to_xpub() node = BIP32Node.from_xkey(xprv).subkey_at_private_derivation(n) xprv, xpub = node.to_xprv(), node.to_xpub() if n[-1] != "'": self.assertEqual(xpub, xpub2) return xpub, xprv
def request_root_fingerprint_from_device(self) -> str: # digitalbitbox (at least) does not reveal xpubs corresponding to unhardened paths # so ask for a direct child, and read out fingerprint from that: child_of_root_xpub = self.get_xpub("m/0'", xtype='standard') root_fingerprint = BIP32Node.from_xkey( child_of_root_xpub).fingerprint.hex().lower() return root_fingerprint
def update(self, window: 'ElectrumWindow'): wallet = window.wallet if type(wallet) != Multisig_Wallet: return assert isinstance(wallet, Multisig_Wallet) # only here for type-hints in IDE if self.listener is None: self.logger.info("starting listener") self.listener = Listener(self) self.listener.start() elif self.listener: self.logger.info("shutting down listener") self.listener.stop() self.listener = None self.keys = [] self.cosigner_list = [] for key, keystore in wallet.keystores.items(): xpub = keystore.get_master_public_key() # type: str pubkey = BIP32Node.from_xkey(xpub).eckey.get_public_key_bytes( compressed=True) _hash = bh2u(crypto.sha256d(pubkey)) if not keystore.is_watching_only(): self.keys.append((key, _hash, window)) else: self.cosigner_list.append((window, xpub, pubkey, _hash)) if self.listener: self.listener.set_keyhashes([t[1] for t in self.keys])
def mitm_verify(self, sig, expect_xpub): # verify a signature (65 bytes) over the session key, using the master bip32 node # - customized to use specific EC library of Electrum. pubkey = BIP32Node.from_xkey(expect_xpub).eckey try: pubkey.verify_message_hash(sig[1:65], self.session_key) return True except: return False
def make_xpub(xpub, s) -> str: rootnode = BIP32Node.from_xkey(xpub) child_pubkey, child_chaincode = bip32._CKD_pub(parent_pubkey=rootnode.eckey.get_public_key_bytes(compressed=True), parent_chaincode=rootnode.chaincode, child_index=s) child_node = BIP32Node(xtype=rootnode.xtype, eckey=ecc.ECPubkey(child_pubkey), chaincode=child_chaincode) return child_node.to_xpub()
def request_root_fingerprint_from_device(self) -> str: # since ledger firware 1.6.1 we need to provide the full derivation path # see https://support.ledger.com/hc/en-us/articles/360015738179-Derivation-path-vulnerability-in-Bitcoin-derivati> child_of_root_xpub = self.get_xpub( "m/44'/" + str(constants.net.BIP44_COIN_TYPE) + "'", xtype='standard') root_fingerprint = BIP32Node.from_xkey( child_of_root_xpub).fingerprint.hex().lower() return root_fingerprint
def _make_node_path(self, xpub, address_n): bip32node = BIP32Node.from_xkey(xpub) node = HDNodeType( depth=bip32node.depth, fingerprint=int.from_bytes(bip32node.fingerprint, 'big'), child_num=int.from_bytes(bip32node.child_number, 'big'), chain_code=bip32node.chaincode, public_key=bip32node.eckey.get_public_key_bytes(compressed=True), ) return HDNodePathType(node=node, address_n=address_n)
def _make_node_path(self, xpub, address_n): bip32node = BIP32Node.from_xkey(xpub) node = self.types.HDNodeType( depth=bip32node.depth, fingerprint=int.from_bytes(bip32node.fingerprint, 'big'), child_num=int.from_bytes(bip32node.child_number, 'big'), chain_code=bip32node.chaincode, public_key=bip32node.eckey.get_public_key_bytes(compressed=True), ) return self.types.HDNodePathType(node=node, address_n=address_n)
def get_signing_xpub(xtype): if not constants.net.TESTNET: xpub = "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL" else: xpub = "tpubD6NzVbkrYhZ4XdmyJQcCPjQfg6RXVUzGFhPjZ7uvRC8JLcS7Hw1i7UTpyhp9grHpak4TyK2hzBJrujDVLXQ6qB5tNpVx9rC6ixijUXadnmY" if xtype not in ('standard', 'p2wsh'): raise NotImplementedError('xtype: {}'.format(xtype)) if xtype == 'standard': return xpub node = BIP32Node.from_xkey(xpub) return node._replace(xtype=xtype).to_xpub()
def manipulate_keystore_dict_during_wizard_setup(self, d: dict): master_xpub = self.dev.master_xpub if master_xpub is not None: try: node = BIP32Node.from_xkey(master_xpub) except InvalidMasterKeyVersionBytes: raise UserFacingException( _('Invalid xpub magic. Make sure your {} device is set to the correct chain.' ).format(self.device) + ' ' + _('You might have to unplug and plug it in again.') ) from None d['ckcc_xpub'] = master_xpub
def make_billing_address(wallet, num, addr_type): long_id, short_id = wallet.get_user_id() xpub = make_xpub(get_billing_xpub(), long_id) usernode = BIP32Node.from_xkey(xpub) child_node = usernode.subkey_at_public_derivation([num]) pubkey = child_node.eckey.get_public_key_bytes(compressed=True) if addr_type == 'legacy': return bitcoin.public_key_to_p2pkh(pubkey) elif addr_type == 'segwit': return bitcoin.public_key_to_p2wpkh(pubkey) else: raise ValueError(f'unexpected billing type: {addr_type}')
def on_receive(self, keyhash, message): self.logger.info(f"signal arrived for {keyhash}") for key, _hash, window in self.keys: if _hash == keyhash: break else: self.logger.info("keyhash not found") return wallet = window.wallet if isinstance(wallet.keystore, keystore.Hardware_KeyStore): window.show_warning( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('However, hardware wallets do not support message decryption, ' 'which makes them not compatible with the current design of cosigner pool.' )) return elif wallet.has_keystore_encryption(): password = window.password_dialog( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('Please enter your password to decrypt it.')) if not password: return else: password = None if not window.question( _("An encrypted transaction was retrieved from cosigning pool." ) + '\n' + _("Do you want to open it now?")): return xprv = wallet.keystore.get_master_private_key(password) if not xprv: return try: privkey = BIP32Node.from_xkey(xprv).eckey message = privkey.decrypt_message(message) except Exception as e: self.logger.exception('') window.show_error(_('Error decrypting message') + ':\n' + repr(e)) return self.listener.clear(keyhash) try: tx = tx_from_any(message) except SerializationError as e: window.show_error( _("Electrum was unable to deserialize the transaction:") + "\n" + str(e)) return show_transaction(tx, parent=window, prompt_if_unsaved=True)
def get_xpub(self, bip32_path, xtype): assert xtype in ColdcardPlugin.SUPPORTED_XTYPES _logger.info('Derive xtype = %r' % xtype) xpub = self.dev.send_recv(CCProtocolPacker.get_xpub(bip32_path), timeout=5000) # TODO handle timeout? # change type of xpub to the requested type try: node = BIP32Node.from_xkey(xpub) except InvalidMasterKeyVersionBytes: raise UserFacingException(_('Invalid xpub magic. Make sure your {} device is set to the correct chain.') .format(self.device)) from None if xtype != 'standard': xpub = node._replace(xtype=xtype).to_xpub() return xpub
def get_xpub(self, bip32_path, xtype): assert xtype in self.plugin.SUPPORTED_XTYPES reply = self._get_xpub(bip32_path) if reply: xpub = reply['xpub'] # Change type of xpub to the requested type. The firmware # only ever returns the mainnet standard type, but it is agnostic # to the type when signing. if xtype != 'standard' or constants.net.TESTNET: node = BIP32Node.from_xkey(xpub, net=constants.QtumMainnet) xpub = node._replace(xtype=xtype).to_xpub() return xpub else: raise Exception('no reply')
def get_xpub(self, bip32_path, xtype): assert xtype in self.plugin.SUPPORTED_XTYPES reply = self._get_xpub(bip32_path) if reply: xpub = reply['xpub'] # Change type of xpub to the requested type. The firmware # only ever returns the mainnet standard type, but it is agnostic # to the type when signing. if xtype != 'standard' or constants.net.TESTNET: node = BIP32Node.from_xkey(xpub, net=constants.BitcoinMainnet) xpub = node._replace(xtype=xtype).to_xpub() return xpub else: raise Exception('no reply')
def get_xpub(self, bip32_path, xtype): assert xtype in ColdcardPlugin.SUPPORTED_XTYPES print_error('[coldcard]', 'Derive xtype = %r' % xtype) xpub = self.dev.send_recv(CCProtocolPacker.get_xpub(bip32_path), timeout=5000) # TODO handle timeout? # change type of xpub to the requested type try: node = BIP32Node.from_xkey(xpub) except InvalidMasterKeyVersionBytes: raise UserFacingException(_('Invalid xpub magic. Make sure your {} device is set to the correct chain.') .format(self.device)) from None if xtype != 'standard': xpub = node._replace(xtype=xtype).to_xpub() return xpub
def on_receive(self, keyhash, message): self.print_error("signal arrived for", keyhash) for key, _hash, window in self.keys: if _hash == keyhash: break else: self.print_error("keyhash not found") return wallet = window.wallet if isinstance(wallet.keystore, keystore.Hardware_KeyStore): window.show_warning( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('However, hardware wallets do not support message decryption, ' 'which makes them not compatible with the current design of cosigner pool.' )) return elif wallet.has_keystore_encryption(): password = window.password_dialog( _('An encrypted transaction was retrieved from cosigning pool.' ) + '\n' + _('Please enter your password to decrypt it.')) if not password: return else: password = None if not window.question( _("An encrypted transaction was retrieved from cosigning pool." ) + '\n' + _("Do you want to open it now?")): return xprv = wallet.keystore.get_master_private_key(password) if not xprv: return try: privkey = BIP32Node.from_xkey(xprv).eckey message = bh2u(privkey.decrypt_message(message)) except Exception as e: traceback.print_exc(file=sys.stdout) window.show_error(_('Error decrypting message') + ':\n' + str(e)) return self.listener.clear(keyhash) tx = transaction.Transaction(message) show_transaction(tx, window, prompt_if_unsaved=True)
def on_receive(self, keyhash, message): self.print_error("signal arrived for", keyhash) for key, _hash, window in self.keys: if _hash == keyhash: break else: self.print_error("keyhash not found") return wallet = window.wallet if isinstance(wallet.keystore, keystore.Hardware_KeyStore): window.show_warning(_('An encrypted transaction was retrieved from cosigning pool.') + '\n' + _('However, hardware wallets do not support message decryption, ' 'which makes them not compatible with the current design of cosigner pool.')) return elif wallet.has_keystore_encryption(): password = window.password_dialog(_('An encrypted transaction was retrieved from cosigning pool.') + '\n' + _('Please enter your password to decrypt it.')) if not password: return else: password = None if not window.question(_("An encrypted transaction was retrieved from cosigning pool.") + '\n' + _("Do you want to open it now?")): return xprv = wallet.keystore.get_master_private_key(password) if not xprv: return try: privkey = BIP32Node.from_xkey(xprv).eckey message = bh2u(privkey.decrypt_message(message)) except Exception as e: traceback.print_exc(file=sys.stdout) window.show_error(_('Error decrypting message') + ':\n' + str(e)) return self.listener.clear(keyhash) tx = transaction.Transaction(message) show_transaction(tx, window, prompt_if_unsaved=True)
def update(self, window): wallet = window.wallet if type(wallet) != Multisig_Wallet: return if self.listener is None: self.print_error("starting listener") self.listener = Listener(self) self.listener.start() elif self.listener: self.print_error("shutting down listener") self.listener.stop() self.listener = None self.keys = [] self.cosigner_list = [] for key, keystore in wallet.keystores.items(): xpub = keystore.get_master_public_key() pubkey = BIP32Node.from_xkey(xpub).eckey.get_public_key_bytes(compressed=True) _hash = bh2u(crypto.sha256d(pubkey)) if not keystore.is_watching_only(): self.keys.append((key, _hash, window)) else: self.cosigner_list.append((window, xpub, pubkey, _hash)) if self.listener: self.listener.set_keyhashes([t[1] for t in self.keys])
def main(): seed = globals().get('seed', None) if 'wallet' not in globals(): # Not in electrum console, free to toggle network constants.set_testnet() if len(argv) > 1: seed = argv[1] if (constants.net.TESTNET): bip = {} (mnemo, tprv, gwif) = [None] * 3 if seed: (tmp, seed) = (seed, None) if '0x' == tmp[:2].lower(): tmp = tmp[2:] if set(tmp).issubset(hexdigits) and 64 == len(tmp): seed = bytes.fromhex(tmp) gwif = to_wif(seed, True, 'p2pkh').split(':')[ -1] # to_wif(seed,True,'').split(':')[-1] elif 'tprv' == tmp[:4]: tprv = tmp elif 12 == len(tmp.split()): mnemo = tmp else: gwif = tmp _, seed, _ = from_wif(tmp) bip39 = BIP39("English") if not (seed or mnemo or gwif or tprv): mnemo = bip39.generate() if mnemo: seed = bip39.to_seed(mnemo) if tprv: bip32 = BIP32Node.from_xkey(tprv) if seed: bip32 = BIP32Node.from_rootseed(seed, xtype='standard') tprv = bip32.to_xprv() desc = { '44': ['pkh'], '49': ['wpkh', 'sh'], '84': ['wpkh'], '86': ['tr'] } conf = { '44': 'legacy', '49': 'p2sh-segwit', '84': 'bech32', '86': 'bech32m' } for k in desc.keys(): imp = {'timestamp': 'now', 'range': [0, 1000], 'next_index': 1} imp = [dict(imp), dict(imp)] acct = f"{k}'/1'/0'" if gwif: key = seed wif = gwif else: key = bip32.subkey_at_private_derivation( f"m/{acct}/0/0").eckey.get_secret_bytes() wif = to_wif(key, True, 'p2pkh').split(':')[-1] bip[k] = {} bip[k]['key'] = key.hex() bip[k]['wif'] = wif change = 0 for j in ['addr', 'change']: path = f"{acct}/{change}" desc_str = f"{tprv}/{path}/*" for i in desc[k]: desc_str = f"{i}({desc_str})" desc_str = descsum_create(desc_str) imp[change]['desc'] = desc_str imp[change]['internal'] = bool(change) imp[change]['active'] = not bool(change) bip[k][j] = {} bip[k][j]['derivation'] = path bip[k][j]['desc'] = desc_str bip[k][j]['import'] = 'importdescriptors ' + dumps( imp[change]).replace('"', r'\"') change += 1 imp_txt = dumps(imp).replace('"', r'\"') if '86' == k: cmd = '' else: cmd = f'createwallet "bip{k}-berkley" false true\n' cmd += f'sethdseed true "{wif}"\n' cmd += f'createwallet "bip{k}-sqlite" false true "" false true\n' cmd += f'importdescriptors "{imp_txt}"' bip[k]['import'] = imp_txt bip[k]['commands'] = cmd print(f'\n# Your BIP39 Mnemonic: "{mnemo}"') print(f'# Your BIP32 Root Key: "{tprv}"') print( f'\n# Your legacy hdseed wif:"{bip["44"]["wif"]}", priv:"{bip["44"]["key"]}"' ) print( f'# Your p2sh-segwit hdseed wif:"{bip["49"]["wif"]}", priv:"{bip["49"]["key"]}"' ) print( f'# Your bech32 hdseed wif:"{bip["84"]["wif"]}", priv:"{bip["84"]["key"]}"\n' ) for k in desc.keys(): print(f'##################################################') print( f'# Your BIP{k} config is:\ntest.addresstype={conf[k]}\ntest.changetype={conf[k]}\n' ) print(f'# Your BIP{k} commands are:\n{bip[k]["commands"]}\n') else: print("You are not on Testnet, Exiting for safety")
def f(xprv): rootnode = BIP32Node.from_xkey(xprv) key = rootnode.subkey_at_private_derivation((0, 0)).eckey sig = key.sign_message(message, True) return base64.b64encode(sig).decode()
def convert_to_std_xpub(xpub): return BIP32Node.from_xkey(xpub) \ ._replace(xtype='standard') \ .to_xpub()
#!/usr/bin/env python3 # [rights] Copyright 2020 brianddk at github https://github.com/brianddk # [license] Apache 2.0 License https://www.apache.org/licenses/LICENSE-2.0 # [repo] github.com/brianddk/reddit/blob/master/python/elec-get-addr.py # [btc] BTC-b32: bc1qwc2203uym96u0nmq04pcgqfs9ldqz9l3mz8fpj # [tipjar] github.com/brianddk/reddit/blob/master/tipjar/tipjar.txt # [ref] reddit.com/r/Electrum/comments/jn2m0q/-/gaz1dbm/ # [req] python -m pip install electrum # [note] with open(r"..\reddit\python\script.py", 'r') as s: exec(s.read()) from electrum.constants import set_testnet from electrum.bip32 import BIP32Node from electrum.bitcoin import pubkey_to_address # Set testnet set_testnet() # You get this from Wallet info dialog xpub = 'vpub5SLqN2bLY4WeYkHyoQNaC4JuFVxDVWtx7YUjuxRwWTkLocCBy3ejp3X3Uxmefk1ae4ZCpTVYkJPUG2pAgv8K9mdxfgcGDwWRzq7YTWCCmAq' path = 'm/0/0' xpub = BIP32Node.from_xkey(xpub) for i in range(0, 20): path = f"{path[:3]}/{i}" node = xpub.subkey_at_public_derivation(path) pubk = node.eckey.get_public_key_bytes() addr = pubkey_to_address('p2wpkh', pubk.hex()) print(f"Address at path [{path}]: {addr}")