Exemplo n.º 1
0
    async def step4_serialize_inputs(self) -> None:
        writers.write_varint(self.serialized_tx, self.tx.inputs_count)

        prefix_hash = self.h_prefix.get_digest()

        for i_sign in range(self.tx.inputs_count):
            progress.advance()

            txi_sign = await helpers.request_tx_input(self.tx_req, i_sign, self.coin)

            self.wallet_path.check_input(txi_sign)
            self.multisig_fingerprint.check_input(txi_sign)

            key_sign = self.keychain.derive(txi_sign.address_n)
            key_sign_pub = key_sign.public_key()

            if txi_sign.script_type == InputScriptType.SPENDMULTISIG:
                prev_pkscript = scripts.output_script_multisig(
                    multisig.multisig_get_pubkeys(txi_sign.multisig),
                    txi_sign.multisig.m,
                )
            elif txi_sign.script_type == InputScriptType.SPENDADDRESS:
                prev_pkscript = scripts.output_script_p2pkh(
                    addresses.ecdsa_hash_pubkey(key_sign_pub, self.coin)
                )
            else:
                raise SigningError("Unsupported input script type")

            h_witness = self.create_hash_writer()
            writers.write_uint32(
                h_witness, self.tx.version | DECRED_SERIALIZE_WITNESS_SIGNING
            )
            writers.write_varint(h_witness, self.tx.inputs_count)

            for ii in range(self.tx.inputs_count):
                if ii == i_sign:
                    writers.write_bytes_prefixed(h_witness, prev_pkscript)
                else:
                    writers.write_varint(h_witness, 0)

            witness_hash = writers.get_tx_hash(
                h_witness, double=self.coin.sign_hash_double, reverse=False
            )

            h_sign = self.create_hash_writer()
            writers.write_uint32(h_sign, DECRED_SIGHASH_ALL)
            writers.write_bytes_fixed(h_sign, prefix_hash, writers.TX_HASH_SIZE)
            writers.write_bytes_fixed(h_sign, witness_hash, writers.TX_HASH_SIZE)

            sig_hash = writers.get_tx_hash(h_sign, double=self.coin.sign_hash_double)
            signature = ecdsa_sign(key_sign, sig_hash)

            # serialize input with correct signature
            gc.collect()
            script_sig = self.input_derive_script(txi_sign, key_sign_pub, signature)
            writers.write_tx_input_decred_witness(
                self.serialized_tx, txi_sign, script_sig
            )
            self.set_serialized_signature(i_sign, signature)
Exemplo n.º 2
0
    def sign_bip143_input(self, txi: TxInputType) -> Tuple[bytes, bytes]:
        self.wallet_path.check_input(txi)
        self.multisig_fingerprint.check_input(txi)

        if txi.amount > self.bip143_in:
            raise SigningError(FailureType.ProcessError,
                               "Transaction has changed during signing")
        self.bip143_in -= txi.amount

        node = self.keychain.derive(txi.address_n)
        public_key = node.public_key()
        hash143_hash = self.hash143_preimage_hash(
            txi, addresses.ecdsa_hash_pubkey(public_key, self.coin))

        signature = ecdsa_sign(node, hash143_hash)

        return public_key, signature
Exemplo n.º 3
0
    async def sign_nonsegwit_input(self, i_sign: int) -> None:
        # hash of what we are signing with this input
        h_sign = self.create_hash_writer()
        # should come out the same as h_confirmed, checked before signing the digest
        h_check = self.create_hash_writer()

        self.write_tx_header(h_sign, self.tx, witness_marker=False)
        writers.write_varint(h_sign, self.tx.inputs_count)

        for i in range(self.tx.inputs_count):
            # STAGE_REQUEST_4_INPUT in legacy
            txi = await helpers.request_tx_input(self.tx_req, i, self.coin)
            writers.write_tx_input_check(h_check, txi)
            if i == i_sign:
                self.wallet_path.check_input(txi)
                self.multisig_fingerprint.check_input(txi)
                # NOTE: wallet_path is checked in write_tx_input_check()
                node = self.keychain.derive(txi.address_n, self.coin.curve_name)
                key_sign_pub = node.public_key()
                # if multisig, do a sanity check to ensure we are signing with a key that is included in the multisig
                if txi.multisig:
                    multisig.multisig_pubkey_index(txi.multisig, key_sign_pub)

                # For the signing process the previous UTXO's scriptPubKey is included in h_sign.
                if txi.script_type == InputScriptType.SPENDMULTISIG:
                    script_pubkey = scripts.output_script_multisig(
                        multisig.multisig_get_pubkeys(txi.multisig), txi.multisig.m,
                    )
                elif txi.script_type == InputScriptType.SPENDADDRESS:
                    script_pubkey = scripts.output_script_p2pkh(
                        addresses.ecdsa_hash_pubkey(key_sign_pub, self.coin)
                    )
                else:
                    raise SigningError(
                        FailureType.ProcessError, "Unknown transaction type"
                    )
                txi_sign = txi
            else:
                script_pubkey = bytes()
            self.write_tx_input(h_sign, txi, script_pubkey)

        writers.write_varint(h_sign, self.tx.outputs_count)

        for i in range(self.tx.outputs_count):
            # STAGE_REQUEST_4_OUTPUT in legacy
            txo = await helpers.request_tx_output(self.tx_req, i, self.coin)
            script_pubkey = self.output_derive_script(txo)
            self.write_tx_output(h_check, txo, script_pubkey)
            self.write_tx_output(h_sign, txo, script_pubkey)

        writers.write_uint32(h_sign, self.tx.lock_time)
        writers.write_uint32(h_sign, self.get_hash_type())

        # check the control digests
        if self.h_confirmed.get_digest() != h_check.get_digest():
            raise SigningError(
                FailureType.ProcessError, "Transaction has changed during signing"
            )

        # compute the signature from the tx digest
        signature = ecdsa_sign(
            node, writers.get_tx_hash(h_sign, double=self.coin.sign_hash_double)
        )

        # serialize input with correct signature
        gc.collect()
        script_sig = self.input_derive_script(txi_sign, key_sign_pub, signature)
        self.write_tx_input(self.serialized_tx, txi_sign, script_sig)
        self.set_serialized_signature(i_sign, signature)