def get_address(script_type: InputScriptType, coin: CoinInfo, node, multisig=None) -> str: if (script_type == InputScriptType.SPENDADDRESS or script_type == InputScriptType.SPENDMULTISIG): if multisig: # p2sh multisig pubkey = node.public_key() index = multisig_pubkey_index(multisig, pubkey) if index is None: raise AddressError(FailureType.ProcessError, "Public key not found") if coin.address_type_p2sh is None: raise AddressError(FailureType.ProcessError, "Multisig not enabled on this coin") pubkeys = multisig_get_pubkeys(multisig) address = address_multisig_p2sh(pubkeys, multisig.m, coin) if coin.cashaddr_prefix is not None: address = address_to_cashaddr(address, coin) return address if script_type == InputScriptType.SPENDMULTISIG: raise AddressError(FailureType.ProcessError, "Multisig details required") # p2pkh address = node.address(coin.address_type) if coin.cashaddr_prefix is not None: address = address_to_cashaddr(address, coin) return address elif script_type == InputScriptType.SPENDWITNESS: # native p2wpkh or native p2wsh if not coin.segwit or not coin.bech32_prefix: raise AddressError(FailureType.ProcessError, "Segwit not enabled on this coin") # native p2wsh multisig if multisig is not None: pubkeys = multisig_get_pubkeys(multisig) return address_multisig_p2wsh(pubkeys, multisig.m, coin.bech32_prefix) # native p2wpkh return address_p2wpkh(node.public_key(), coin) elif (script_type == InputScriptType.SPENDP2SHWITNESS ): # p2wpkh or p2wsh nested in p2sh if not coin.segwit or coin.address_type_p2sh is None: raise AddressError(FailureType.ProcessError, "Segwit not enabled on this coin") # p2wsh multisig nested in p2sh if multisig is not None: pubkeys = multisig_get_pubkeys(multisig) return address_multisig_p2wsh_in_p2sh(pubkeys, multisig.m, coin) # p2wpkh nested in p2sh return address_p2wpkh_in_p2sh(node.public_key(), coin) else: raise AddressError(FailureType.ProcessError, "Invalid script type")
def input_script_multisig( multisig: MultisigRedeemScriptType, signature: bytes, signature_index: int, sighash: int, ): signatures = multisig.signatures # other signatures if len(signatures[signature_index]) > 0: raise ScriptsError("Invalid multisig parameters") signatures[signature_index] = signature # our signature w = bytearray() # Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which # consumes one additional item on the stack: # https://bitcoin.org/en/developer-guide#standard-transactions w.append(0x00) for s in signatures: if len(s): append_signature(w, s, sighash) # redeem script pubkeys = multisig_get_pubkeys(multisig) redeem_script = output_script_multisig(pubkeys, multisig.m) write_op_push(w, len(redeem_script)) write_bytes(w, redeem_script) return w
def witness_p2wsh( multisig: MultisigRedeemScriptType, signature: bytes, signature_index: int, sighash: int, ): signatures = multisig.signatures # other signatures if len(signatures[signature_index]) > 0: raise ScriptsError("Invalid multisig parameters") signatures[signature_index] = signature # our signature # filter empty signatures = [s for s in multisig.signatures if len(s) > 0] # witness program + signatures + redeem script num_of_witness_items = 1 + len(signatures) + 1 w = bytearray() write_varint(w, num_of_witness_items) write_varint(w, 0) # version 0 witness program for s in signatures: append_signature(w, s, sighash) # size of the witness included # redeem script pubkeys = multisig_get_pubkeys(multisig) redeem_script = output_script_multisig(pubkeys, multisig.m) write_varint(w, len(redeem_script)) write_bytes(w, redeem_script) return w
def input_derive_script( txi: TxInputType, coin: CoinInfo, hash_type: int, pubkey: bytes, signature: Optional[bytes], ) -> bytes: if txi.script_type == InputScriptType.SPENDADDRESS: # p2pkh or p2sh return input_script_p2pkh_or_p2sh(pubkey, signature, hash_type) if txi.script_type == InputScriptType.SPENDP2SHWITNESS: # p2wpkh or p2wsh using p2sh if txi.multisig: # p2wsh in p2sh pubkeys = multisig_get_pubkeys(txi.multisig) witness_script_hasher = utils.HashWriter(sha256()) write_output_script_multisig(witness_script_hasher, pubkeys, txi.multisig.m) witness_script_hash = witness_script_hasher.get_digest() return input_script_p2wsh_in_p2sh(witness_script_hash) # p2wpkh in p2sh return input_script_p2wpkh_in_p2sh(addresses.ecdsa_hash_pubkey(pubkey, coin)) elif txi.script_type == InputScriptType.SPENDWITNESS: # native p2wpkh or p2wsh return input_script_native_p2wpkh_or_p2wsh() elif txi.script_type == InputScriptType.SPENDMULTISIG: # p2sh multisig signature_index = multisig_pubkey_index(txi.multisig, pubkey) return input_script_multisig( txi.multisig, signature, signature_index, hash_type, coin ) else: raise ScriptsError(FailureType.ProcessError, "Invalid script type")
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 derive_script_code(txi: TxInputType, pubkeyhash: bytes) -> bytearray: if txi.multisig: return output_script_multisig(multisig_get_pubkeys(txi.multisig), txi.multisig.m) p2pkh = txi.script_type == InputScriptType.SPENDADDRESS if p2pkh: return output_script_p2pkh(pubkeyhash) else: raise ZcashError(FailureType.DataError, "Unknown input script type for zip143 script code")
def derive_script_code(self, txi: TxInputType, pubkeyhash: bytes) -> bytearray: if txi.multisig: return output_script_multisig(multisig_get_pubkeys(txi.multisig), txi.multisig.m) p2pkh = (txi.script_type == InputScriptType.SPENDWITNESS or txi.script_type == InputScriptType.SPENDP2SHWITNESS or txi.script_type == InputScriptType.SPENDADDRESS) if p2pkh: # for p2wpkh in p2sh or native p2wpkh # the scriptCode is a classic p2pkh return output_script_p2pkh(pubkeyhash) else: raise Bip143Error(FailureType.DataError, 'Unknown input script type for bip143 script code')
def witness_p2wsh( multisig: MultisigRedeemScriptType, signature: bytes, signature_index: int, sighash: int, ) -> bytearray: # get other signatures, stretch with None to the number of the pubkeys signatures = multisig.signatures + [None] * ( multisig_get_pubkey_count(multisig) - len(multisig.signatures) ) # fill in our signature if signatures[signature_index]: raise ScriptsError("Invalid multisig parameters") signatures[signature_index] = signature # filter empty signatures = [s for s in signatures if s] # witness program + signatures + redeem script num_of_witness_items = 1 + len(signatures) + 1 # length of the redeem script pubkeys = multisig_get_pubkeys(multisig) redeem_script_length = output_script_multisig_length(pubkeys, multisig.m) # length of the result total_length = 1 + 1 # number of items, OP_FALSE for s in signatures: total_length += 1 + len(s) + 1 # length, signature, sighash total_length += 1 + redeem_script_length # length, script w = empty_bytearray(total_length) write_varint(w, num_of_witness_items) # Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which # consumes one additional item on the stack: # https://bitcoin.org/en/developer-guide#standard-transactions write_varint(w, 0) for s in signatures: append_signature(w, s, sighash) # size of the witness included # redeem script write_varint(w, redeem_script_length) write_output_script_multisig(w, pubkeys, multisig.m) return w
def witness_p2wsh( multisig: MultisigRedeemScriptType, signature: bytes, signature_index: int, sighash: int, ): # get other signatures, stretch with None to the number of the pubkeys signatures = multisig.signatures + [None] * ( multisig_get_pubkey_count(multisig) - len(multisig.signatures) ) # fill in our signature if signatures[signature_index]: raise ScriptsError("Invalid multisig parameters") signatures[signature_index] = signature # filter empty signatures = [s for s in signatures if s] # witness program + signatures + redeem script num_of_witness_items = 1 + len(signatures) + 1 # length of the redeem script pubkeys = multisig_get_pubkeys(multisig) redeem_script_length = output_script_multisig_length(pubkeys, multisig.m) # length of the result total_length = 1 + 1 # number of items, version for s in signatures: total_length += 1 + len(s) + 1 # length, signature, sighash total_length += 1 + redeem_script_length # length, script w = empty_bytearray(total_length) write_varint(w, num_of_witness_items) write_varint(w, 0) # version 0 witness program for s in signatures: append_signature(w, s, sighash) # size of the witness included # redeem script write_varint(w, redeem_script_length) output_script_multisig(pubkeys, multisig.m, w) return w
def input_script_multisig( multisig: MultisigRedeemScriptType, signature: bytes, signature_index: int, sighash: int, coin: CoinInfo, ) -> bytearray: signatures = multisig.signatures # other signatures if len(signatures[signature_index]) > 0: raise ScriptsError("Invalid multisig parameters") signatures[signature_index] = signature # our signature # length of the redeem script pubkeys = multisig_get_pubkeys(multisig) redeem_script_length = output_script_multisig_length(pubkeys, multisig.m) # length of the result total_length = 0 if utils.BITCOIN_ONLY or not coin.decred: total_length += 1 # OP_FALSE for s in signatures: total_length += 1 + len(s) + 1 # length, signature, sighash total_length += 1 + redeem_script_length # length, script w = empty_bytearray(total_length) if utils.BITCOIN_ONLY or not coin.decred: # Starts with OP_FALSE because of an old OP_CHECKMULTISIG bug, which # consumes one additional item on the stack: # https://bitcoin.org/en/developer-guide#standard-transactions w.append(0x00) for s in signatures: if len(s): append_signature(w, s, sighash) # redeem script write_op_push(w, redeem_script_length) write_output_script_multisig(w, pubkeys, multisig.m) return w
def input_derive_script(coin: coininfo.CoinInfo, i: TxInputType, pubkey: bytes, signature: bytes = None) -> bytes: if i.script_type == InputScriptType.SPENDADDRESS: # p2pkh or p2sh return scripts.input_script_p2pkh_or_p2sh(pubkey, signature, get_hash_type(coin)) if i.script_type == InputScriptType.SPENDP2SHWITNESS: # p2wpkh or p2wsh using p2sh if i.multisig: # p2wsh in p2sh pubkeys = multisig.multisig_get_pubkeys(i.multisig) witness_script_hasher = utils.HashWriter(sha256()) scripts.output_script_multisig(pubkeys, i.multisig.m, witness_script_hasher) witness_script_hash = witness_script_hasher.get_digest() return scripts.input_script_p2wsh_in_p2sh(witness_script_hash) # p2wpkh in p2sh return scripts.input_script_p2wpkh_in_p2sh( addresses.ecdsa_hash_pubkey(pubkey, coin)) elif i.script_type == InputScriptType.SPENDWITNESS: # native p2wpkh or p2wsh return scripts.input_script_native_p2wpkh_or_p2wsh() elif i.script_type == InputScriptType.SPENDMULTISIG: # p2sh multisig signature_index = multisig.multisig_pubkey_index(i.multisig, pubkey) return scripts.input_script_multisig(i.multisig, signature, signature_index, get_hash_type(coin), coin) else: raise SigningError(FailureType.ProcessError, "Invalid script type")
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)
async def sign_tx(tx: SignTx, keychain: seed.Keychain): tx = helpers.sanitize_sign_tx(tx) progress.init(tx.inputs_count, tx.outputs_count) # Phase 1 h_first, hash143, segwit, authorized_in, wallet_path = await check_tx_fee( tx, keychain) # Phase 2 # - sign inputs # - check that nothing changed coin = coins.by_name(tx.coin_name) tx_ser = TxRequestSerializedType() txo_bin = TxOutputBinType() tx_req = TxRequest() tx_req.details = TxRequestDetailsType() tx_req.serialized = None if coin.decred: prefix_hash = hash143.prefix_hash() for i_sign in range(tx.inputs_count): progress.advance() txi_sign = None key_sign = None key_sign_pub = None if segwit[i_sign]: # STAGE_REQUEST_SEGWIT_INPUT txi_sign = await helpers.request_tx_input(tx_req, i_sign) if not input_is_segwit(txi_sign): raise SigningError(FailureType.ProcessError, "Transaction has changed during signing") input_check_wallet_path(txi_sign, wallet_path) key_sign = keychain.derive(txi_sign.address_n, coin.curve_name) key_sign_pub = key_sign.public_key() txi_sign.script_sig = input_derive_script(coin, txi_sign, key_sign_pub) w_txi = writers.empty_bytearray(7 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4) if i_sign == 0: # serializing first input => prepend headers writers.write_bytes(w_txi, get_tx_header(coin, tx, True)) writers.write_tx_input(w_txi, txi_sign) tx_ser.serialized_tx = w_txi tx_req.serialized = tx_ser elif coin.force_bip143 or tx.overwintered: # STAGE_REQUEST_SEGWIT_INPUT txi_sign = await helpers.request_tx_input(tx_req, i_sign) input_check_wallet_path(txi_sign, wallet_path) is_bip143 = (txi_sign.script_type == InputScriptType.SPENDADDRESS or txi_sign.script_type == InputScriptType.SPENDMULTISIG) if not is_bip143 or txi_sign.amount > authorized_in: raise SigningError(FailureType.ProcessError, "Transaction has changed during signing") authorized_in -= txi_sign.amount key_sign = keychain.derive(txi_sign.address_n, coin.curve_name) key_sign_pub = key_sign.public_key() hash143_hash = hash143.preimage_hash( coin, tx, txi_sign, addresses.ecdsa_hash_pubkey(key_sign_pub, coin), get_hash_type(coin), ) # if multisig, check if signing with a key that is included in multisig if txi_sign.multisig: multisig.multisig_pubkey_index(txi_sign.multisig, key_sign_pub) signature = ecdsa_sign(key_sign, hash143_hash) tx_ser.signature_index = i_sign tx_ser.signature = signature # serialize input with correct signature txi_sign.script_sig = input_derive_script(coin, txi_sign, key_sign_pub, signature) w_txi_sign = writers.empty_bytearray(5 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4) if i_sign == 0: # serializing first input => prepend headers writers.write_bytes(w_txi_sign, get_tx_header(coin, tx)) writers.write_tx_input(w_txi_sign, txi_sign) tx_ser.serialized_tx = w_txi_sign tx_req.serialized = tx_ser elif coin.decred: txi_sign = await helpers.request_tx_input(tx_req, i_sign) input_check_wallet_path(txi_sign, wallet_path) key_sign = keychain.derive(txi_sign.address_n, coin.curve_name) 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, coin)) else: raise ValueError("Unknown input script type") h_witness = utils.HashWriter(blake256()) writers.write_uint32( h_witness, tx.version | decred.DECRED_SERIALIZE_WITNESS_SIGNING) writers.write_varint(h_witness, tx.inputs_count) for ii in range(tx.inputs_count): if ii == i_sign: writers.write_varint(h_witness, len(prev_pkscript)) writers.write_bytes(h_witness, prev_pkscript) else: writers.write_varint(h_witness, 0) witness_hash = writers.get_tx_hash(h_witness, double=coin.sign_hash_double, reverse=False) h_sign = utils.HashWriter(blake256()) writers.write_uint32(h_sign, decred.DECRED_SIGHASHALL) writers.write_bytes(h_sign, prefix_hash) writers.write_bytes(h_sign, witness_hash) sig_hash = writers.get_tx_hash(h_sign, double=coin.sign_hash_double) signature = ecdsa_sign(key_sign, sig_hash) tx_ser.signature_index = i_sign tx_ser.signature = signature # serialize input with correct signature txi_sign.script_sig = input_derive_script(coin, txi_sign, key_sign_pub, signature) w_txi_sign = writers.empty_bytearray( 8 + 4 + len(hash143.get_last_output_bytes()) if i_sign == 0 else 0 + 16 + 4 + len(txi_sign.script_sig)) if i_sign == 0: writers.write_bytes(w_txi_sign, hash143.get_last_output_bytes()) writers.write_uint32(w_txi_sign, tx.lock_time) writers.write_uint32(w_txi_sign, tx.expiry) writers.write_varint(w_txi_sign, tx.inputs_count) writers.write_tx_input_decred_witness(w_txi_sign, txi_sign) tx_ser.serialized_tx = w_txi_sign tx_req.serialized = tx_ser else: # hash of what we are signing with this input h_sign = utils.HashWriter(sha256()) # same as h_first, checked before signing the digest h_second = utils.HashWriter(sha256()) if tx.overwintered: writers.write_uint32( h_sign, tx.version | zcash.OVERWINTERED) # nVersion | fOverwintered writers.write_uint32(h_sign, tx.version_group_id) # nVersionGroupId else: writers.write_uint32(h_sign, tx.version) # nVersion if tx.timestamp: writers.write_uint32(h_sign, tx.timestamp) writers.write_varint(h_sign, tx.inputs_count) for i in range(tx.inputs_count): # STAGE_REQUEST_4_INPUT txi = await helpers.request_tx_input(tx_req, i) input_check_wallet_path(txi, wallet_path) writers.write_tx_input_check(h_second, txi) if i == i_sign: txi_sign = txi key_sign = keychain.derive(txi.address_n, coin.curve_name) key_sign_pub = key_sign.public_key() # for the signing process the script_sig is equal # to the previous tx's scriptPubKey (P2PKH) or a redeem script (P2SH) if txi_sign.script_type == InputScriptType.SPENDMULTISIG: txi_sign.script_sig = scripts.output_script_multisig( multisig.multisig_get_pubkeys(txi_sign.multisig), txi_sign.multisig.m, ) elif txi_sign.script_type == InputScriptType.SPENDADDRESS: txi_sign.script_sig = scripts.output_script_p2pkh( addresses.ecdsa_hash_pubkey(key_sign_pub, coin)) if coin.bip115: txi_sign.script_sig += scripts.script_replay_protection_bip115( txi_sign.prev_block_hash_bip115, txi_sign.prev_block_height_bip115, ) else: raise SigningError(FailureType.ProcessError, "Unknown transaction type") else: txi.script_sig = bytes() writers.write_tx_input(h_sign, txi) writers.write_varint(h_sign, tx.outputs_count) for o in range(tx.outputs_count): # STAGE_REQUEST_4_OUTPUT txo = await helpers.request_tx_output(tx_req, o) txo_bin.amount = txo.amount txo_bin.script_pubkey = output_derive_script( txo, coin, keychain) writers.write_tx_output(h_second, txo_bin) writers.write_tx_output(h_sign, txo_bin) writers.write_uint32(h_sign, tx.lock_time) if tx.overwintered: writers.write_uint32(h_sign, tx.expiry) # expiryHeight writers.write_varint(h_sign, 0) # nJoinSplit writers.write_uint32(h_sign, get_hash_type(coin)) # check the control digests if writers.get_tx_hash(h_first, False) != writers.get_tx_hash(h_second): raise SigningError(FailureType.ProcessError, "Transaction has changed during signing") # if multisig, check if signing with a key that is included in multisig if txi_sign.multisig: multisig.multisig_pubkey_index(txi_sign.multisig, key_sign_pub) # compute the signature from the tx digest signature = ecdsa_sign( key_sign, writers.get_tx_hash(h_sign, double=coin.sign_hash_double)) tx_ser.signature_index = i_sign tx_ser.signature = signature # serialize input with correct signature txi_sign.script_sig = input_derive_script(coin, txi_sign, key_sign_pub, signature) w_txi_sign = writers.empty_bytearray(5 + len(txi_sign.prev_hash) + 4 + len(txi_sign.script_sig) + 4) if i_sign == 0: # serializing first input => prepend headers writers.write_bytes(w_txi_sign, get_tx_header(coin, tx)) writers.write_tx_input(w_txi_sign, txi_sign) tx_ser.serialized_tx = w_txi_sign tx_req.serialized = tx_ser if coin.decred: return await helpers.request_tx_finish(tx_req) for o in range(tx.outputs_count): progress.advance() # STAGE_REQUEST_5_OUTPUT txo = await helpers.request_tx_output(tx_req, o) txo_bin.amount = txo.amount txo_bin.script_pubkey = output_derive_script(txo, coin, keychain) # serialize output w_txo_bin = writers.empty_bytearray(5 + 8 + 5 + len(txo_bin.script_pubkey) + 4) if o == 0: # serializing first output => prepend outputs count writers.write_varint(w_txo_bin, tx.outputs_count) writers.write_tx_output(w_txo_bin, txo_bin) tx_ser.signature_index = None tx_ser.signature = None tx_ser.serialized_tx = w_txo_bin tx_req.serialized = tx_ser any_segwit = True in segwit.values() for i in range(tx.inputs_count): progress.advance() if segwit[i]: # STAGE_REQUEST_SEGWIT_WITNESS txi = await helpers.request_tx_input(tx_req, i) input_check_wallet_path(txi, wallet_path) if not input_is_segwit(txi) or txi.amount > authorized_in: raise SigningError(FailureType.ProcessError, "Transaction has changed during signing") authorized_in -= txi.amount key_sign = keychain.derive(txi.address_n, coin.curve_name) key_sign_pub = key_sign.public_key() hash143_hash = hash143.preimage_hash( coin, tx, txi, addresses.ecdsa_hash_pubkey(key_sign_pub, coin), get_hash_type(coin), ) signature = ecdsa_sign(key_sign, hash143_hash) if txi.multisig: # find out place of our signature based on the pubkey signature_index = multisig.multisig_pubkey_index( txi.multisig, key_sign_pub) witness = scripts.witness_p2wsh(txi.multisig, signature, signature_index, get_hash_type(coin)) else: witness = scripts.witness_p2wpkh(signature, key_sign_pub, get_hash_type(coin)) tx_ser.serialized_tx = witness tx_ser.signature_index = i tx_ser.signature = signature elif any_segwit: tx_ser.serialized_tx = bytearray( 1) # empty witness for non-segwit inputs tx_ser.signature_index = None tx_ser.signature = None tx_req.serialized = tx_ser writers.write_uint32(tx_ser.serialized_tx, tx.lock_time) if tx.overwintered: if tx.version == 3: writers.write_uint32(tx_ser.serialized_tx, tx.expiry) # expiryHeight writers.write_varint(tx_ser.serialized_tx, 0) # nJoinSplit elif tx.version == 4: writers.write_uint32(tx_ser.serialized_tx, tx.expiry) # expiryHeight writers.write_uint64(tx_ser.serialized_tx, 0) # valueBalance writers.write_varint(tx_ser.serialized_tx, 0) # nShieldedSpend writers.write_varint(tx_ser.serialized_tx, 0) # nShieldedOutput writers.write_varint(tx_ser.serialized_tx, 0) # nJoinSplit else: raise SigningError( FailureType.DataError, "Unsupported version for overwintered transaction", ) await helpers.request_tx_finish(tx_req)