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 show_qr(self): text = bfh(str(self.tx)) text = base_encode(text, base=43) try: self.main_window.show_qrcode(text, 'Transaction', parent=self) except Exception as e: self.show_message(str(e))
def do_send(self, wallet, tx): def on_success(result): item.window.show_message('\n'.join(( _("Your transaction was sent to the cosigning pool."), _("Open your cosigner wallet to retrieve it."), ))) def on_failure(exc_info): logger.exception("", exc_info=exc_info) item.window.show_error('\n'.join(( _("Failed to send transaction to cosigning pool"), str(exc_info[1]), ))) def send_message(): server.put(item.hash, message) for item in self.items: if self.is_theirs(wallet, item, tx): raw_tx_bytes = bfh(str(tx)) public_key = ecc.ECPubkey(item.K) message = public_key.encrypt_message(raw_tx_bytes).decode( 'ascii') WaitingDialog(item.window, _('Sending transaction to cosigning pool...'), send_message, on_success, on_failure)
def f(x_pubkey): if is_xpubkey(x_pubkey): xpub, s = parse_xpubkey(x_pubkey) else: xpub = xpub_from_pubkey( 'standard', bfh(x_pubkey)) s = [] return self._make_node_path(xpub, s)
def f(x_pubkey): if x_pubkey.is_bip32_key(): xpub, path = x_pubkey.bip32_extended_key_and_path() else: xpub = BIP32PublicKey(bfh(x_pubkey), NULL_DERIVATION, Net.COIN) xpub = xpub.to_extended_key_string() path = [] node = self.ckd_public.deserialize(xpub) return self.types.HDNodePathType(node=node, address_n=path)
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)
def f(x_pubkey): if is_xpubkey(x_pubkey): xpub, s = parse_xpubkey(x_pubkey) else: xpub = xpub_from_pubkey( 'standard', bfh(x_pubkey)) s = [] node = self.ckd_public.deserialize(xpub) return self.types.HDNodePathType(node=node, address_n=s)
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 electrumsv_tx_to_txtype(self, tx): t = self.types.TransactionType() d = deserialize(tx.raw) t.version = d['version'] t.lock_time = d['lockTime'] inputs = self.tx_inputs(tx) t.inputs.extend(inputs) for vout in d['outputs']: o = t.bin_outputs.add() o.amount = vout['value'] o.script_pubkey = bfh(vout['scriptPubKey']) return t
def validate_op_return_output_and_get_data(output): if output.type != TYPE_SCRIPT: raise Exception("Unexpected output type: {}".format(output.type)) script = bfh(output.address) if not (script[0] == Ops.OP_RETURN and script[1] == len(script) - 2 and script[1] <= 75): raise Exception( _("Only OP_RETURN scripts, with one constant push, are supported.") ) if output.value != 0: raise Exception(_("Amount for OP_RETURN output must be zero.")) return script[2:]
def test_sign_transaction(self): eckey1 = PrivateKey(bfh('7e1255fddb52db1729fc3ceb21a46f95b8d9fe94cc83425e936a6c5223bb679d')) sig1 = eckey1.sign(bfh('5a548b12369a53faaa7e51b5081829474ebdd9c924b3a8230b69aa0be254cd94'), None) self.assertEqual(bfh('3045022100902a288b98392254cd23c0e9a49ac6d7920f171b8249a48e484b998f1874a2010220723d844826828f092cf400cb210c4fa0b8cd1b9d1a7f21590e78e022ff6476b9'), sig1) eckey2 = PrivateKey(bfh('c7ce8c1462c311eec24dff9e2532ac6241e50ae57e7d1833af21942136972f23')) sig2 = eckey2.sign(bfh('642a2e66332f507c92bda910158dfe46fc10afbf72218764899d3af99a043fac'), None) self.assertEqual(bfh('30440220618513f4cfc87dde798ce5febae7634c23e7b9254a1eabf486be820f6a7c2c4702204fef459393a2b931f949e63ced06888f35e286e446dc46feb24b5b5f81c6ed52'), sig2)
def _do_test_bip32(self, seed, sequence): xprv, xpub = bip32_root(bfh(seed), 'standard') self.assertEqual("m/", sequence[0:2]) path = 'm' sequence = sequence[2:] for n in sequence.split('/'): child_path = path + '/' + n if n[-1] != "'": xpub2 = bip32_public_derivation(xpub, path, child_path) xprv, xpub = bip32_private_derivation(xprv, path, child_path) if n[-1] != "'": self.assertEqual(xpub, xpub2) path = child_path return xpub, xprv
def _make_multisig(self, m, xpubs, signatures=None): if len(xpubs) == 1: return None pubkeys = [self._make_node_path(xpub, deriv) for xpub, deriv in xpubs] if signatures is None: signatures = [b''] * len(pubkeys) elif len(signatures) != len(pubkeys): raise RuntimeError('Mismatched number of signatures') else: signatures = [bfh(x)[:-1] if x else b'' for x in signatures] return MultisigRedeemScriptType(pubkeys=pubkeys, signatures=signatures, m=m)
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 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 do_send(self, wallet, tx): def on_done(window, future): try: future.result() except Exception as exc: window.on_exception(exc) else: window.show_message('\n'.join(( _("Your transaction was sent to the cosigning pool."), _("Open your cosigner wallet to retrieve it."), ))) def send_message(): server.put(item.hash, message) for item in self.items: if self.is_theirs(wallet, item, tx): raw_tx_bytes = bfh(str(tx)) public_key = PublicKey.from_bytes(item.K) message = public_key.encrypt_message_to_base64(raw_tx_bytes) WaitingDialog(item.window, _('Sending transaction to cosigning pool...'), send_message, on_done=partial(on_done, item.window))
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 sign_transaction(self, tx, password): if tx.is_complete(): return client = self.get_client() inputs = [] inputsPaths = [] chipInputs = [] redeemScripts = [] signatures = [] preparedTrustedInputs = [] changePath = "" changeAmount = None output = None outputAmount = None pin = "" self.get_client() # prompt for the PIN before displaying the dialog if necessary # Sanity check is_p2sh = any(txin.type() == 'p2sh' for txin in tx.inputs) if is_p2sh and not all(txin.type() == 'p2sh' for txin in tx.inputs): self.give_error("P2SH / regular input mixed in same transaction not supported") # Fetch inputs of the transaction to sign derivations = self.get_tx_derivations(tx) for txin in tx.inputs: for i, x_pubkey in enumerate(txin.x_pubkeys): if x_pubkey.to_hex() in derivations: signingPos = i s = derivations.get(x_pubkey.to_hex()) 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.value, txin.prev_idx, redeemScript, txin.prev_hash, signingPos, txin.sequence]) inputsPaths.append(hwAddress) # Concatenate all the tx outputs as binary txOutput = pack_list(tx.outputs, TxOutput.to_bytes) # Recognize outputs - only one output and one change is authorized if not is_p2sh: for tx_output, info in zip(tx.outputs, tx.output_info): if (info is not None) and len(tx.outputs) != 1: index, xpubs, m = info changePath = self.get_derivation()[2:] + "/{:d}/{:d}".format(*index) changeAmount = tx_output.value else: output = classify_tx_output(tx_output) outputAmount = tx_output.value self.handler.show_message(_("Confirm Transaction on your Ledger device...")) try: for utxo in inputs: sequence = int_to_hex(utxo[5], 4) chipInputs.append({'value' : utxo[0], '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.to_string(coin=Net.COIN) self.handler.finished() pin = self.handler.get_auth( outputData ) # the authenticate dialog and returns pin if not pin: raise UserWarning() 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: logger.exception("") self.give_error(e, True) except Exception as e: logger.exception("") self.give_error(e, True) finally: self.handler.finished() for txin, input, signature in zip(tx.inputs, inputs, signatures): txin.signatures[input[4]] = signature tx.raw = tx.serialize()
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 tx_inputs(self, tx, for_sig=False): inputs = [] for txin in tx.inputs(): txinputtype = self.types.TxInputType() if txin['type'] == 'coinbase': prev_hash = "\0" * 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 = self.client_class.expand_path( 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 = xpub_from_pubkey( 'standard', bfh(x_pubkey)) 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 = self.client_class.expand_path( self.xpub_path[xpub]) txinputtype.address_n.extend(xpub_n + s) break prev_hash = unhexlify(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