From 0cfdd52b688ee7da3120d6796f9588350ced4f96 Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 00:54:41 +0100 Subject: [PATCH 01/10] Update __init__.py not tested / not working - just some initial ideas from Jberman implemented. I am assuming that pre hard fork transcations will have the old json structure --- monero/transaction/__init__.py | 73 +++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index fe5f780..c6a8994 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -140,27 +140,46 @@ class Transaction(object): already generated. """ - def _scan_pubkeys(svk, psk, stealth_address, amount, encamount): + def _scan_pubkeys(svk, psk, stealth_address, amount, encamount, on_chain_vt): for keyidx, tx_key in enumerate(self.pubkeys): # precompute svk_2 = ed25519.scalar_add(svk, svk) svk_4 = ed25519.scalar_add(svk_2, svk_2) svk_8 = ed25519.scalar_add(svk_4, svk_4) # - hsdata = b"".join( + svk_8 = ed25519.scalar_add(svk_4, svk_4) + + if on_chain_vt: + shared_secret = ed25519.scalarmult(svk_8, tx_key) + vt_hsdata = b"".join([ + on_chain_vt, # need to make sure to only take first 8 bytes of this string + shared_secret, + varint.encode(idx) + ]) + vt_full = keccak_256(vt_hsdata).digest() + vt = vt_full[0] # the view tag is the first byte of vt_full + + if vt != on_chain_vt: + continue + else: + hsdata = b"".join( [ ed25519.scalarmult(svk_8, tx_key), varint.encode(idx), - ] - ) - Hs_ur = keccak_256(hsdata).digest() - Hs = ed25519.scalar_reduce(Hs_ur) - k = ed25519.edwards_add( - ed25519.scalarmult_B(Hs), - psk, - ) - if k != stealth_address: - continue + ]) + Hs_ur = keccak_256(hsdata).digest() + Hs = ed25519.scalar_reduce(Hs_ur) + k = ed25519.edwards_add( + ed25519.scalarmult_B(Hs), + psk, + ) + if k != stealth_address: + continue + hsdata = b"".join([ + shared_secret, + varint.encode(idx), + ]) + Hs_ur = keccak_256(hsdata).digest() if not encamount: # Tx ver 1 return Payment( @@ -199,9 +218,35 @@ class Transaction(object): *map(operator.methodcaller("addresses"), wallet.accounts) ) ) + ''' + pre hard fork: + { + "target": { + "key": "ea3f..." + } + } + post hard fork: + { + "target": { + "tagged_key": { + "key": "ea3f...", + "view_tag": "a1" + } + } + } + ''' outs = [] for idx, vout in enumerate(self.json["vout"]): - stealth_address = binascii.unhexlify(vout["target"]["key"]) + #see if post hard fork json structure present: + try: + if vout["target"]["tagged_key"]: + #post fork transaction + stealth_address = binascii.unhexlify(vout["target"]["tagged_key"]["key"]) + on_chain_vt = binascii.unhexlify(vout["target"]["tagged_key"]["view_tag"]) + except: + #pre fork transaction + stealth_address = binascii.unhexlify(vout["target"]["key"]) + on_chain_vt = False encamount = None if self.version == 2 and not self.is_coinbase: encamount = binascii.unhexlify( @@ -217,7 +262,7 @@ class Transaction(object): for addridx, addr in enumerate(addresses): psk = binascii.unhexlify(addr.spend_key()) payment = _scan_pubkeys( - svk, psk, stealth_address, amount, encamount + svk, psk, stealth_address, amount, encamount, on_chain_vt ) if payment: break From 11a89cc7ff38957d3b117b29fbb6c2d1f0edc305 Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 00:57:42 +0100 Subject: [PATCH 02/10] "view_tag" --- monero/transaction/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index c6a8994..62bf097 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -152,7 +152,7 @@ class Transaction(object): if on_chain_vt: shared_secret = ed25519.scalarmult(svk_8, tx_key) vt_hsdata = b"".join([ - on_chain_vt, # need to make sure to only take first 8 bytes of this string + "view_tag", # need to make sure to only take first 8 bytes of this string shared_secret, varint.encode(idx) ]) From c9848d45d14267ec97ec29882463204f3f3d8dbf Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 01:11:43 +0100 Subject: [PATCH 03/10] updates from jberman --- monero/transaction/__init__.py | 40 +++++++++++++++------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 62bf097..1258577 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -151,14 +151,12 @@ class Transaction(object): if on_chain_vt: shared_secret = ed25519.scalarmult(svk_8, tx_key) - vt_hsdata = b"".join([ - "view_tag", # need to make sure to only take first 8 bytes of this string - shared_secret, - varint.encode(idx) + vt_hsdata = b"view_tag" + b"".join([ + shared_secret, + varint.encode(idx) ]) vt_full = keccak_256(vt_hsdata).digest() - vt = vt_full[0] # the view tag is the first byte of vt_full - + vt = binascii.hexlify(vt_full)[0:2] if vt != on_chain_vt: continue else: @@ -174,12 +172,7 @@ class Transaction(object): psk, ) if k != stealth_address: - continue - hsdata = b"".join([ - shared_secret, - varint.encode(idx), - ]) - Hs_ur = keccak_256(hsdata).digest() + continue if not encamount: # Tx ver 1 return Payment( @@ -238,15 +231,17 @@ class Transaction(object): outs = [] for idx, vout in enumerate(self.json["vout"]): #see if post hard fork json structure present: - try: - if vout["target"]["tagged_key"]: - #post fork transaction - stealth_address = binascii.unhexlify(vout["target"]["tagged_key"]["key"]) - on_chain_vt = binascii.unhexlify(vout["target"]["tagged_key"]["view_tag"]) - except: - #pre fork transaction - stealth_address = binascii.unhexlify(vout["target"]["key"]) - on_chain_vt = False + try: + if vout["target"]["tagged_key"]: + #post fork transaction + stealth_address = binascii.unhexlify(vout["target"]["tagged_key"]["key"]) + on_chain_vt = binascii.unhexlify(vout["target"]["tagged_key"]["view_tag"]) + except: + #pre fork transaction + stealth_address = binascii.unhexlify(vout["target"]["key"]) + on_chain_vt = False + pass + encamount = None if self.version == 2 and not self.is_coinbase: encamount = binascii.unhexlify( @@ -279,8 +274,7 @@ class Transaction(object): def __repr__(self): return self.hash - - + class Output(object): """ A Monero one-time public output (A.K.A stealth address). From 0d898467a9d4a897aaec147b28a3c1583612be8b Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 01:17:44 +0100 Subject: [PATCH 04/10] Update __init__.py --- monero/transaction/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 1258577..530bf0d 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -147,11 +147,10 @@ class Transaction(object): svk_4 = ed25519.scalar_add(svk_2, svk_2) svk_8 = ed25519.scalar_add(svk_4, svk_4) # - svk_8 = ed25519.scalar_add(svk_4, svk_4) - if on_chain_vt: shared_secret = ed25519.scalarmult(svk_8, tx_key) - vt_hsdata = b"view_tag" + b"".join([ + vt_hsdata = b"".join([ + b"view_tag", shared_secret, varint.encode(idx) ]) From cea6f6f6d1e6e19c76581fcecf2a5c8b50f0a164 Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 01:20:52 +0100 Subject: [PATCH 05/10] Update __init__.py --- monero/transaction/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 530bf0d..9714c1a 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -230,16 +230,16 @@ class Transaction(object): outs = [] for idx, vout in enumerate(self.json["vout"]): #see if post hard fork json structure present: - try: - if vout["target"]["tagged_key"]: - #post fork transaction - stealth_address = binascii.unhexlify(vout["target"]["tagged_key"]["key"]) - on_chain_vt = binascii.unhexlify(vout["target"]["tagged_key"]["view_tag"]) - except: - #pre fork transaction - stealth_address = binascii.unhexlify(vout["target"]["key"]) - on_chain_vt = False - pass + try: + if vout["target"]["tagged_key"]: + #post fork transaction + stealth_address = binascii.unhexlify(vout["target"]["tagged_key"]["key"]) + on_chain_vt = binascii.unhexlify(vout["target"]["tagged_key"]["view_tag"]) + except: + #pre fork transaction + stealth_address = binascii.unhexlify(vout["target"]["key"]) + on_chain_vt = False + pass encamount = None if self.version == 2 and not self.is_coinbase: From 4de5a3fc8fe4d2e53abacc3014078c5b1af4ba0c Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 01:29:22 +0100 Subject: [PATCH 06/10] stealth_address=stealth_address --- monero/transaction/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 9714c1a..cc476d4 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -262,7 +262,7 @@ class Transaction(object): break outs.append( Output( - stealth_address=vout["target"]["key"], + stealth_address=stealth_address, amount=payment.amount if payment else amount, index=self.output_indices[idx] if self.output_indices else None, transaction=self, From a431eb5af1f171f756ba5c48f449de4413a4aff1 Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 02:10:16 +0100 Subject: [PATCH 07/10] Update __init__.py --- monero/transaction/__init__.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index cc476d4..5cdb432 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -157,21 +157,23 @@ class Transaction(object): vt_full = keccak_256(vt_hsdata).digest() vt = binascii.hexlify(vt_full)[0:2] if vt != on_chain_vt: + #short-circuit so it doesn't have to do the rest of this for ~99.6% of outputs + #the view tag check yields false positives 1/256 times, because it's just 1 byte continue - else: - hsdata = b"".join( - [ - ed25519.scalarmult(svk_8, tx_key), - varint.encode(idx), - ]) - Hs_ur = keccak_256(hsdata).digest() - Hs = ed25519.scalar_reduce(Hs_ur) - k = ed25519.edwards_add( - ed25519.scalarmult_B(Hs), - psk, - ) - if k != stealth_address: - continue + + hsdata = b"".join( + [ + ed25519.scalarmult(svk_8, tx_key), + varint.encode(idx), + ]) + Hs_ur = keccak_256(hsdata).digest() + Hs = ed25519.scalar_reduce(Hs_ur) + k = ed25519.edwards_add( + ed25519.scalarmult_B(Hs), + psk, + ) + if k != stealth_address: + continue if not encamount: # Tx ver 1 return Payment( From 65c18787da4de4ae9b696fce8139b313abdd6e66 Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 02:28:45 +0100 Subject: [PATCH 08/10] Update __init__.py --- monero/transaction/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 5cdb432..9bafeb2 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -147,15 +147,15 @@ class Transaction(object): svk_4 = ed25519.scalar_add(svk_2, svk_2) svk_8 = ed25519.scalar_add(svk_4, svk_4) # + shared_secret = ed25519.scalarmult(svk_8, tx_key) if on_chain_vt: - shared_secret = ed25519.scalarmult(svk_8, tx_key) vt_hsdata = b"".join([ b"view_tag", shared_secret, varint.encode(idx) ]) vt_full = keccak_256(vt_hsdata).digest() - vt = binascii.hexlify(vt_full)[0:2] + vt = vt_full[0:1] if vt != on_chain_vt: #short-circuit so it doesn't have to do the rest of this for ~99.6% of outputs #the view tag check yields false positives 1/256 times, because it's just 1 byte @@ -163,7 +163,7 @@ class Transaction(object): hsdata = b"".join( [ - ed25519.scalarmult(svk_8, tx_key), + shared_secret, varint.encode(idx), ]) Hs_ur = keccak_256(hsdata).digest() From e8ba5dcdb4ca23684f3478c501620d7a0962af02 Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Thu, 21 Apr 2022 11:54:21 +0100 Subject: [PATCH 09/10] Update __init__.py --- monero/transaction/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 9bafeb2..556bb42 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -173,7 +173,7 @@ class Transaction(object): psk, ) if k != stealth_address: - continue + continue if not encamount: # Tx ver 1 return Payment( From 106d6f3bd423486824991378cba2f39b7ba36d35 Mon Sep 17 00:00:00 2001 From: plowsof <77655812+plowsof@users.noreply.github.com> Date: Fri, 22 Apr 2022 04:05:43 +0100 Subject: [PATCH 10/10] pass original type stealth address --- monero/transaction/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 556bb42..6afc7d9 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -236,10 +236,12 @@ class Transaction(object): if vout["target"]["tagged_key"]: #post fork transaction stealth_address = binascii.unhexlify(vout["target"]["tagged_key"]["key"]) + orig_stealth_address = vout["target"]["tagged_key"]["key"] on_chain_vt = binascii.unhexlify(vout["target"]["tagged_key"]["view_tag"]) except: #pre fork transaction stealth_address = binascii.unhexlify(vout["target"]["key"]) + orig_stealth_address = vout["target"]["key"] on_chain_vt = False pass @@ -264,7 +266,7 @@ class Transaction(object): break outs.append( Output( - stealth_address=stealth_address, + stealth_address=orig_stealth_address, amount=payment.amount if payment else amount, index=self.output_indices[idx] if self.output_indices else None, transaction=self,