Skip to content

Instantly share code, notes, and snippets.

@ph4r05
Last active September 13, 2021 02:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ph4r05/b95c085b101cd9c9ba5dad104dfab007 to your computer and use it in GitHub Desktop.
Save ph4r05/b95c085b101cd9c9ba5dad104dfab007 to your computer and use it in GitHub Desktop.
Fixes payment ID problem
# pip install monero_agent>=1.7.6
import binascii
import json
import requests
from monero_glue.xmr import crypto
from monero_serialize.xmrserialize import load_uvarint_b, uvarint_size
# Set your values here:
daemon_addr = 'opennode.xmr-tw.org:18089'
txid = '2331873ba8d7d2b935e2d89b36ebe2c031a2f785fc799a4fb4641385c098c66d'
viewkey = 'f27ab2ae6c9849601b15d55da305f02b6a4e373432f280a78263eb137ca36603' # viewkey command in monero-wallet-cli, the private (secret) part
def parse_payment_id(extra):
offset = 0
extra = bytearray(extra)
pub_key = None
payment_id = None
nonce = None
while offset < len(extra):
tag = extra[offset]
offset += 1
if tag == 0x1: # pubkey
pub_key = extra[offset : offset + 32]
offset += 32
elif tag == 0x2: # nonce
ln = load_uvarint_b(extra[offset:])
offset += uvarint_size(ln)
nonce = extra[offset : offset + ln]
offset += ln
elif tag == 0x4: # additionals
ln = load_uvarint_b(extra[offset:])
offset += uvarint_size(ln) + 32 * ln
else:
raise ValueError('Not Trezor transaction')
if nonce and len(nonce) == 9 and nonce[0] == 1:
payment_id = nonce[1:]
if not payment_id or not pub_key:
raise ValueError('Payment ID or pubkey not found in TX')
return pub_key, payment_id
def encrypt_payment_id(payment_id, public_key, secret_key, tag):
derivation_p = crypto.generate_key_derivation(public_key, secret_key)
derivation = bytearray(33)
derivation = crypto.encodepoint_into(derivation, derivation_p)
derivation[32] = tag # ENCRYPTED_PAYMENT_ID_TAIL
hash = crypto.cn_fast_hash(derivation)
pm_copy = bytearray(payment_id)
for i in range(8):
pm_copy[i] ^= hash[i]
return pm_copy
def fix_payment_id(daemon_addr, txid, viewkey):
res = requests.post('http://%s/gettransactions' % daemon_addr, json={"txs_hashes":[txid], "decode_as_json": True})
txjs = json.loads([x['as_json'] for x in res.json()['txs'] if x['tx_hash'] == txid][0])
extra = bytearray(txjs['extra'])
pub_bin, payment_id = parse_payment_id(extra)
pub = crypto.decodepoint(pub_bin)
viewkey_bin = binascii.unhexlify(viewkey)
viewkey_sec = crypto.decodeint(viewkey_bin)
return encrypt_payment_id(payment_id, pub, viewkey_sec, 0x8b)
if __name__ == '__main__':
fixed = fix_payment_id(daemon_addr, txid, viewkey)
print('Fixed: %s for txid %s' % (binascii.hexlify(fixed), txid))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment