def gen_hmac_vini(key, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, idx: int) -> bytes: """ Computes hmac (TxSourceEntry[i] || tx.vin[i]) In src_entr.outputs only src_entr.outputs[src_entr.real_output] is HMACed as it is used across the protocol. Consistency of other values across the protocol is not required as they are used only once and hard to check. I.e., indices in step 2 are uncheckable, decoy keys in step 9 are just random keys. """ from trezor import protobuf from apps.monero.xmr.keccak_hasher import get_keccak_writer kwriter = get_keccak_writer() real_outputs = src_entr.outputs real_additional = src_entr.real_out_additional_tx_keys src_entr.outputs = [src_entr.outputs[src_entr.real_output]] if real_additional and len(real_additional) > 1: src_entr.real_out_additional_tx_keys = [ src_entr.real_out_additional_tx_keys[ src_entr.real_output_in_tx_index] ] kwriter.write(protobuf.dump_message_buffer(src_entr)) src_entr.outputs = real_outputs src_entr.real_out_additional_tx_keys = real_additional kwriter.write(vini_bin) hmac_key_vini = hmac_key_txin(key, idx) hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest()) return hmac_vini
def compute_enc_key_host(view_key_private: Sc25519, tx_prefix_hash: bytes) -> Tuple[bytes, bytes]: from apps.monero.xmr import crypto salt = crypto.random_bytes(32) passwd = crypto.keccak_2hash( crypto.encodeint(view_key_private) + tx_prefix_hash) tx_key = crypto.compute_hmac(salt, passwd) return tx_key, salt
def _compute_tx_key(spend_key_private, tx_prefix_hash): salt = crypto.random_bytes(32) rand_mult_num = crypto.random_scalar() rand_mult = crypto.encodeint(rand_mult_num) rand_inp = crypto.sc_add(spend_key_private, rand_mult_num) passwd = crypto.keccak_2hash(crypto.encodeint(rand_inp) + tx_prefix_hash) tx_key = crypto.compute_hmac(salt, passwd) return tx_key, salt, rand_mult
def _hash_vini_pseudo_out(state: State, pseudo_out: bytes, pseudo_out_hmac: bytes): """ Incremental hasing of pseudo output. Only applicable for simple rct. """ idx = state.source_permutation[state.current_input_index] pseudo_out_hmac_comp = crypto.compute_hmac( offloading_keys.hmac_key_txin_comm(state.key_hmac, idx), pseudo_out) if not crypto.ct_equals(pseudo_out_hmac, pseudo_out_hmac_comp): raise ValueError("HMAC invalid for pseudo outs") state.full_message_hasher.set_pseudo_out(pseudo_out)
def compute_tx_key( spend_key_private: Sc25519, tx_prefix_hash: bytes, salt: bytes, rand_mult_num: Sc25519, ) -> bytes: from apps.monero.xmr import crypto rand_inp = crypto.sc_add(spend_key_private, rand_mult_num) passwd = crypto.keccak_2hash(crypto.encodeint(rand_inp) + tx_prefix_hash) tx_key = crypto.compute_hmac(salt, passwd) return tx_key
async def gen_hmac_tsxdest(key, dst_entr, idx: int) -> bytes: """ Generates HMAC for TxDestinationEntry[i] """ import protobuf from apps.monero.xmr.keccak_hasher import get_keccak_writer kwriter = get_keccak_writer() await protobuf.dump_message(kwriter, dst_entr) hmac_key = hmac_key_txdst(key, idx) hmac_tsxdest = crypto.compute_hmac(hmac_key, kwriter.get_digest()) return hmac_tsxdest
async def gen_hmac_vouti(key, dst_entr, tx_out_bin, idx: int) -> bytes: """ Generates HMAC for (TxDestinationEntry[i] || tx.vout[i]) """ import protobuf from apps.monero.xmr.keccak_hasher import get_keccak_writer kwriter = get_keccak_writer() await protobuf.dump_message(kwriter, dst_entr) kwriter.write(tx_out_bin) hmac_key_vouti = hmac_key_txout(key, idx) hmac_vouti = crypto.compute_hmac(hmac_key_vouti, kwriter.get_digest()) return hmac_vouti
async def gen_hmac_vini(key, src_entr, vini_bin, idx: int) -> bytes: """ Computes hmac (TxSourceEntry[i] || tx.vin[i]) """ import protobuf from apps.monero.xmr.keccak_hasher import get_keccak_writer kwriter = get_keccak_writer() await protobuf.dump_message(kwriter, src_entr) kwriter.write(vini_bin) hmac_key_vini = hmac_key_txin(key, idx) hmac_vini = crypto.compute_hmac(hmac_key_vini, kwriter.get_digest()) return hmac_vini
def gen_hmac_tsxdest(key, dst_entr: MoneroTransactionDestinationEntry, idx: int) -> bytes: """ Generates HMAC for TxDestinationEntry[i] """ from trezor import protobuf from apps.monero.xmr.keccak_hasher import get_keccak_writer kwriter = get_keccak_writer() kwriter.write(protobuf.dump_message_buffer(dst_entr)) hmac_key = hmac_key_txdst(key, idx) hmac_tsxdest = crypto.compute_hmac(hmac_key, kwriter.get_digest()) return hmac_tsxdest
def gen_hmac_vouti(key, dst_entr: MoneroTransactionDestinationEntry, tx_out_bin: bytes, idx: int) -> bytes: """ Generates HMAC for (TxDestinationEntry[i] || tx.vout[i]) """ from trezor import protobuf from apps.monero.xmr.keccak_hasher import get_keccak_writer kwriter = get_keccak_writer() kwriter.write(protobuf.dump_message_buffer(dst_entr)) kwriter.write(tx_out_bin) hmac_key_vouti = hmac_key_txout(key, idx) hmac_vouti = crypto.compute_hmac(hmac_key_vouti, kwriter.get_digest()) return hmac_vouti
async def set_input( state: State, src_entr: MoneroTransactionSourceEntry ) -> MoneroTransactionSetInputAck: from trezor.messages import MoneroTransactionSetInputAck from apps.monero.xmr.crypto import chacha_poly from apps.monero.xmr.serialize_messages.tx_prefix import TxinToKey from apps.monero.signing import offloading_keys state.current_input_index += 1 await layout.transaction_step(state, state.STEP_INP, state.current_input_index) if state.last_step > state.STEP_INP: raise ValueError("Invalid state transition") if state.current_input_index >= state.input_count: raise ValueError("Too many inputs") # real_output denotes which output in outputs is the real one (ours) if src_entr.real_output >= len(src_entr.outputs): raise ValueError( f"real_output index {src_entr.real_output} bigger than output_keys.size() {len(src_entr.outputs)}" ) state.summary_inputs_money += src_entr.amount # Secrets derivation # the UTXO's one-time address P out_key = crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.dest) # the tx_pub of our UTXO stored inside its transaction tx_key = crypto.decodepoint(src_entr.real_out_tx_key) additional_tx_pub_key = _get_additional_public_key(src_entr) # Calculates `derivation = Ra`, private spend key `x = H(Ra||i) + b` to be able # to spend the UTXO; and key image `I = x*H(P||i)` xi, ki, _di = monero.generate_tx_spend_and_key_image_and_derivation( state.creds, state.subaddresses, out_key, tx_key, additional_tx_pub_key, src_entr.real_output_in_tx_index, state.account_idx, src_entr.subaddr_minor, ) state.mem_trace(1, True) # Construct tx.vin # If multisig is used then ki in vini should be src_entr.multisig_kLRki.ki vini = TxinToKey(amount=src_entr.amount, k_image=crypto.encodepoint(ki)) vini.key_offsets = _absolute_output_offsets_to_relative( [x.idx for x in src_entr.outputs]) if src_entr.rct: vini.amount = 0 # Serialize `vini` with variant code for TxinToKey (prefix = TxinToKey.VARIANT_CODE). # The binary `vini_bin` is later sent to step 4 and 9 with its hmac, # where it is checked and directly used. vini_bin = serialize.dump_msg(vini, preallocate=64, prefix=b"\x02") state.mem_trace(2, True) # HMAC(T_in,i || vin_i) hmac_vini = offloading_keys.gen_hmac_vini(state.key_hmac, src_entr, vini_bin, state.current_input_index) state.mem_trace(3, True) # PseudoOuts commitment, alphas stored to state alpha, pseudo_out = _gen_commitment(state, src_entr.amount) pseudo_out = crypto.encodepoint(pseudo_out) # The alpha is encrypted and passed back for storage pseudo_out_hmac = crypto.compute_hmac( offloading_keys.hmac_key_txin_comm(state.key_hmac, state.current_input_index), pseudo_out, ) alpha_enc = chacha_poly.encrypt_pack( offloading_keys.enc_key_txin_alpha(state.key_enc, state.current_input_index), crypto.encodeint(alpha), ) spend_enc = chacha_poly.encrypt_pack( offloading_keys.enc_key_spend(state.key_enc, state.current_input_index), crypto.encodeint(xi), ) state.last_step = state.STEP_INP if state.current_input_index + 1 == state.input_count: # When we finish the inputs processing, we no longer need # the precomputed subaddresses so we clear them to save memory. state.subaddresses = None state.input_last_amount = src_entr.amount return MoneroTransactionSetInputAck( vini=vini_bin, vini_hmac=hmac_vini, pseudo_out=pseudo_out, pseudo_out_hmac=pseudo_out_hmac, pseudo_out_alpha=alpha_enc, spend_key=spend_enc, )
async def sign_input( state: State, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, vini_hmac: bytes, pseudo_out: bytes, pseudo_out_hmac: bytes, pseudo_out_alpha_enc: bytes, spend_enc: bytes, orig_idx: int, ) -> MoneroTransactionSignInputAck: """ :param state: transaction state :param src_entr: Source entry :param vini_bin: tx.vin[i] for the transaction. Contains key image, offsets, amount (usually zero) :param vini_hmac: HMAC for the tx.vin[i] as returned from Trezor :param pseudo_out: Pedersen commitment for the current input, uses pseudo_out_alpha as a mask. Only applicable for RCTTypeSimple. :param pseudo_out_hmac: HMAC for pseudo_out :param pseudo_out_alpha_enc: alpha mask used in pseudo_out, only applicable for RCTTypeSimple. Encrypted. :param spend_enc: one time address spending private key. Encrypted. :param orig_idx: original index of the src_entr before sorting (HMAC check) :return: Generated signature MGs[i] """ await confirms.transaction_step(state, state.STEP_SIGN, state.current_input_index + 1) state.current_input_index += 1 if state.last_step not in (state.STEP_ALL_OUT, state.STEP_SIGN): raise ValueError("Invalid state transition") if state.current_input_index >= state.input_count: raise ValueError("Invalid inputs count") if pseudo_out is None: raise ValueError("SimpleRCT requires pseudo_out but none provided") if pseudo_out_alpha_enc is None: raise ValueError( "SimpleRCT requires pseudo_out's mask but none provided") input_position = (state.source_permutation[state.current_input_index] if state.client_version <= 1 else orig_idx) mods = utils.unimport_begin() # Check input's HMAC from apps.monero.signing import offloading_keys vini_hmac_comp = await offloading_keys.gen_hmac_vini( state.key_hmac, src_entr, vini_bin, input_position) if not crypto.ct_equals(vini_hmac_comp, vini_hmac): raise ValueError("HMAC is not correct") # Key image sorting check - permutation correctness cur_ki = offloading_keys.get_ki_from_vini(vini_bin) if state.current_input_index > 0 and state.last_ki <= cur_ki: raise ValueError("Key image order invalid") state.last_ki = cur_ki if state.current_input_index < state.input_count else None del (cur_ki, vini_bin, vini_hmac, vini_hmac_comp) gc.collect() state.mem_trace(1, True) from apps.monero.xmr.crypto import chacha_poly pseudo_out_alpha = crypto.decodeint( chacha_poly.decrypt_pack( offloading_keys.enc_key_txin_alpha(state.key_enc, input_position), bytes(pseudo_out_alpha_enc), )) # Last pseudo_out is recomputed so mask sums hold if input_position + 1 == state.input_count: # Recompute the lash alpha so the sum holds state.mem_trace("Correcting alpha") alpha_diff = crypto.sc_sub(state.sumout, state.sumpouts_alphas) crypto.sc_add_into(pseudo_out_alpha, pseudo_out_alpha, alpha_diff) pseudo_out_c = crypto.gen_commitment(pseudo_out_alpha, state.input_last_amount) else: if input_position + 1 == state.input_count: utils.ensure(crypto.sc_eq(state.sumpouts_alphas, state.sumout), "Sum eq error") # both pseudo_out and its mask were offloaded so we need to # validate pseudo_out's HMAC and decrypt the alpha pseudo_out_hmac_comp = crypto.compute_hmac( offloading_keys.hmac_key_txin_comm(state.key_hmac, input_position), pseudo_out, ) if not crypto.ct_equals(pseudo_out_hmac_comp, pseudo_out_hmac): raise ValueError("HMAC is not correct") pseudo_out_c = crypto.decodepoint(pseudo_out) state.mem_trace(2, True) # Spending secret spend_key = crypto.decodeint( chacha_poly.decrypt_pack( offloading_keys.enc_key_spend(state.key_enc, input_position), bytes(spend_enc), )) del ( offloading_keys, chacha_poly, pseudo_out, pseudo_out_hmac, pseudo_out_alpha_enc, spend_enc, ) utils.unimport_end(mods) state.mem_trace(3, True) # Basic setup, sanity check from apps.monero.xmr.serialize_messages.tx_ct_key import CtKey index = src_entr.real_output input_secret_key = CtKey(spend_key, crypto.decodeint(src_entr.mask)) # Private key correctness test utils.ensure( crypto.point_eq( crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.dest), crypto.scalarmult_base(input_secret_key.dest), ), "Real source entry's destination does not equal spend key's", ) utils.ensure( crypto.point_eq( crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.commitment), crypto.gen_commitment(input_secret_key.mask, src_entr.amount), ), "Real source entry's mask does not equal spend key's", ) state.mem_trace(4, True) from apps.monero.xmr import mlsag mg_buffer = [] ring_pubkeys = [x.key for x in src_entr.outputs if x] utils.ensure(len(ring_pubkeys) == len(src_entr.outputs), "Invalid ring") del src_entr state.mem_trace(5, True) if state.hard_fork and state.hard_fork >= 13: state.mem_trace("CLSAG") mlsag.generate_clsag_simple( state.full_message, ring_pubkeys, input_secret_key, pseudo_out_alpha, pseudo_out_c, index, mg_buffer, ) else: mlsag.generate_mlsag_simple( state.full_message, ring_pubkeys, input_secret_key, pseudo_out_alpha, pseudo_out_c, index, mg_buffer, ) del (CtKey, input_secret_key, pseudo_out_alpha, mlsag, ring_pubkeys) state.mem_trace(6, True) from trezor.messages.MoneroTransactionSignInputAck import ( MoneroTransactionSignInputAck, ) # Encrypt signature, reveal once protocol finishes OK if state.client_version >= 3: utils.unimport_end(mods) state.mem_trace(7, True) mg_buffer = _protect_signature(state, mg_buffer) state.mem_trace(8, True) state.last_step = state.STEP_SIGN return MoneroTransactionSignInputAck( signature=mg_buffer, pseudo_out=crypto.encodepoint(pseudo_out_c))
async def sign_input( state: State, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, vini_hmac: bytes, pseudo_out: bytes, pseudo_out_hmac: bytes, pseudo_out_alpha_enc: bytes, spend_enc: bytes, ): """ :param state: transaction state :param src_entr: Source entry :param vini_bin: tx.vin[i] for the transaction. Contains key image, offsets, amount (usually zero) :param vini_hmac: HMAC for the tx.vin[i] as returned from Trezor :param pseudo_out: Pedersen commitment for the current input, uses pseudo_out_alpha as a mask. Only applicable for RCTTypeSimple. :param pseudo_out_hmac: HMAC for pseudo_out :param pseudo_out_alpha_enc: alpha mask used in pseudo_out, only applicable for RCTTypeSimple. Encrypted. :param spend_enc: one time address spending private key. Encrypted. :return: Generated signature MGs[i] """ await confirms.transaction_step(state, state.STEP_SIGN, state.current_input_index + 1) state.current_input_index += 1 if state.current_input_index >= state.input_count: raise ValueError("Invalid inputs count") if pseudo_out is None: raise ValueError("SimpleRCT requires pseudo_out but none provided") if pseudo_out_alpha_enc is None: raise ValueError( "SimpleRCT requires pseudo_out's mask but none provided") input_position = state.source_permutation[state.current_input_index] mods = utils.unimport_begin() # Check input's HMAC from apps.monero.signing import offloading_keys vini_hmac_comp = await offloading_keys.gen_hmac_vini( state.key_hmac, src_entr, vini_bin, input_position) if not crypto.ct_equals(vini_hmac_comp, vini_hmac): raise ValueError("HMAC is not correct") gc.collect() state.mem_trace(1, True) from apps.monero.xmr.crypto import chacha_poly pseudo_out_alpha = crypto.decodeint( chacha_poly.decrypt_pack( offloading_keys.enc_key_txin_alpha(state.key_enc, input_position), bytes(pseudo_out_alpha_enc), )) # Last pseud_out is recomputed so mask sums hold if state.is_det_mask() and input_position + 1 == state.input_count: # Recompute the lash alpha so the sum holds state.mem_trace("Correcting alpha") alpha_diff = crypto.sc_sub(state.sumout, state.sumpouts_alphas) crypto.sc_add_into(pseudo_out_alpha, pseudo_out_alpha, alpha_diff) pseudo_out_c = crypto.gen_commitment(pseudo_out_alpha, state.input_last_amount) else: if input_position + 1 == state.input_count: utils.ensure(crypto.sc_eq(state.sumpouts_alphas, state.sumout), "Sum eq error") # both pseudo_out and its mask were offloaded so we need to # validate pseudo_out's HMAC and decrypt the alpha pseudo_out_hmac_comp = crypto.compute_hmac( offloading_keys.hmac_key_txin_comm(state.key_hmac, input_position), pseudo_out, ) if not crypto.ct_equals(pseudo_out_hmac_comp, pseudo_out_hmac): raise ValueError("HMAC is not correct") pseudo_out_c = crypto.decodepoint(pseudo_out) state.mem_trace(2, True) # Spending secret spend_key = crypto.decodeint( chacha_poly.decrypt_pack( offloading_keys.enc_key_spend(state.key_enc, input_position), bytes(spend_enc), )) del ( offloading_keys, chacha_poly, pseudo_out, pseudo_out_hmac, pseudo_out_alpha_enc, spend_enc, ) utils.unimport_end(mods) state.mem_trace(3, True) from apps.monero.xmr.serialize_messages.ct_keys import CtKey # Basic setup, sanity check index = src_entr.real_output input_secret_key = CtKey(dest=spend_key, mask=crypto.decodeint(src_entr.mask)) kLRki = None # for multisig: src_entr.multisig_kLRki # Private key correctness test utils.ensure( crypto.point_eq( crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.dest), crypto.scalarmult_base(input_secret_key.dest), ), "Real source entry's destination does not equal spend key's", ) utils.ensure( crypto.point_eq( crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.commitment), crypto.gen_commitment(input_secret_key.mask, src_entr.amount), ), "Real source entry's mask does not equal spend key's", ) state.mem_trace(4, True) from apps.monero.xmr import mlsag mg_buffer = [] ring_pubkeys = [x.key for x in src_entr.outputs] del src_entr mlsag.generate_mlsag_simple( state.full_message, ring_pubkeys, input_secret_key, pseudo_out_alpha, pseudo_out_c, kLRki, index, mg_buffer, ) del (input_secret_key, pseudo_out_alpha, mlsag, ring_pubkeys) state.mem_trace(5, True) from trezor.messages.MoneroTransactionSignInputAck import ( MoneroTransactionSignInputAck, ) return MoneroTransactionSignInputAck( signature=mg_buffer, pseudo_out=crypto.encodepoint(pseudo_out_c))
async def sign_input( state: State, src_entr: MoneroTransactionSourceEntry, vini_bin: bytes, vini_hmac: bytes, pseudo_out: bytes, pseudo_out_hmac: bytes, pseudo_out_alpha_enc: bytes, spend_enc: bytes, ): """ :param state: transaction state :param src_entr: Source entry :param vini_bin: tx.vin[i] for the transaction. Contains key image, offsets, amount (usually zero) :param vini_hmac: HMAC for the tx.vin[i] as returned from Trezor :param pseudo_out: Pedersen commitment for the current input, uses pseudo_out_alpha as a mask. Only applicable for RCTTypeSimple. :param pseudo_out_hmac: HMAC for pseudo_out :param pseudo_out_alpha_enc: alpha mask used in pseudo_out, only applicable for RCTTypeSimple. Encrypted. :param spend_enc: one time address spending private key. Encrypted. :return: Generated signature MGs[i] """ from apps.monero.signing import offloading_keys await confirms.transaction_step(state.ctx, state.STEP_SIGN, state.current_input_index + 1, state.input_count) state.current_input_index += 1 if state.current_input_index >= state.input_count: raise ValueError("Invalid inputs count") if state.rct_type == RctType.Simple and pseudo_out is None: raise ValueError("SimpleRCT requires pseudo_out but none provided") if state.rct_type == RctType.Simple and pseudo_out_alpha_enc is None: raise ValueError( "SimpleRCT requires pseudo_out's mask but none provided") if state.current_input_index >= 1 and not state.rct_type == RctType.Simple: raise ValueError("Two and more inputs must imply SimpleRCT") input_position = state.source_permutation[state.current_input_index] # Check input's HMAC vini_hmac_comp = await offloading_keys.gen_hmac_vini( state.key_hmac, src_entr, vini_bin, input_position) if not crypto.ct_equals(vini_hmac_comp, vini_hmac): raise ValueError("HMAC is not correct") gc.collect() state.mem_trace(1) if state.rct_type == RctType.Simple: # both pseudo_out and its mask were offloaded so we need to # validate pseudo_out's HMAC and decrypt the alpha pseudo_out_hmac_comp = crypto.compute_hmac( offloading_keys.hmac_key_txin_comm(state.key_hmac, input_position), pseudo_out, ) if not crypto.ct_equals(pseudo_out_hmac_comp, pseudo_out_hmac): raise ValueError("HMAC is not correct") gc.collect() state.mem_trace(2) from apps.monero.xmr.crypto import chacha_poly pseudo_out_alpha = crypto.decodeint( chacha_poly.decrypt_pack( offloading_keys.enc_key_txin_alpha(state.key_enc, input_position), bytes(pseudo_out_alpha_enc), )) pseudo_out_c = crypto.decodepoint(pseudo_out) # Spending secret from apps.monero.xmr.crypto import chacha_poly from apps.monero.xmr.serialize_messages.ct_keys import CtKey spend_key = crypto.decodeint( chacha_poly.decrypt_pack( offloading_keys.enc_key_spend(state.key_enc, input_position), bytes(spend_enc), )) gc.collect() state.mem_trace(3) # Basic setup, sanity check index = src_entr.real_output input_secret_key = CtKey(dest=spend_key, mask=crypto.decodeint(src_entr.mask)) kLRki = None # for multisig: src_entr.multisig_kLRki # Private key correctness test utils.ensure( crypto.point_eq( crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.dest), crypto.scalarmult_base(input_secret_key.dest), ), "Real source entry's destination does not equal spend key's", ) utils.ensure( crypto.point_eq( crypto.decodepoint( src_entr.outputs[src_entr.real_output].key.commitment), crypto.gen_commitment(input_secret_key.mask, src_entr.amount), ), "Real source entry's mask does not equal spend key's", ) gc.collect() state.mem_trace(4) from apps.monero.xmr import mlsag if state.rct_type == RctType.Simple: ring_pubkeys = [x.key for x in src_entr.outputs] mg = mlsag.generate_mlsag_simple( state.full_message, ring_pubkeys, input_secret_key, pseudo_out_alpha, pseudo_out_c, kLRki, index, ) else: # Full RingCt, only one input txn_fee_key = crypto.scalarmult_h(state.fee) ring_pubkeys = [[x.key] for x in src_entr.outputs] mg = mlsag.generate_mlsag_full( state.full_message, ring_pubkeys, [input_secret_key], state.output_sk_masks, state.output_pk_commitments, kLRki, index, txn_fee_key, ) gc.collect() state.mem_trace(5) # Encode mgs = _recode_msg([mg]) gc.collect() state.mem_trace(6) from trezor.messages.MoneroTransactionSignInputAck import ( MoneroTransactionSignInputAck, ) return MoneroTransactionSignInputAck( signature=serialize.dump_msg_gc(mgs[0], preallocate=488))