async def reformat_out(self, out): res = misc.StdObj( amount=out.amount, addr=addr.build_address(out.addr.m_spend_public_key, out.addr.m_view_public_key), is_subaddress=out.is_subaddress, ) return res
def init_rct_sig(self): """ Initializes RCTsig structure (fee, tx prefix hash, type) :return: """ rv = misc.StdObj( txnFee=self.get_fee(), message=self.tx_prefix_hash, type=self.get_rct_type() ) return rv
async def primary_change_address(self, key, account): D, C = monero.generate_sub_address_keys( key.view_key_private, crypto.scalarmult_base(key.spend_key_private), account, 0, ) return misc.StdObj( view_public_key=crypto.encodepoint(C), spend_public_key=crypto.encodepoint(D), )
async def range_proof(self, idx, dest_pub_key, amount, amount_key): """ Computes rangeproof and related information - out_sk, out_pk, ecdh_info. In order to optimize incremental transaction build, the mask computation is changed compared to the official Monero code. In the official code, the input pedersen commitments are computed after range proof in such a way summed masks for commitments (alpha) and rangeproofs (ai) are equal. In order to save roundtrips we compute commitments randomly and then for the last rangeproof a[63] = (\\sum_{i=0}^{num_inp}alpha_i - \\sum_{i=0}^{num_outs-1} amasks_i) - \\sum_{i=0}^{62}a_i The range proof is incrementally hashed to the final_message. :param idx: :param dest_pub_key: :param amount: :param amount_key: :return: """ from monero_glue.xmr import ring_ct rsig = bytearray(32 * (64 + 64 + 64 + 1)) rsig_mv = memoryview(rsig) out_pk = misc.StdObj(dest=dest_pub_key, mask=None) is_last = idx + 1 == self.num_dests() last_mask = ( None if not is_last or not self.use_simple_rct else crypto.sc_sub(self.sumpouts_alphas, self.sumout) ) # Pedersen commitment on the value, mask from the commitment, range signature. C, mask, rsig = None, 0, None # Rangeproof gc.collect() if self.use_bulletproof: raise ValueError("Bulletproof not yet supported") else: C, mask, rsig = ring_ct.prove_range( amount, last_mask, backend_impl=True, byte_enc=True, rsig=rsig_mv ) rsig = memoryview(rsig) if __debug__: rsig_bytes = monero.inflate_rsig(rsig) self.assrt(ring_ct.ver_range(C, rsig_bytes)) self.assrt( crypto.point_eq( C, crypto.point_add( crypto.scalarmult_base(mask), crypto.scalarmult_h(amount) ), ), "rproof", ) # Incremental hashing await self.full_message_hasher.rsig_val( rsig, self.use_bulletproof, raw=True ) gc.collect() self._log_trace("rproof") # Mask sum out_pk.mask = crypto.encodepoint(C) self.sumout = crypto.sc_add(self.sumout, mask) self.output_sk.append(misc.StdObj(mask=mask)) # ECDH masking from monero_glue.xmr.sub.recode import recode_ecdh from monero_serialize.xmrtypes import EcdhTuple ecdh_info = EcdhTuple(mask=mask, amount=crypto.sc_init(amount)) ecdh_info = ring_ct.ecdh_encode( ecdh_info, derivation=crypto.encodeint(amount_key) ) recode_ecdh(ecdh_info, encode=True) gc.collect() return rsig, out_pk, ecdh_info
async def sign_input( self, src_entr, vini, hmac_vini, pseudo_out, pseudo_out_hmac, alpha_enc, spend_enc, ): """ Generates a signature for one input. :param src_entr: Source entry :type src_entr: monero_glue.xmr.serialize_messages.tx_construct.TxSourceEntry :param vini: tx.vin[i] for the transaction. Contains key image, offsets, amount (usually zero) :param hmac_vini: HMAC for the tx.vin[i] as returned from Trezor :param pseudo_out: pedersen commitment for the current input, uses alpha as the mask. Only in memory offloaded scenario. Tuple containing HMAC, as returned from the Trezor. :param pseudo_out_hmac: :param alpha_enc: alpha mask for the current input. Only in memory offloaded scenario, tuple as returned from the Trezor :param spend_enc: :return: Generated signature MGs[i] """ self.state.set_signature() await self.trezor.iface.transaction_step( self.STEP_SIGN, self.inp_idx + 1, self.num_inputs() ) self.inp_idx += 1 if self.inp_idx >= self.num_inputs(): raise ValueError("Invalid ins") if self.use_simple_rct and (not self.in_memory() and alpha_enc is None): raise ValueError("Inconsistent1") if self.use_simple_rct and (not self.in_memory() and pseudo_out is None): raise ValueError("Inconsistent2") if self.inp_idx >= 1 and not self.use_simple_rct: raise ValueError("Inconsistent3") inv_idx = self.source_permutation[self.inp_idx] # Check HMAC of all inputs hmac_vini_comp = await self.gen_hmac_vini(src_entr, vini, inv_idx) if not common.ct_equal(hmac_vini_comp, hmac_vini): raise ValueError("HMAC is not correct") gc.collect() self._log_trace(1) if self.use_simple_rct and not self.in_memory(): pseudo_out_hmac_comp = crypto.compute_hmac( self.hmac_key_txin_comm(inv_idx), pseudo_out ) if not common.ct_equal(pseudo_out_hmac_comp, pseudo_out_hmac): raise ValueError("HMAC is not correct") gc.collect() self._log_trace(2) from monero_glue.xmr.enc import chacha_poly alpha_c = crypto.decodeint( chacha_poly.decrypt_pack( self.enc_key_txin_alpha(inv_idx), bytes(alpha_enc) ) ) pseudo_out_c = crypto.decodepoint(pseudo_out) elif self.use_simple_rct: alpha_c = self.input_alphas[self.inp_idx] pseudo_out_c = crypto.decodepoint(self.input_pseudo_outs[self.inp_idx]) else: alpha_c = None pseudo_out_c = None # Spending secret if self.many_inputs(): from monero_glue.xmr.enc import chacha_poly input_secret = crypto.decodeint( chacha_poly.decrypt_pack(self.enc_key_spend(inv_idx), bytes(spend_enc)) ) else: input_secret = self.input_secrets[self.inp_idx] gc.collect() self._log_trace(3) # Basic setup, sanity check index = src_entr.real_output in_sk = misc.StdObj(dest=input_secret, mask=crypto.decodeint(src_entr.mask)) kLRki = src_entr.multisig_kLRki if self.multi_sig else None # Private key correctness test self.assrt( crypto.point_eq( crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].dest), crypto.scalarmult_base(in_sk.dest), ), "a1", ) self.assrt( crypto.point_eq( crypto.decodepoint(src_entr.outputs[src_entr.real_output][1].mask), crypto.gen_c(in_sk.mask, src_entr.amount), ), "a2", ) gc.collect() self._log_trace(4) # RCT signature gc.collect() from monero_glue.xmr import mlsag2 mg = None if self.use_simple_rct: # Simple RingCT mix_ring = [x[1] for x in src_entr.outputs] mg, msc = mlsag2.prove_rct_mg_simple( self.full_message, mix_ring, in_sk, alpha_c, pseudo_out_c, kLRki, None, index, ) if __debug__: self.assrt( mlsag2.ver_rct_mg_simple( self.full_message, mg, mix_ring, pseudo_out_c ) ) else: # Full RingCt, only one input txn_fee_key = crypto.scalarmult_h(self.get_fee()) mix_ring = [[x[1]] for x in src_entr.outputs] mg, msc = mlsag2.prove_rct_mg( self.full_message, mix_ring, [in_sk], self.output_sk, self.output_pk, kLRki, None, index, txn_fee_key, ) if __debug__: self.assrt( mlsag2.ver_rct_mg( mg, mix_ring, self.output_pk, txn_fee_key, self.full_message ) ) gc.collect() self._log_trace(5) # Encode from monero_glue.xmr.sub.recode import recode_msg mgs = recode_msg([mg]) cout = None gc.collect() self._log_trace(6) # Multisig values returned encrypted, keys returned after finished successfully. if self.multi_sig: from monero_glue.xmr.enc import chacha_poly cout = chacha_poly.encrypt_pack(self.enc_key_cout(), crypto.encodeint(msc)) # Final state transition if self.inp_idx + 1 == self.num_inputs(): self.state.set_signature_done() await self.trezor.iface.transaction_signed() gc.collect() self._log_trace() from monero_glue.messages.MoneroTransactionSignInputAck import ( MoneroTransactionSignInputAck ) return MoneroTransactionSignInputAck( signature=await misc.dump_msg_gc(mgs[0], preallocate=488, del_msg=True), cout=cout, )
async def _req_dst(self, Aout, Bout, amount, is_sub=False): addr = misc.StdObj(view_public_key=Aout, spend_public_key=Bout) out = misc.StdObj(addr=addr, amount=amount, is_subaddress=is_sub) await self.iface.confirm_out(out, False, self.creds, self.ctx)