def tx_inputs(self, tx: Transaction, xpub_path: Dict[str, str]) -> List[types.TxInputType]: inputs = [] for txin in tx.inputs: txinputtype = types.TxInputType() x_pubkeys = txin.x_pubkeys if len(x_pubkeys) == 1: x_pubkey = x_pubkeys[0] xpub, path = x_pubkey.bip32_extended_key_and_path() xpub_n = bip32_decompose_chain_string(xpub_path[xpub]) txinputtype.address_n.extend(xpub_n) txinputtype.address_n.extend(path) txinputtype.script_type = types.SPENDADDRESS else: def f(x_pubkey): if x_pubkey.is_bip32_key(): xpub, path = x_pubkey.bip32_extended_key_and_path() else: xpub = BIP32PublicKey(x_pubkey.to_public_key(), NULL_DERIVATION, Net.COIN) xpub = xpub.to_extended_key_string() path = [] node = keepkeylib.ckd_public.deserialize(xpub) return types.HDNodePathType(node=node, address_n=path) pubkeys = [f(x) for x in x_pubkeys] multisig = types.MultisigRedeemScriptType( pubkeys=pubkeys, signatures=txin.stripped_signatures_with_blanks(), m=txin.threshold, ) script_type = types.SPENDMULTISIG txinputtype = types.TxInputType(script_type=script_type, multisig=multisig) # find which key is mine for x_pubkey in x_pubkeys: if x_pubkey.is_bip32_key(): xpub, path = x_pubkey.bip32_extended_key_and_path() if xpub in xpub_path: xpub_n = tuple( bip32_decompose_chain_string(xpub_path[xpub])) txinputtype.address_n.extend(xpub_n) txinputtype.address_n.extend(path) break txinputtype.prev_hash = bytes(reversed(txin.prev_hash)) txinputtype.prev_index = txin.prev_idx txinputtype.sequence = txin.sequence txinputtype.amount = txin.value inputs.append(txinputtype) return inputs
def tx_outputs(self, derivation, tx): outputs = [] has_change = False for tx_output, info in zip(tx.outputs, tx.output_info): if info is not None and not has_change: has_change = True # no more than one change address index, xpubs, m = info if len(xpubs) == 1: script_type = self.types.PAYTOADDRESS address_n = bip32_decompose_chain_string(derivation + "/%d/%d" % index) txoutputtype = self.types.TxOutputType( amount=tx_output.value, script_type=script_type, address_n=address_n, ) else: script_type = self.types.PAYTOMULTISIG address_n = bip32_decompose_chain_string("/%d/%d" % index) nodes = [ self.ckd_public.deserialize(xpub) for xpub in xpubs ] pubkeys = [ self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes ] multisig = self.types.MultisigRedeemScriptType( pubkeys=pubkeys, signatures=[b''] * len(pubkeys), m=m) txoutputtype = self.types.TxOutputType( multisig=multisig, amount=tx_output.value, address_n=bip32_decompose_chain_string(derivation + "/%d/%d" % index), script_type=script_type) else: txoutputtype = self.types.TxOutputType() txoutputtype.amount = tx_output.value address = classify_tx_output(tx_output) if isinstance(address, Address): txoutputtype.script_type = self.types.PAYTOADDRESS txoutputtype.address = address.to_string(coin=Net.COIN) elif isinstance(address, OP_RETURN_Output): txoutputtype.script_type = self.types.PAYTOOPRETURN txoutputtype.op_return_data = bytes( tx_output.script_pubkey)[2:] outputs.append(txoutputtype) return outputs
def create_output_by_derivation(): deriv = bip32_decompose_chain_string("m/%d/%d" % index) multisig = self._make_multisig(m, [(xpub, deriv) for xpub in xpubs]) if multisig is None: script_type = OutputScriptType.PAYTOADDRESS else: script_type = OutputScriptType.PAYTOMULTISIG return TxOutputType( multisig=multisig, amount=tx_output.value, address_n=bip32_decompose_chain_string(derivation + "/%d/%d" % index), script_type=script_type )
def tx_outputs(self, derivation, tx): outputs = [] has_change = False for _type, address, amount in tx.outputs(): info = tx.output_info.get(address) if info is not None and not has_change: has_change = True # no more than one change address index, xpubs, m = info if len(xpubs) == 1: script_type = self.types.PAYTOADDRESS address_n = bip32_decompose_chain_string(derivation + "/%d/%d" % index) txoutputtype = self.types.TxOutputType( amount=amount, script_type=script_type, address_n=address_n, ) else: script_type = self.types.PAYTOMULTISIG address_n = bip32_decompose_chain_string("/%d/%d" % index) nodes = [ self.ckd_public.deserialize(xpub) for xpub in xpubs ] pubkeys = [ self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes ] multisig = self.types.MultisigRedeemScriptType( pubkeys=pubkeys, signatures=[b''] * len(pubkeys), m=m) txoutputtype = self.types.TxOutputType( multisig=multisig, amount=amount, address_n=bip32_decompose_chain_string(derivation + "/%d/%d" % index), script_type=script_type) else: txoutputtype = self.types.TxOutputType() txoutputtype.amount = amount if _type == TYPE_SCRIPT: txoutputtype.script_type = self.types.PAYTOOPRETURN txoutputtype.op_return_data = address.to_script()[2:] elif _type == TYPE_ADDRESS: txoutputtype.script_type = self.types.PAYTOADDRESS txoutputtype.address = address.to_string() outputs.append(txoutputtype) return outputs
def sign_message(self, sequence, message, password): client = self.get_client() address_path = self.get_derivation() + "/%d/%d" % sequence address_n = bip32_decompose_chain_string(address_path) msg_sig = client.sign_message(self.plugin.get_coin_name(client), address_n, message) return msg_sig.signature
def tx_inputs(self, tx, xpub_path): inputs = [] for txin in tx.inputs: txinputtype = TxInputType() txinputtype.prev_hash = bytes(reversed(txin.prev_hash)) txinputtype.prev_index = txin.prev_idx txinputtype.sequence = txin.sequence txinputtype.amount = txin.value xpubs = [ x_pubkey.bip32_extended_key_and_path() for x_pubkey in txin.x_pubkeys ] txinputtype.multisig = self._make_multisig( txin.threshold, xpubs, txin.stripped_signatures_with_blanks()) txinputtype.script_type = self.get_trezor_input_script_type( txinputtype.multisig is not None) # find which key is mine for xpub, path in xpubs: if xpub in xpub_path: xpub_n = bip32_decompose_chain_string(xpub_path[xpub]) txinputtype.address_n = xpub_n + path break # if txin.script_sig: # txinputtype.script_sig = bytes(txin.script_sig) inputs.append(txinputtype) return inputs
def show_address(self, derivation_text: str, script_type, multisig=None): coin_name = self.plugin.get_coin_name() address_n = bip32_decompose_chain_string(derivation_text) with self.run_flow(): return trezorlib.btc.get_address(self.client, coin_name, address_n, show_display=True, script_type=script_type, multisig=multisig)
def show_address(self, wallet, address): keystore = wallet.get_keystore() client = self.get_client(keystore) change, index = wallet.get_address_index(address) derivation = keystore.derivation address_path = "%s/%d/%d"%(derivation, change, index) address_n = bip32_decompose_chain_string(address_path) script_type = self.types.SPENDADDRESS client.get_address(Net.KEEPKEY_DISPLAY_COIN_NAME, address_n, True, script_type=script_type)
def show_key(self, account: AbstractAccount, keyinstance_id: int) -> None: keystore = cast(KeepKey_KeyStore, account.get_keystore()) client = self.get_client(keystore) derivation_path = account.get_derivation_path(keyinstance_id) assert derivation_path is not None subpath = '/'.join(str(x) for x in derivation_path) address_path = f"{keystore.derivation}/{subpath}" address_n = bip32_decompose_chain_string(address_path) script_type = self.types.SPENDADDRESS client.get_address(Net.KEEPKEY_DISPLAY_COIN_NAME, address_n, True, script_type=script_type)
def get_master_public_key(self, bip32_path): address_n = bip32_decompose_chain_string(bip32_path) creating = False node = self.get_public_node(address_n, creating).node self.used() derivation = BIP32Derivation(chain_code=node.chain_code, depth=node.depth, parent_fingerprint=pack_be_uint32( node.fingerprint), n=node.child_num) return BIP32PublicKey(PublicKey.from_bytes(node.public_key), derivation, Net.COIN)
def get_master_public_key(self, bip32_path, creating=False): address_n = bip32_decompose_chain_string(bip32_path) with self.run_flow(creating_wallet=creating): node = trezorlib.btc.get_public_node(self.client, address_n).node self.used() derivation = BIP32Derivation(chain_code=node.chain_code, depth=node.depth, parent_fingerprint=pack_be_uint32( node.fingerprint), n=node.child_num) return BIP32PublicKey(PublicKey.from_bytes(node.public_key), derivation, Net.COIN)
def tx_outputs(self, keystore: KeepKey_KeyStore, derivation: str, tx: Transaction): has_change = False account_derivation = tuple(bip32_decompose_chain_string(derivation)) keystore_fingerprint = keystore.get_fingerprint() outputs = [] assert tx.output_info is not None for tx_output, output_metadatas in zip(tx.outputs, tx.output_info): info = output_metadatas.get(keystore_fingerprint) if info is not None and not has_change: has_change = True # no more than one change address key_derivation, xpubs, m = info if len(xpubs) == 1: script_type = types.PAYTOADDRESS txoutputtype = types.TxOutputType( amount=tx_output.value, script_type=script_type, address_n=account_derivation + key_derivation, ) else: script_type = types.PAYTOMULTISIG nodes = [ keepkeylib.ckd_public.deserialize(xpub) for xpub in xpubs ] pubkeys = [ types.HDNodePathType(node=node, address_n=key_derivation) for node in nodes ] multisig = types.MultisigRedeemScriptType( pubkeys=pubkeys, signatures=[b''] * len(pubkeys), m=m) txoutputtype = types.TxOutputType( multisig=multisig, amount=tx_output.value, address_n=account_derivation + key_derivation, script_type=script_type) else: txoutputtype = types.TxOutputType() txoutputtype.amount = tx_output.value address = classify_tx_output(tx_output) if isinstance(address, Address): txoutputtype.script_type = types.PAYTOADDRESS txoutputtype.address = address.to_string() outputs.append(txoutputtype) return outputs
def get_master_public_key(self, bip32_path: str, creating: bool = False) -> BIP32PublicKey: address_n = bip32_decompose_chain_string(bip32_path) # This will be cleared by the wrapper around `get_public_node`. self.creating_wallet = creating node = self.get_public_node(address_n).node self.used() derivation = BIP32Derivation(chain_code=node.chain_code, depth=node.depth, parent_fingerprint=pack_be_uint32( node.fingerprint), n=node.child_num) return BIP32PublicKey(PublicKey.from_bytes(node.public_key), derivation, Net.COIN)
def tx_inputs(self, tx, xpub_path, for_sig=False): inputs = [] for txin in tx.inputs(): txinputtype = TxInputType() if txin['type'] == 'coinbase': prev_hash = b"\x00" * 32 prev_index = 0xffffffff # signed int -1 else: if for_sig: x_pubkeys = txin['x_pubkeys'] xpubs = [parse_xpubkey(x) for x in x_pubkeys] multisig = self._make_multisig(txin.get('num_sig'), xpubs, txin.get('signatures')) script_type = self.get_trezor_input_script_type( multisig is not None) txinputtype = TxInputType(script_type=script_type, multisig=multisig) # find which key is mine for xpub, deriv in xpubs: if xpub in xpub_path: xpub_n = bip32_decompose_chain_string( xpub_path[xpub]) txinputtype.address_n = xpub_n + deriv break prev_hash = bfh(txin['prevout_hash']) prev_index = txin['prevout_n'] if 'value' in txin: txinputtype.amount = txin['value'] txinputtype.prev_hash = prev_hash txinputtype.prev_index = prev_index if 'scriptSig' in txin: script_sig = bfh(txin['scriptSig']) txinputtype.script_sig = script_sig txinputtype.sequence = txin.get('sequence', 0xffffffff - 1) inputs.append(txinputtype) return inputs
def tx_outputs(self, keystore: TrezorKeyStore, derivation: str, tx: Transaction) \ -> List[TxOutputType]: account_derivation: Sequence[int] = tuple( bip32_decompose_chain_string(derivation)) keystore_fingerprint = keystore.get_fingerprint() def create_output_by_derivation(key_derivation: Sequence[int], xpubs, m: int) -> TxOutputType: multisig = self._make_multisig(m, [(xpub, key_derivation) for xpub in xpubs]) if multisig is None: script_type = OutputScriptType.PAYTOADDRESS else: script_type = OutputScriptType.PAYTOMULTISIG return TxOutputType(multisig=multisig, amount=tx_output.value, address_n=(*account_derivation, *key_derivation), script_type=script_type) def create_output_by_address(tx_output: XTxOutput) -> TxOutputType: txoutputtype = TxOutputType() txoutputtype.amount = tx_output.value address = classify_tx_output(tx_output) if isinstance(address, Address): txoutputtype.script_type = OutputScriptType.PAYTOADDRESS txoutputtype.address = address.to_string() return txoutputtype outputs = [] for i, tx_output in enumerate(tx.outputs): if tx.output_info is not None and keystore_fingerprint in tx.output_info[ i]: output_info = tx.output_info[i][keystore_fingerprint] txoutputtype = create_output_by_derivation(*output_info) else: txoutputtype = create_output_by_address(tx_output) outputs.append(txoutputtype) return outputs
def parse_label_export_json(klass, account: AbstractAccount, text: str) -> LabelImportResult: updates: Dict[str, Any] = json.loads(text) results = LabelImportResult(LabelImportFormat.ACCOUNT) for tx_id, label_text in updates.get("transactions", []): if len(tx_id) == 64: # length of the transaction id (hex of hash) try: tx_hash = hex_str_to_hash(tx_id) except (TypeError, ValueError): pass else: results.transaction_labels[tx_hash] = label_text continue results.unknown_labels[tx_id] = label_text keydata: Optional[Dict[str, Any]] = updates.get("keys") if keydata is not None: account_fingerprint = account.get_fingerprint().hex() if isinstance(keydata.get("account_fingerprint"), str): results.account_fingerprint = keydata["account_fingerprint"] derivations = klass._get_derivations(account) for derivation_path_text, label_text in keydata["entries"]: try: derivation_path = tuple( bip32_decompose_chain_string(derivation_path_text)) except (TypeError, ValueError): pass else: # We never import key descriptions if the account does not match. if account_fingerprint == results.account_fingerprint: keyinstance_id = derivations.get(derivation_path) if keyinstance_id is not None: results.key_labels[keyinstance_id] = label_text continue results.unknown_labels[derivation_path_text] = label_text return results
def tx_inputs(self, tx: Transaction, xpub_path: Optional[Dict[str, str]] = None, is_prev_tx: bool = False) -> List[TxInputType]: inputs = [] for txin in tx.inputs: txinputtype = TxInputType() # Trezor tx hashes are same byte order as the reversed hex tx id. txinputtype.prev_hash = bytes(reversed(txin.prev_hash)) txinputtype.prev_index = txin.prev_idx txinputtype.sequence = txin.sequence txinputtype.amount = txin.value if txin.script_sig: txinputtype.script_sig = bytes(txin.script_sig) if not is_prev_tx: assert xpub_path is not None, "no xpubs provided for hw signing operation" xpubs = [ x_pubkey.bip32_extended_key_and_path() for x_pubkey in txin.x_pubkeys ] txinputtype.multisig = self._make_multisig( txin.threshold, xpubs, txin.stripped_signatures_with_blanks()) txinputtype.script_type = self.get_trezor_input_script_type( txinputtype.multisig is not None) # find which key is mine for xpub, path in xpubs: if xpub in xpub_path: xpub_n = tuple( bip32_decompose_chain_string(xpub_path[xpub])) # Sequences cannot be added according to mypy, annoying.. txinputtype.address_n = xpub_n + path # type: ignore break inputs.append(txinputtype) return inputs
def test_bip32_chain_string_composition(path: str) -> None: assert compose_chain_string(bip32_decompose_chain_string(path)) == path
def add_xprv_from_seed(self, bip32_seed, derivation) -> None: xprv = BIP32PrivateKey.from_seed(bip32_seed, Net.COIN) for n in bip32_decompose_chain_string(derivation): xprv = xprv.child_safe(n) self.add_xprv(xprv)
def instantiate_keystore_from_text(text_type: KeystoreTextType, text_match: Union[str, List[str]], password: Optional[str], derivation_text: Optional[str] = None, passphrase: Optional[str] = None, watch_only: bool = False) -> KeyStore: derivation_type: Optional[DerivationType] = None data: Dict[str, Any] = {} if text_type == KeystoreTextType.EXTENDED_PUBLIC_KEY: derivation_type = DerivationType.BIP32 assert isinstance(text_match, str) assert passphrase is None # `watch_only` is ignored. data['xpub'] = text_match elif text_type == KeystoreTextType.EXTENDED_PRIVATE_KEY: derivation_type = DerivationType.BIP32 assert isinstance(text_match, str) assert passphrase is None if not watch_only: assert password is not None data['xprv'] = pw_encode(text_match, password) private_key = bip32_key_from_string(text_match) data['xpub'] = private_key.public_key.to_extended_key_string() elif text_type == KeystoreTextType.PRIVATE_KEYS: derivation_type = DerivationType.IMPORTED # watch_only? elif text_type == KeystoreTextType.ADDRESSES: derivation_type = DerivationType.IMPORTED # All address types have to be the same. pass elif text_type == KeystoreTextType.BIP39_SEED_WORDS: derivation_type = DerivationType.BIP32 if derivation_text is None: derivation_text = bip44_derivation_cointype(0, 0) assert isinstance(text_match, str) bip32_seed = bip39_to_seed(text_match, passphrase) xprv = BIP32PrivateKey.from_seed(bip32_seed, Net.COIN) for n in bip32_decompose_chain_string(derivation_text): xprv = xprv.child_safe(n) if not watch_only: assert password is not None data['xprv'] = pw_encode(xprv.to_extended_key_string(), password) data['seed'] = pw_encode(text_match, password) if passphrase is not None: data['passphrase'] = pw_encode(passphrase, password) data['derivation'] = derivation_text data['xpub'] = xprv.public_key.to_extended_key_string() elif text_type == KeystoreTextType.ELECTRUM_SEED_WORDS: derivation_type = DerivationType.BIP32 assert isinstance(text_match, str) bip32_seed = Mnemonic.mnemonic_to_seed(text_match, passphrase or '') derivation_text = "m" xprv = BIP32PrivateKey.from_seed(bip32_seed, Net.COIN) for n in bip32_decompose_chain_string(derivation_text): xprv = private_key.child_safe(n) if not watch_only: assert password is not None data['xprv'] = pw_encode(xprv.to_extended_key_string(), password) data['seed'] = pw_encode(text_match, password) if passphrase is not None: data['passphrase'] = pw_encode(passphrase, password) data['derivation'] = derivation_text data['xpub'] = xprv.public_key.to_extended_key_string() elif text_type == KeystoreTextType.ELECTRUM_OLD_SEED_WORDS: derivation_type = DerivationType.ELECTRUM_OLD assert isinstance(text_match, str) assert passphrase is None # `watch_only` is ignored. hex_seed = Old_KeyStore._seed_to_hex(text_match) assert password is not None data['seed'] = pw_encode(hex_seed, password) data['mpk'] = Old_KeyStore._mpk_from_hex_seed(hex_seed) else: raise NotImplementedError("Unsupported text match type", text_type) return instantiate_keystore(derivation_type, data)
def tx_inputs(self, tx, for_sig=False): inputs = [] for txin in tx.inputs(): txinputtype = self.types.TxInputType() if txin['type'] == 'coinbase': prev_hash = bytes(32) prev_index = 0xffffffff # signed int -1 else: if for_sig: x_pubkeys = txin['x_pubkeys'] if len(x_pubkeys) == 1: x_pubkey = x_pubkeys[0] xpub, s = parse_xpubkey(x_pubkey) xpub_n = bip32_decompose_chain_string( self.xpub_path[xpub]) txinputtype.address_n.extend(xpub_n + s) txinputtype.script_type = self.types.SPENDADDRESS else: def f(x_pubkey): if is_xpubkey(x_pubkey): xpub, s = parse_xpubkey(x_pubkey) else: xpub = BIP32PublicKey(bfh(x_pubkey), NULL_DERIVATION, Net.COIN) xpub = xpub.to_extended_key_string() s = [] node = self.ckd_public.deserialize(xpub) return self.types.HDNodePathType(node=node, address_n=s) pubkeys = [f(x) for x in x_pubkeys] multisig = self.types.MultisigRedeemScriptType( pubkeys=pubkeys, signatures=[ bfh(x)[:-1] if x else b'' for x in txin.get('signatures') ], m=txin.get('num_sig'), ) script_type = self.types.SPENDMULTISIG txinputtype = self.types.TxInputType( script_type=script_type, multisig=multisig) # find which key is mine for x_pubkey in x_pubkeys: if is_xpubkey(x_pubkey): xpub, s = parse_xpubkey(x_pubkey) if xpub in self.xpub_path: xpub_n = bip32_decompose_chain_string( self.xpub_path[xpub]) txinputtype.address_n.extend(xpub_n + s) break prev_hash = bytes.fromhex(txin['prevout_hash']) prev_index = txin['prevout_n'] if 'value' in txin: txinputtype.amount = txin['value'] txinputtype.prev_hash = prev_hash txinputtype.prev_index = prev_index if 'scriptSig' in txin: script_sig = bfh(txin['scriptSig']) txinputtype.script_sig = script_sig txinputtype.sequence = txin.get('sequence', 0xffffffff - 1) inputs.append(txinputtype) return inputs
def sign_message(self, address_str, message): coin_name = self.plugin.get_coin_name() address_n = bip32_decompose_chain_string(address_str) with self.run_flow(): return trezorlib.btc.sign_message(self.client, coin_name, address_n, message)