async def sign_tx(ctx, msg): keychain = await seed.get_keychain(ctx) progress.init(msg.transactions_count, "Loading data") try: attested = len(msg.inputs) * [False] input_coins_sum = 0 # request transactions tx_req = CardanoTxRequest() for index in range(msg.transactions_count): progress.advance() tx_ack = await request_transaction(ctx, tx_req, index) tx_hash = hashlib.blake2b(data=bytes(tx_ack.transaction), outlen=32).digest() tx_decoded = cbor.decode(tx_ack.transaction) for i, input in enumerate(msg.inputs): if not attested[i] and input.prev_hash == tx_hash: attested[i] = True outputs = tx_decoded[1] amount = outputs[input.prev_index][1] input_coins_sum += amount if not all(attested): raise wire.ProcessError("No tx data sent for input " + str(attested.index(False))) transaction = Transaction(msg.inputs, msg.outputs, keychain, msg.protocol_magic, input_coins_sum) # clear progress bar display_homescreen() for i in msg.inputs: await validate_path(ctx, validate_full_path, keychain, i.address_n, CURVE) # sign the transaction bundle and prepare the result tx_body, tx_hash = transaction.serialise_tx() tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash) except ValueError as e: if __debug__: log.exception(__name__, e) raise wire.ProcessError("Signing failed") # display the transaction in UI if not await show_tx( ctx, transaction.output_addresses, transaction.outgoing_coins, transaction.fee, transaction.network_name, transaction.inputs, transaction.outputs, ): raise wire.ActionCancelled("Signing cancelled") return tx
def is_safe_output_address(address) -> bool: """ Determines whether it is safe to include the address as-is as a tx output, preventing unintended side effects (e.g. CBOR injection) """ try: address_hex = base58.decode(address) address_unpacked = cbor.decode(address_hex) except ValueError as e: if __debug__: log.exception(__name__, e) return False if not isinstance(address_unpacked, list) or len(address_unpacked) != 2: return False address_data_encoded = address_unpacked[0] if not isinstance(address_data_encoded, bytes): return False return _encode_address_raw(address_data_encoded) == address
def _process_inputs(self): input_coins = [] input_hashes = [] output_indexes = [] types = [] tx_data = {} for raw_transaction in self.transactions: tx_hash = hashlib.blake2b(data=bytes(raw_transaction), outlen=32).digest() tx_data[tx_hash] = cbor.decode(raw_transaction) for input in self.inputs: input_hashes.append(input.prev_hash) output_indexes.append(input.prev_index) types.append(input.type or 0) nodes = [] for input in self.inputs: _, node = derive_address_and_node(self.keychain, input.address_n) nodes.append(node) for index, output_index in enumerate(output_indexes): tx_hash = bytes(input_hashes[index]) if tx_hash in tx_data: tx = tx_data[tx_hash] outputs = tx[1] amount = outputs[output_index][1] input_coins.append(amount) else: raise wire.ProcessError("No tx data sent for input " + str(index)) self.input_coins = input_coins self.nodes = nodes self.types = types self.input_hashes = input_hashes self.output_indexes = output_indexes