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)
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
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)