async def load_unsigned_tx(priv_key, data): """ Loads unsigned transaction from the encrypted file :param priv_key: :param data: :return: """ magic_len = len(UNSIGNED_TX_PREFIX) magic = data[:magic_len - 1] version = int(data[magic_len - 1]) data = data[magic_len:] if magic != UNSIGNED_TX_PREFIX[:-1]: raise ValueError("Invalid file header") if version != 4: raise ValueError("Unsigned transaction v4 is supported only") tx_uns_ser = chacha.decrypt_xmr(priv_key, data, authenticated=True) reader = xmrserialize.MemoryReaderWriter(bytearray(tx_uns_ser)) ar = xmrboost.Archive(reader, False) msg = xmrtypes.UnsignedTxSet() await ar.root() await ar.message(msg) return msg
async def tx_sign_unsigned(self, unsigned_tx, fl=None): """ Tx sign test with given unsigned transaction data :param unsigned_tx: :param fl: :return: """ reader = xmrserialize.MemoryReaderWriter(bytearray(unsigned_tx)) ar = xmrserialize.Archive(reader, False) unsig = xmrtypes.UnsignedTxSet() await ar.message(unsig) await self.tx_sign_unsigned_msg(unsig, fl)
async def tx_sign_unsigned(self, unsigned_tx, fl=None): """ Tx sign test with given unsigned transaction data :param unsigned_tx: :param fl: :return: """ self.skipTest('HP <= 8 not supported anymore') reader = xmrserialize.MemoryReaderWriter(bytearray(unsigned_tx)) ar = xmrserialize.Archive(reader, False, self._get_bc_ver()) unsig = xmrtypes.UnsignedTxSet() await ar.message(unsig) await self.tx_sign_unsigned_msg(unsig, fl)
async def tx_sign(self, unsigned_tx): """ Tx sign test with given unsigned transaction data :param unsigned_tx: :return: """ reader = xmrserialize.MemoryReaderWriter(bytearray(unsigned_tx)) ar = xmrserialize.Archive(reader, False) unsig = xmrtypes.UnsignedTxSet() await ar.message(unsig) tagent = self.init_agent() await tagent.transfer_unsigned(unsig)
async def tx_sign_unsigned_boost(self, unsigned_tx, fl=None): """ Tx sign test with given unsigned transaction data, serialized by boost - unsigned tx produced by watch-only cli wallet. :param unsigned_tx: :param fl: :return: """ reader = xmrserialize.MemoryReaderWriter(bytearray(unsigned_tx)) ar = xmrboost.Archive(reader, False) unsig = xmrtypes.UnsignedTxSet() await ar.root() await ar.message(unsig) await self.tx_sign_unsigned_msg(unsig, fl)
async def store_cdata(self, cdata, signed_tx, tx, transfers): """ Stores transaction data for later usage. - cdata.enc_salt1, cdata.enc_salt2, cdata.enc_keys - tx_keys are AEAD protected, key derived from spend key - only token can open. - construction data for further proofs. :param cdata: :param signed_tx: :param tx: :param transfers: :return: """ hash = cdata.tx_prefix_hash prefix = binascii.hexlify(hash[:12]) tx_key_salt = crypto.random_bytes(32) tx_key_inp = hash + crypto.encodeint(self.priv_view) tx_view_key = crypto.pbkdf2(tx_key_inp, tx_key_salt, 2048) unsigned_data = xmrtypes.UnsignedTxSet() unsigned_data.txes = [tx] unsigned_data.transfers = transfers if transfers is not None else [] writer = xmrserialize.MemoryReaderWriter() ar = xmrboost.Archive(writer, True) await ar.root() await ar.message(unsigned_data) unsigned_key = crypto.keccak_2hash(b'unsigned;' + tx_view_key) ciphertext = chacha_poly.encrypt_pack(unsigned_key, bytes(writer.get_buffer())) # Serialize signed transaction writer = xmrserialize.MemoryReaderWriter() ar = xmrserialize.Archive(writer, True) await ar.root() await ar.message(signed_tx) signed_tx_bytes = writer.get_buffer() signed_tx_hmac_key = crypto.keccak_2hash(b'hmac;' + tx_view_key) signed_tx_hmac = crypto.compute_hmac(signed_tx_hmac_key, signed_tx_bytes) try: js = { "time": int(time.time()), "hash": binascii.hexlify(hash).decode("ascii"), "enc_salt1": binascii.hexlify(cdata.enc_salt1).decode("ascii"), "enc_salt2": binascii.hexlify(cdata.enc_salt2).decode("ascii"), "tx_keys": binascii.hexlify(cdata.enc_keys).decode("ascii"), "unsigned_data": binascii.hexlify(ciphertext).decode("ascii"), "tx_salt": binascii.hexlify(tx_key_salt).decode("ascii"), "tx_signed": binascii.hexlify(signed_tx_bytes).decode("ascii"), "tx_signed_hmac": binascii.hexlify(signed_tx_hmac).decode("ascii"), } with open("transaction_%s.json" % prefix.decode("ascii"), "w") as fh: json.dump(js, fh, indent=2) fh.write("\n") except Exception as e: self.trace_logger.log(e) print("Unable to save transaction data for transaction %s" % binascii.hexlify(hash).decode("ascii"))
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 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)