From 9674aa1e9b4a340b18402eb93ab4e12890a1e1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sa=C5=82aban?= Date: Thu, 4 Feb 2021 21:40:42 +0100 Subject: [PATCH] Add tests on coinbase transactions --- monero/transaction/__init__.py | 11 +++- .../test_coinbase_no_own_output-26dcb5.json | 15 +++++ .../test_coinbase_own_output-dc0861.json | 57 +++++++++++++++++++ tests/test_outputs.py | 47 ++++++++++++++- 4 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 tests/data/test_outputs/test_coinbase_no_own_output-26dcb5.json create mode 100644 tests/data/test_outputs/test_coinbase_own_output-dc0861.json diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 058db4f..1a6dc2d 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -125,7 +125,7 @@ class Transaction(object): for outputs directed to the wallet, provided that matching subaddresses have been already generated. """ - def _scan_pubkeys(svk, psk, stealth_address): + def _scan_pubkeys(svk, psk, stealth_address, amount, encamount): for keyidx, tx_key in enumerate(self.pubkeys): hsdata = b"".join( [ @@ -153,7 +153,11 @@ class Transaction(object): continue if not encamount: # Tx ver 1 - break + return Payment( + amount=amount, + timestamp=self.timestamp, + transaction=self, + local_address=addr) amount_hs = sha3.keccak_256(b"amount" + Hs).digest() xormask = amount_hs[:len(encamount)] dec_amount = bytes(a ^ b for a, b in zip(encamount, xormask)) @@ -183,6 +187,7 @@ class Transaction(object): outs = [] for idx, vout in enumerate(self.json['vout']): stealth_address = binascii.unhexlify(vout['target']['key']) + encamount = None if self.version == 2 and not self.is_coinbase: encamount = binascii.unhexlify( self.json["rct_signatures"]["ecdhInfo"][idx]["amount"] @@ -192,7 +197,7 @@ class Transaction(object): if wallet: for addridx, addr in enumerate(addresses): psk = binascii.unhexlify(addr.spend_key()) - payment = _scan_pubkeys(svk, psk, stealth_address) + payment = _scan_pubkeys(svk, psk, stealth_address, amount, encamount) if payment: break outs.append(OneTimeOutput( diff --git a/tests/data/test_outputs/test_coinbase_no_own_output-26dcb5.json b/tests/data/test_outputs/test_coinbase_no_own_output-26dcb5.json new file mode 100644 index 0000000..b0cd3ae --- /dev/null +++ b/tests/data/test_outputs/test_coinbase_no_own_output-26dcb5.json @@ -0,0 +1,15 @@ +{ + "as_hex": "", + "as_json": "{\n \"version\": 2, \n \"unlock_time\": 766519, \n \"vin\": [ {\n \"gen\": {\n \"height\": 766459\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 8415513145431, \n \"target\": {\n \"key\": \"6c4a7168678f9d5c504e72c54d55a1cfc118cce9af944b6ecca10556541af056\"\n }\n }\n ], \n \"extra\": [ 1, 128, 93, 146, 154, 55, 1, 170, 165, 76, 172, 184, 227, 115, 121, 76, 30, 233, 193, 8, 228, 133, 96, 186, 122, 30, 54, 92, 179, 188, 229, 65, 243\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n}", + "block_height": 766459, + "block_timestamp": 1612470441, + "double_spend_seen": false, + "in_pool": false, + "output_indices": [ + 3129279 + ], + "prunable_as_hex": "", + "prunable_hash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "pruned_as_hex": "02b7e42e01fffbe32e01d7e0af9df6f401026c4a7168678f9d5c504e72c54d55a1cfc118cce9af944b6ecca10556541af0562101805d929a3701aaa54cacb8e373794c1ee9c108e48560ba7a1e365cb3bce541f300", + "tx_hash": "26dcb55c3c93a2176949fd9ec4e20a9d97ece7c420408d9353c390a909e9a7c1" +} diff --git a/tests/data/test_outputs/test_coinbase_own_output-dc0861.json b/tests/data/test_outputs/test_coinbase_own_output-dc0861.json new file mode 100644 index 0000000..228f5d2 --- /dev/null +++ b/tests/data/test_outputs/test_coinbase_own_output-dc0861.json @@ -0,0 +1,57 @@ +{ + "version": 2, + "unlock_time": 518207, + "vin": [ + { + "gen": { + "height": 518147 + } + } + ], + "vout": [ + { + "amount": 13515927959357, + "target": { + "key": "5b695861b1f0b409e1e51f0fed323fbb9dc2fe020146c060f85520e96468659d" + } + } + ], + "extra": [ + 1, + 220, + 188, + 34, + 119, + 62, + 166, + 51, + 229, + 198, + 46, + 210, + 214, + 191, + 54, + 140, + 193, + 246, + 219, + 234, + 35, + 107, + 202, + 9, + 213, + 173, + 245, + 16, + 4, + 39, + 197, + 32, + 231 + ], + "rct_signatures": { + "type": 0 + } +} diff --git a/tests/test_outputs.py b/tests/test_outputs.py index d4c1695..425adc5 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -1,13 +1,16 @@ from decimal import Decimal +import json try: from unittest.mock import patch, Mock except ImportError: from mock import patch, Mock import responses +from monero.backends.jsonrpc import JSONRPCDaemon, JSONRPCWallet +from monero.backends.offline import OfflineWallet from monero.daemon import Daemon +from monero.transaction import Transaction from monero.wallet import Wallet -from monero.backends.jsonrpc import JSONRPCDaemon, JSONRPCWallet from .base import JSONTestCase @@ -79,3 +82,45 @@ class OutputTestCase(JSONTestCase): self.assertEqual( outs[4].payment.local_address, "7BJxHKTa4p5USJ9Z5GY15ZARXL6Qe84qT3FnWkMbSJSoEj9ugGjnpQ1N9H1jqkjsTzLiN5VTbCP8f4MYYVPAcXhr36bHXzP") + + def test_coinbase_no_own_output(self): + txdata = self._read("test_coinbase_no_own_output-26dcb5.json") + tx = Transaction( + hash="26dcb55c3c93a2176949fd9ec4e20a9d97ece7c420408d9353c390a909e9a7c1", + height=766459, + output_indices=txdata["output_indices"], + json=json.loads(txdata["as_json"])) + self.assertTrue(tx.is_coinbase) + wallet = Wallet(OfflineWallet( + address="56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj", + view_key="e507923516f52389eae889b6edc182ada82bb9354fb405abedbe0772a15aea0a")) + outs = tx.outputs(wallet=wallet) + self.assertEqual(len(outs), 1) + self.assertIsNone(outs[0].payment) + self.assertEqual(outs[0].amount, Decimal("8.415513145431")) + self.assertEqual(outs[0].index, 3129279) + + def test_coinbase_own_output(self): + txdata = self._read("test_coinbase_own_output-dc0861.json") + tx = Transaction( + hash="dc08610685b8a55dc7d64454ecbe12868e4e73c766e2d19ee092885a06fc092d", + height=518147, + json=txdata) + self.assertTrue(tx.is_coinbase) + wallet = Wallet(OfflineWallet( + address="56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj", + view_key="e507923516f52389eae889b6edc182ada82bb9354fb405abedbe0772a15aea0a")) + outs = tx.outputs(wallet=wallet) + self.assertEqual(len(outs), 1) + self.assertIsNotNone(outs[0].payment) + self.assertEqual( + outs[0].payment.local_address, + "56eDKfprZtQGfB4y6gVLZx5naKVHw6KEKLDoq2WWtLng9ANuBvsw67wfqyhQECoLmjQN4cKAdvMp2WsC5fnw9seKLcCSfjj") + self.assertEqual(outs[0].amount, outs[0].payment.amount) + self.assertEqual(outs[0].payment.amount, Decimal("13.515927959357")) + + # TODO add test of v1 transaction + # TODO add test of ExtraParser initialized with str and bytes + # TODO add test of extra with TX_EXTRA_TAG_PADDING + # TODO add test of extra with TX_EXTRA_TAG_EXTRA_NONCE + # TODO add test of extra with unknown tag, e.g. 0x03