def update(features): self.features = features set_label_enabled() if features.bootloader_hash: bl_hash = bh2u(features.bootloader_hash) bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]]) else: bl_hash = "N/A" noyes = [_("No"), _("Yes")] endis = [_("Enable Passphrases"), _("Disable Passphrases")] disen = [_("Disabled"), _("Enabled")] setchange = [_("Set a PIN"), _("Change PIN")] version = "%d.%d.%d" % (features.major_version, features.minor_version, features.patch_version) device_label.setText(features.label) pin_set_label.setText(noyes[features.pin_protection]) passphrases_label.setText(disen[features.passphrase_protection]) bl_hash_label.setText(bl_hash) label_edit.setText(features.label) device_id_label.setText(features.device_id) initialized_label.setText(noyes[features.initialized]) version_label.setText(version) clear_pin_button.setVisible(features.pin_protection) clear_pin_warning.setVisible(features.pin_protection) pin_button.setText(setchange[features.pin_protection]) pin_msg.setVisible(not features.pin_protection) passphrase_button.setText(endis[features.passphrase_protection]) language_label.setText(features.language)
def on_receive(self, keyhash, message): logger.debug("signal arrived for '%s'", keyhash) window = None for key, _hash, window in self.keys: if _hash == keyhash: break else: logger.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 if wallet.has_password(): 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: k = bh2u(bitcoin.deserialize_xprv(xprv)[-1]) EC = bitcoin.EC_KEY(bfh(k)) message = bh2u(EC.decrypt_message(message)) except Exception as e: logging.exception("") window.show_message(str(e)) return self.listener.clear(keyhash) tx = transaction.Transaction(message) window.show_transaction(tx, prompt_if_unsaved=True)
def test_push_script(self): # https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#push-operators self.assertEqual(push_script(''), bh2u(bytes([Ops.OP_0]))) self.assertEqual(push_script('07'), bh2u(bytes([Ops.OP_7]))) self.assertEqual(push_script('10'), bh2u(bytes([Ops.OP_16]))) self.assertEqual(push_script('81'), bh2u(bytes([Ops.OP_1NEGATE]))) self.assertEqual(push_script('11'), '0111') self.assertEqual(push_script(75 * '42'), '4b' + 75 * '42') self.assertEqual(push_script(76 * '42'), bh2u(bytes([Ops.OP_PUSHDATA1]) + bfh('4c' + 76 * '42'))) self.assertEqual(push_script(100 * '42'), bh2u(bytes([Ops.OP_PUSHDATA1]) + bfh('64' + 100 * '42'))) self.assertEqual(push_script(255 * '42'), bh2u(bytes([Ops.OP_PUSHDATA1]) + bfh('ff' + 255 * '42'))) self.assertEqual(push_script(256 * '42'), bh2u(bytes([Ops.OP_PUSHDATA2]) + bfh('0001' + 256 * '42'))) self.assertEqual(push_script(520 * '42'), bh2u(bytes([Ops.OP_PUSHDATA2]) + bfh('0802' + 520 * '42')))
def sign_transaction(self, keystore, tx, xpub_path): self.xpub_path = xpub_path client = self.get_client(keystore) inputs = self.tx_inputs(tx, True) outputs = self.tx_outputs(keystore.get_derivation(), tx, client) signed_tx = client.sign_tx(self.get_coin_name(), inputs, outputs, lock_time=tx.locktime)[1] raw = bh2u(signed_tx) tx.update_signatures(raw)
def window_opened(self, window): wallet = window.wallet if cosigner_pool.is_enabled() and type(wallet) == Multisig_Wallet: items = [] for key, keystore in wallet.keystores.items(): xpub = keystore.get_master_public_key() K = deserialize_xpub(xpub)[-1] K_hash = bh2u(sha256d(K)) items.append( CosignerItem(window, xpub, K, K_hash, keystore.is_watching_only())) # Presumably atomic self.items.extend(items)
def on_receive(self, keyhash, message): logger.debug("signal arrived for '%s'", keyhash) for item in self.items: if item.hash == keyhash and not item.watching_only: window = item.window break else: logger.error("keyhash not found") return parent_wallet = window.parent_wallet wallet = parent_wallet.get_default_wallet() if isinstance(wallet.get_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.' )) self.listener.clear(keyhash) return if parent_wallet.has_password(): 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 self.listener.clear(keyhash) xprv = wallet.get_keystore().get_master_private_key(password) if not xprv: return privkey = bip32_key_from_string(xprv) try: message = bh2u(privkey.decrypt_message(message)) except Exception as e: logger.exception("") window.show_error(_('Error decrypting message') + ':\n' + str(e)) return tx = Transaction.from_hex(message) window.show_transaction(tx, prompt_if_unsaved=True)
def window_opened(self, window: 'ElectrumWindow'): wallet = window.parent_wallet.get_default_wallet() if cosigner_pool.is_enabled() and type(wallet) is Multisig_Wallet: items = [] for keystore in wallet.get_keystores(): xpub = keystore.get_master_public_key() pubkey = bip32_key_from_string(xpub) K = pubkey.to_bytes() K_hash = bh2u(sha256d(K)) items.append( CosignerItem(window, xpub, K, K_hash, keystore.is_watching_only())) # Presumably atomic self.items.extend(items)
def do_send(self, tx): for window, xpub, K, _hash in self.cosigner_list: if not self.cosigner_can_sign(tx, xpub): continue message = bitcoin.encrypt_message(bfh(tx.raw), bh2u(K)).decode('ascii') try: server.put(_hash, message) except Exception as e: logging.exception("") window.show_message( _("Failed to send transaction to cosigning pool.")) return window.show_message( _("Your transaction was sent to the cosigning pool.") + '\n' + _("Open your cosigner wallet to retrieve it."))
def test_compact_size(self): s = transaction._BCDataStream() values = [0, 1, 252, 253, 2**16-1, 2**16, 2**32-1, 2**32, 2**64-1] for v in values: s.write_compact_size(v) with self.assertRaises(transaction.SerializationError): s.write_compact_size(-1) self.assertEqual(bh2u(s.input), '0001fcfdfd00fdfffffe00000100feffffffffff0000000001000000ffffffffffffffffff') for v in values: self.assertEqual(s.read_compact_size(), v) with self.assertRaises(transaction.SerializationError): s.read_compact_size()
def update(self, window): wallet = window.wallet if type(wallet) != Multisig_Wallet: return if self.listener is None: logger.debug("starting listener") self.listener = Listener(self) self.listener.start() elif self.listener: logger.debug("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() K = bitcoin.deserialize_xpub(xpub)[-1] _hash = bh2u(bitcoin.Hash(K)) if not keystore_.is_watching_only(): self.keys.append((key, _hash, window)) else: self.cosigner_list.append((window, xpub, K, _hash)) if self.listener: self.listener.set_keyhashes([t[1] for t in self.keys])
def sign_transaction(self, tx, password): if tx.is_complete(): return client = self.get_client() inputs = [] inputsPaths = [] pubKeys = [] chipInputs = [] redeemScripts = [] signatures = [] preparedTrustedInputs = [] changePath = "" changeAmount = None output = None outputAmount = None p2shTransaction = False pin = "" self.get_client() # prompt for the PIN before displaying the dialog if necessary # Fetch inputs of the transaction to sign derivations = self.get_tx_derivations(tx) for txin in tx.inputs(): if txin['type'] == 'coinbase': self.give_error("Coinbase not supported") # should never happen if txin['type'] in ['p2sh']: p2shTransaction = True pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin) for i, x_pubkey in enumerate(x_pubkeys): if x_pubkey in derivations: signingPos = i s = derivations.get(x_pubkey) hwAddress = "{:s}/{:d}/{:d}".format(self.get_derivation()[2:], s[0], s[1]) break else: self.give_error("No matching x_key for sign_transaction") # should never happen redeemScript = Transaction.get_preimage_script(txin) inputs.append([txin['prev_tx'].raw, txin['prevout_n'], redeemScript, txin['prevout_hash'], signingPos, txin.get('sequence', 0xffffffff - 1)]) inputsPaths.append(hwAddress) pubKeys.append(pubkeys) # Sanity check if p2shTransaction: for txin in tx.inputs(): if txin['type'] != 'p2sh': # should never happen self.give_error( "P2SH / regular input mixed in same transaction not supported") txOutput = var_int(len(tx.outputs())) for txout in tx.outputs(): output_type, addr, amount = txout txOutput += int_to_hex(amount, 8) script = tx.pay_script(addr) txOutput += var_int(len(script)//2) txOutput += script txOutput = bfh(txOutput) # Recognize outputs - only one output and one change is authorized if not p2shTransaction: for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS info = tx.output_info.get(address) if (info is not None) and (len(tx.outputs()) != 1): index, xpubs, m = info changePath = self.get_derivation()[2:] + "/{:d}/{:d}".format(*index) changeAmount = amount else: output = address outputAmount = amount self.handler.show_message(_("Confirm Transaction on your Ledger device...")) try: # Get trusted inputs from the original transactions for utxo in inputs: sequence = int_to_hex(utxo[5], 4) txtmp = bitcoinTransaction(bfh(utxo[0])) tmp = bfh(utxo[3])[::-1] tmp += bfh(int_to_hex(utxo[1], 4)) tmp += txtmp.outputs[utxo[1]].amount chipInputs.append({'value' : tmp, 'witness' : True, 'sequence' : sequence}) redeemScripts.append(bfh(utxo[2])) # Sign all inputs inputIndex = 0 rawTx = tx.serialize() self.get_client().enableAlternate2fa(False) self.get_client().startUntrustedTransaction(True, inputIndex, chipInputs, redeemScripts[inputIndex]) outputData = self.get_client().finalizeInputFull(txOutput) outputData['outputData'] = txOutput transactionOutput = outputData['outputData'] if outputData['confirmationNeeded']: outputData['address'] = output self.handler.finished() pin = self.handler.get_auth( outputData ) # the authenticate dialog and returns pin if not pin: raise UserWarning() if pin != 'paired': self.handler.show_message(_("Confirmed. Signing Transaction...")) while inputIndex < len(inputs): singleInput = [ chipInputs[inputIndex] ] self.get_client().startUntrustedTransaction( False, 0, singleInput, redeemScripts[inputIndex]) inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex], pin, lockTime=tx.locktime, sighashType=tx.nHashType()) inputSignature[0] = 0x30 # force for 1.4.9+ signatures.append(inputSignature) inputIndex = inputIndex + 1 except UserWarning: self.handler.show_error(_('Cancelled by user')) return except BTChipException as e: if e.sw == 0x6985: # cancelled by user return else: logging.exception("") self.give_error(e, True) except BaseException as e: logging.exception("") self.give_error(e, True) finally: self.handler.finished() for i, txin in enumerate(tx.inputs()): signingPos = inputs[i][4] txin['signatures'][signingPos] = bh2u(signatures[i]) tx.raw = tx.serialize()
def test_to_seed(self): seed = mnemonic.Mnemonic.mnemonic_to_seed(mnemonic='foobar', passphrase='none') self.assertEqual(bh2u(seed), '741b72fd15effece6bfe5a26a52184f66811bd2be363190e07a42cca442b1a5b' 'b22b3ad0eb338197287e6d314866c7fba863ac65d3f156087a5052ebc7157fce')