def __init__(self): self.tsx_data = None # type: MoneroTransactionData self.tx_data = None # construction data self.rsig_type = 0 self.rsig_batches = [] self.rsig_param = None self.cur_input_idx = 0 self.cur_output_idx = 0 self.cur_batch_idx = 0 self.cur_output_in_batch_idx = 0 self.tx = xmrtypes.Transaction(version=2, vin=[], vout=[], extra=[]) self.tx_in_hmacs = [] self.tx_out_entr_hmacs = [] self.tx_out_hmacs = [] self.tx_out_rsigs = [] self.tx_out_pk = [] self.tx_out_ecdh = [] self.source_permutation = [] self.alphas = [] self.spend_encs = [] self.pseudo_outs = [] self.couts = [] self.rsig_gamma = [] self.tx_prefix_hash = None self.enc_salt1 = None self.enc_salt2 = None self.enc_keys = None # encrypted tx keys self.rv = None
def __init__(self, trezor=None): self.trezor = trezor # type: Trezor self.creds = None # type: monero.AccountCreds self.key_master = None self.key_hmac = None self.r = None # txkey self.r_pub = None self.tsx_data = None # type: monero.TsxData self.need_additional_txkeys = False self.use_bulletproof = False self.use_rct = True self.use_simple_rct = False self.additional_tx_keys = [] self.additional_tx_public_keys = [] self.inp_idx = -1 self.out_idx = -1 self.summary_inputs_money = 0 self.summary_outs_money = 0 self.input_rcts = [] self.input_secrets = [] self.output_secrets = [] self.subaddresses = {} self.tx = xmrtypes.Transaction(vin=[], vout=[], extra=b"") self.source_permutation = [] # sorted by key images self.tx_prefix_hash = None
def __init__(self): self.tsx_data = None # type: TsxData self.tx_data = None # construction data self.tx = xmrtypes.Transaction(version=2, vin=[], vout=[], extra=[]) self.tx_in_hmacs = [] self.tx_out_entr_hmacs = [] self.tx_out_hmacs = [] self.tx_out_rsigs = [] self.tx_out_pk = [] self.tx_out_ecdh = [] self.source_permutation = [] self.alphas = [] self.spend_encs = [] self.pseudo_outs = [] self.couts = [] self.tx_prefix_hash = None self.enc_salt1 = None self.enc_salt2 = None self.enc_keys = None # encrypted tx keys
def __init__(self): self.tsx_data = None # type: monero.TsxData self.tx = xmrtypes.Transaction(vin=[], vout=[], extra=[]) self.tx_in_hmacs = [] self.source_permutation = []
async def test_tx_prefix(self): return url = "http://localhost:48084/json_rpc" req = { "jsonrpc": "2.0", "id": "0", "method": "transfer_unsigned", "params": { "destinations": [ { "amount": 2110000000000, "address": "BZZeyHTQYZ9W9KX2M69WWxWat1Z6JQYsi4LjnZxuVTmCbsNxrUyLFbXiZHRwXgBcaESRz8HtHxTDGSCtgxDdEFpQFrKqXoX", }, { "amount": 2120000000000, "address": "BZg53n1EgLJhYDZNCi3VvxXFMdmmgk6HhhFCvvw9sMf1RQFp7LyjGvrNuF7TzukfaGh7Gsin2bEDpUNRv9oc8qSGMKCnktw", }, { "amount": 2130000000000, "address": "9wviCeWe2D8XS82k2ovp5EUYLzBt9pYNW2LXUFsZiv8S3Mt21FZ5qQaAroko1enzw3eGr9qC7X1D7Geoo2RrAotYPwq9Gm8", }, ], "account_index": 0, "subaddr_indices": [], "priority": 5, "mixin": 4, "unlock_time": 0, # "payment_id": "deadc0dedeadc0d1", "get_tx_keys": True, "do_not_relay": True, "get_tx_hex": True, "get_tx_metadata": True, }, } resp = requests.post(url, json=req) js = resp.json() # Transaction parsing blobs = js["result"]["tx_blob_list"] tx_blob = blobs[0] tx_unsigned = js["result"]["tx_unsigned"] tsx_bin = base64.b16decode(tx_blob, True) reader = xmrserialize.MemoryReaderWriter(bytearray(tsx_bin)) ar = xmrserialize.Archive(reader, False) msg = xmrtypes.Transaction() await ar.message(msg) # Unsigned transaction parsing tsx_unsigned_bin = base64.b16decode(tx_unsigned, True) reader = xmrserialize.MemoryReaderWriter(bytearray(tsx_unsigned_bin)) ar = xmrserialize.Archive(reader, False) unsig = xmrtypes.UnsignedTxSet() await ar.message(unsig) tagent = self.init_agent() txes = await tagent.sign_unsigned_tx(unsig) resp = requests.post( "http://localhost:48081/sendrawtransaction", json={ "tx_as_hex": binascii.hexlify(txes[0]).decode("utf8"), "do_not_relay": False, }, ) print(resp) print("Txblob: \n %s\n" % tx_blob) print("TxUns: \n %s\n" % tx_unsigned) print("TxMeta: \n %s\n" % js["result"]["tx_metadata_list"][0]) print("Done")
async def receive(self, tx, all_creds, con_data=None, exp_payment_id=None): """ Test transaction receive with known view/spend keys of destinations. :param tx: :param all_creds: :param con_data: :param exp_payment_id: :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) tx_pub = 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 ) num_outs = len(tx_obj.vout) num_received = 0 # Try to receive tsx outputs with each account. tx_money_got_in_outs = collections.defaultdict(lambda: 0) outs = [] change_idx = get_change_addr_idx(con_data.tsx_data.outputs, con_data.tsx_data.change_dts) for idx, creds in enumerate(all_creds): wallet_subs = {} for account in range(0, 5): monero.compute_subaddresses(creds, account, range(25), wallet_subs) derivation = crypto.generate_key_derivation( crypto.decodepoint(tx_pub), creds.view_key_private ) additional_derivations = [] if additional_pub_keys and additional_pub_keys.data: for x in additional_pub_keys.data: additional_derivations.append( crypto.generate_key_derivation( crypto.decodepoint(x), creds.view_key_private ) ) for ti, to in enumerate(tx_obj.vout): tx_scan_info = monero.check_acc_out_precomp( to, wallet_subs, derivation, additional_derivations, ti ) if not tx_scan_info.received: continue num_received += 1 tx_scan_info = monero.scan_output( creds, tx_obj, ti, tx_scan_info, tx_money_got_in_outs, outs, False ) # Check spending private key correctness self.assertTrue( crypto.point_eq( crypto.decodepoint(tx_obj.rct_signatures.outPk[ti].mask), crypto.gen_c(tx_scan_info.mask, tx_scan_info.amount), ) ) self.assertTrue( crypto.point_eq( crypto.decodepoint(tx_obj.vout[ti].target.key), crypto.scalarmult_base(tx_scan_info.in_ephemeral), ) ) if exp_payment_id is not None: payment_id = None # Not checking payment id for change transaction if exp_payment_id[0] == 1 and change_idx is not None and ti == change_idx: continue payment_id_type = None extra_nonce = monero.find_tx_extra_field_by_type(extras, xmrtypes.TxExtraNonce) if extra_nonce and monero.has_encrypted_payment_id(extra_nonce.nonce): payment_id_type = 1 payment_id = monero.get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce) payment_id = monero.encrypt_payment_id(payment_id, crypto.decodepoint(tx_pub), creds.view_key_private) elif extra_nonce and monero.has_payment_id(extra_nonce.nonce): payment_id_type = 0 payment_id = monero.get_payment_id_from_tx_extra_nonce(extra_nonce.nonce) self.assertEqual(payment_id_type, exp_payment_id[0]) self.assertEqual(payment_id, exp_payment_id[1]) # All outputs have to be successfully received self.assertEqual(num_outs, num_received)
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 ))
async def test_node_transaction(self): tx_j = pkg_resources.resource_string( __name__, os.path.join("data", "tsx_01.json")) tx_c = pkg_resources.resource_string( __name__, os.path.join("data", "tsx_01_plain.txt")) tx_u_c = pkg_resources.resource_string( __name__, os.path.join("data", "tsx_01_uns.txt")) tx_js = json.loads(tx_j.decode("utf8")) reader = xmrserialize.MemoryReaderWriter( bytearray(binascii.unhexlify(tx_c))) ar = xmrserialize.Archive(reader, False, self._get_bc_ver()) tx = xmrtypes.Transaction() await ar.message(tx) reader = xmrserialize.MemoryReaderWriter( bytearray(binascii.unhexlify(tx_u_c))) ar = xmrserialize.Archive(reader, False, self._get_bc_ver()) uns = xmrtypes.UnsignedTxSet() await ar.message(uns) # Test message hash computation tx_prefix_hash = await monero.get_transaction_prefix_hash(tx) message = binascii.unhexlify(tx_js["tx_prefix_hash"]) self.assertEqual(tx_prefix_hash, message) # RingCT, range sigs, hash rv = tx.rct_signatures rv.message = message rv.mixRing = self.mixring(tx_js) digest = await monero.get_pre_mlsag_hash(rv) full_message = binascii.unhexlify(tx_js["pre_mlsag_hash"]) self.assertEqual(digest, full_message) # Recompute missing data monero.expand_transaction(tx) # Unmask ECDH data, check range proofs for i in range(len(tx_js["amount_keys"])): ecdh = monero.copy_ecdh(rv.ecdhInfo[i]) monero.recode_ecdh(ecdh, encode=False) ecdh = ring_ct.ecdh_decode(ecdh, derivation=binascii.unhexlify( tx_js["amount_keys"][i])) self.assertEqual(crypto.sc_get64(ecdh.amount), tx_js["outamounts"][i]) self.assertTrue( crypto.sc_eq( ecdh.mask, crypto.decodeint( binascii.unhexlify(tx_js["outSk"][i])[32:]), )) C = crypto.decodepoint(rv.outPk[i].mask) rsig = rv.p.rangeSigs[i] res = ring_ct.ver_range(C, rsig) self.assertTrue(res) res = ring_ct.ver_range( crypto.point_add(C, crypto.scalarmult_base(crypto.sc_init(3))), rsig) self.assertFalse(res) is_simple = len(tx.vin) > 1 monero.recode_rct(rv, encode=False) if is_simple: for index in range(len(rv.p.MGs)): pseudo_out = crypto.decodepoint( binascii.unhexlify( tx_js["tx"]["rct_signatures"]["pseudoOuts"][index])) r = mlsag2.ver_rct_mg_simple(full_message, rv.p.MGs[index], rv.mixRing[index], pseudo_out) self.assertTrue(r) r = mlsag2.ver_rct_mg_simple(full_message, rv.p.MGs[index], rv.mixRing[index - 1], pseudo_out) self.assertFalse(r) else: txn_fee_key = crypto.scalarmult_h(rv.txnFee) r = mlsag2.ver_rct_mg(rv.p.MGs[0], rv.mixRing, rv.outPk, txn_fee_key, digest) self.assertTrue(r) r = mlsag2.ver_rct_mg( rv.p.MGs[0], rv.mixRing, rv.outPk, crypto.scalarmult_h(rv.txnFee - 100), digest, ) self.assertFalse(r)