diff --git a/monero/transaction/__init__.py b/monero/transaction/__init__.py index 117300e..f334fe8 100644 --- a/monero/transaction/__init__.py +++ b/monero/transaction/__init__.py @@ -140,16 +140,30 @@ class Transaction(object): already generated. """ - def _scan_pubkeys(svk, psk, stealth_address, amount, encamount, commitment): + def _scan_pubkeys( + svk, psk, stealth_address, amount, encamount, commitment, 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) # + shared_secret = ed25519.scalarmult(svk_8, tx_key) + if on_chain_vt: + vt_hsdata = b"".join( + [b"view_tag", shared_secret, varint.encode(idx)] + ) + vt_full = keccak_256(vt_hsdata).digest() + 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 + continue + hsdata = b"".join( [ - ed25519.scalarmult(svk_8, tx_key), + shared_secret, varint.encode(idx), ] ) @@ -207,9 +221,43 @@ 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"] + ) + 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 + encamount = None commitment = None if self.version == 2 and not self.is_coinbase: @@ -229,13 +277,19 @@ class Transaction(object): for addridx, addr in enumerate(addresses): psk = binascii.unhexlify(addr.spend_key()) payment = _scan_pubkeys( - svk, psk, stealth_address, amount, encamount, commitment + svk, + psk, + stealth_address, + amount, + encamount, + commitment, + on_chain_vt, ) if payment: break outs.append( Output( - stealth_address=vout["target"]["key"], + 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,