Esempio n. 1
0
 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
Esempio n. 2
0
 def create_output_by_address():
     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(coin=Net.COIN)
     elif isinstance(address, OP_RETURN_Output):
         txoutputtype.script_type = OutputScriptType.PAYTOOPRETURN
         txoutputtype.op_return_data = validate_op_return(tx_output)
     return txoutputtype
Esempio n. 3
0
    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
Esempio n. 4
0
    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
Esempio n. 5
0
    def sign_transaction(self, tx: Transaction, password: str,
                         tx_context: TransactionContext) -> None:
        if tx.is_complete():
            return

        assert self.handler is not None
        client = self.get_client()
        inputs: List[YInput] = []
        inputsPaths = []
        chipInputs = []
        redeemScripts = []
        signatures = []
        changePath = ""
        changeAmount = None
        output = None
        outputAmount = None
        pin = ""
        self.get_client(
        )  # prompt for the PIN before displaying the dialog if necessary

        # Fetch inputs of the transaction to sign
        foundP2SHSpend = False
        allSpendsAreP2SH = True
        for txin in tx.inputs:
            foundP2SHSpend = foundP2SHSpend or txin.type(
            ) == ScriptType.MULTISIG_P2SH
            allSpendsAreP2SH = allSpendsAreP2SH and txin.type(
            ) == ScriptType.MULTISIG_P2SH

            for i, x_pubkey in enumerate(txin.x_pubkeys):
                if self.is_signature_candidate(x_pubkey):
                    txin_xpub_idx = i
                    inputPath = "%s/%d/%d" % (self.get_derivation()[2:],
                                              *x_pubkey.bip32_path())
                    break
            else:
                self.give_error("No matching x_key for sign_transaction"
                                )  # should never happen

            inputs.append(
                YInput(txin.value, Transaction.get_preimage_script_bytes(txin),
                       txin_xpub_idx, txin.sequence))
            inputsPaths.append(inputPath)

        # Sanity check
        if foundP2SHSpend and not allSpendsAreP2SH:
            self.give_error(
                "P2SH / regular input mixed in same transaction not supported")

        # Concatenate all the tx outputs as binary
        txOutput = pack_list(tx.outputs, XTxOutput.to_bytes)

        # Recognize outputs - only one output and one change is authorized
        if not foundP2SHSpend:
            keystore_fingerprint = self.get_fingerprint()
            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 len(tx.outputs) != 1:
                    key_derivation, xpubs, m = info
                    key_subpath = compose_chain_string(key_derivation)[1:]
                    changePath = self.get_derivation()[2:] + key_subpath
                    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 i, utxo in enumerate(inputs):
                txin = tx.inputs[i]
                sequence = int_to_hex(utxo.sequence, 4)
                prevout_bytes = txin.prevout_bytes()
                value_bytes = prevout_bytes + pack_le_int64(utxo.value)
                chipInputs.append({
                    'value': value_bytes,
                    'witness': True,
                    'sequence': sequence
                })
                redeemScripts.append(utxo.script_sig)

            # 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'] = cast(ScriptTemplate,
                                             output).to_string()
                self.handler.finished()
                # the authenticate dialog and returns pin
                auth_pin = self.handler.get_auth(self, outputData)
                if not auth_pin:
                    raise UserWarning()
                pin = auth_pin
                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.txin_xpub_idx] = signature
Esempio n. 6
0
    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()