async def import_outputs(self, outputs): """ Key images sync. Required for hot wallet be able to construct transactions. If the signed transaction is not relayed with the hot wallet it gets out of sync with key images. Thus importing is needed. Wallet2::import_outputs() :param outputs: :return: """ ki_export_init = await key_image.generate_commitment(outputs) ki_export_init.address_n = self.address_n ki_export_init.network_type = self.network_type t_res = await self.trezor.key_image_sync( MoneroKeyImageSyncRequest(init=ki_export_init)) self.handle_error(t_res) sub_res = [] iter = await key_image.yield_key_image_data(outputs) batches = common.chunk(iter, 10) for rr in batches: # type: list[key_image.MoneroTransferDetails] t_res = await self.trezor.key_image_sync( MoneroKeyImageSyncRequest(step=MoneroKeyImageSyncStepRequest( tdis=rr))) self.handle_error(t_res) sub_res += t_res.kis t_res = await self.trezor.key_image_sync( MoneroKeyImageSyncRequest( final_msg=MoneroKeyImageSyncFinalRequest())) self.handle_error(t_res) # Decrypting phase enc_key = bytes(t_res.enc_key) final_res = [] for sub in sub_res: # type: key_image.MoneroExportedKeyImage plain = chacha_poly.decrypt(enc_key, bytes(sub.iv), bytes(sub.blob), bytes(sub.tag)) ki_bin = plain[:32] # ki = crypto.decodepoint(ki_bin) # sig = [crypto.decodeint(plain[32:64]), crypto.decodeint(plain[64:])] final_res.append((ki_bin, (plain[32:64], plain[64:]))) return final_res
async def verify(self, tx, con_data=None, creds=None): """ Transaction verification :param tx: :param con_data: :param creds: :return: """ # Unserialize the transaction tx_obj = xmrtypes.Transaction() reader = xmrserialize.MemoryReaderWriter(bytearray(tx)) ar1 = xmrserialize.Archive(reader, False) await ar1.message(tx_obj, msg_type=xmrtypes.Transaction) extras = await monero.parse_extra_fields(tx_obj.extra) monero.expand_transaction(tx_obj) tx_pub = crypto.decodepoint(monero.find_tx_extra_field_by_type( extras, xmrtypes.TxExtraPubKey ).pub_key) additional_pub_keys = monero.find_tx_extra_field_by_type( extras, xmrtypes.TxExtraAdditionalPubKeys ) additional_pub_keys = [crypto.decodepoint(x) for x in additional_pub_keys.data] if additional_pub_keys is not None else None # Verify range proofs out_idx = 0 is_bp = tx_obj.rct_signatures.type in [RctType.SimpleBulletproof, RctType.FullBulletproof] if not is_bp: for idx, rsig in enumerate(tx_obj.rct_signatures.p.rangeSigs): out_pk = tx_obj.rct_signatures.outPk[idx] C = crypto.decodepoint(out_pk.mask) rsig = tx_obj.rct_signatures.p.rangeSigs[idx] res = ring_ct.ver_range(C, rsig, use_bulletproof=is_bp) self.assertTrue(res) else: for idx, rsig in enumerate(tx_obj.rct_signatures.p.bulletproofs): rsig_num_outs = min(len(tx_obj.rct_signatures.outPk), 1 << (len(rsig.L) - 6)) outs = tx_obj.rct_signatures.outPk[out_idx : out_idx + rsig_num_outs] rsig.V = [crypto.encodepoint(ring_ct.bp_comm_to_v(crypto.decodepoint(xx.mask))) for xx in outs] res = ring_ct.ver_range(None, rsig, use_bulletproof=is_bp) self.assertTrue(res) # Prefix hash prefix_hash = await monero.get_transaction_prefix_hash(tx_obj) is_simple = len(tx_obj.vin) > 1 or is_bp self.assertEqual(prefix_hash, con_data.tx_prefix_hash) tx_obj.rct_signatures.message = prefix_hash # MLSAG hash mlsag_hash = await monero.get_pre_mlsag_hash(tx_obj.rct_signatures) # Decrypt transaction key tx_key = misc.compute_tx_key(creds.spend_key_private, prefix_hash, salt=con_data.enc_salt1, rand_mult=con_data.enc_salt2)[0] key_buff = chacha_poly.decrypt_pack(tx_key, con_data.enc_keys) tx_priv_keys = [crypto.decodeint(x) for x in common.chunk(key_buff, 32) if x] tx_priv = tx_priv_keys[0] tx_additional_priv = tx_priv_keys[1:] # Verify mlsag signature monero.recode_msg(tx_obj.rct_signatures.p.MGs, encode=False) for idx in range(len(tx_obj.vin)): if is_simple: mix_ring = [MoneroRctKeyPublic(dest=x[1].dest, commitment=x[1].mask) for x in con_data.tx_data.sources[idx].outputs] if is_bp: pseudo_out = crypto.decodepoint(bytes(tx_obj.rct_signatures.p.pseudoOuts[idx])) else: pseudo_out = crypto.decodepoint(bytes(tx_obj.rct_signatures.pseudoOuts[idx])) self.assertTrue(mlsag2.ver_rct_mg_simple( mlsag_hash, tx_obj.rct_signatures.p.MGs[idx], mix_ring, pseudo_out )) else: txn_fee_key = crypto.scalarmult_h(tx_obj.rct_signatures.txnFee) mix_ring = [[MoneroRctKeyPublic(dest=x[1].dest, commitment=x[1].mask)] for x in con_data.tx_data.sources[idx].outputs] self.assertTrue(mlsag2.ver_rct_mg( tx_obj.rct_signatures.p.MGs[idx], mix_ring, tx_obj.rct_signatures.outPk, txn_fee_key, mlsag_hash ))