I had 2 quick ques. I'm trying to use the monero PHP library to decrypt a transaction pulled from a block explorer so that I can avoid sending my private view key to 3rd parties.

(We'd add a box for the user to submit their own transaction IDs after paying, and store admins can load txids from their own RPC wallets every 2 days or so since our script won't be checking every new transaction. But this way we wouldn't have to set up an RPC client on public facing servers.) We'd only be pulling encrypted transactions from 3rd parties, and ideally (optionally) we'd like to have some way of knowing, in addition to the transaction being ours and the amount, being able to validate the transaction signature and being able to validate its inclusion in a block. Setting up additional lservers to run all these RPCs properly added a lot of project complexity; A third party at worst would only have the list of addresses we checked but wouldn't know whether we were able to decrypt it.

use MoneroIntegrations\MoneroPhp\Cryptonote;

use MoneroIntegrations\MoneroPhp\ed25519;






$cn = new Cryptonote();

$tx = json_decode(file_get_contents('https://moneroblocks.info/api/get_transaction_data/insert txid here'), true);

$extra = implode(array_map(function($x){ return str_pad(dechex($x), 2, '0', STR_PAD_LEFT); }, $tx['transaction_data']['extra']));

$derived = $cn->gen_key_derivation($cn->txpub_from_extra($extra), 'insert private view key here');

if( $cn->derive_public_key($derived, 'transaction ioutput index', 'insert public spend key here') == $tx['transaction_data']['vout'][0]['target']['key']){

`$sec1 = $cn->derivation_to_scalar($derived, 'transaction ioutput index');`

`$sec2 = $cn->hash_to_scalar($sec1);`

`$ec = new ed25519();`

`echo bcsub($ec->decodeint(hex2bin($tx['transaction_data']['rct_signatures']['ecdhInfo'][0]['mask'])),  $ec->decodeint(hex2bin($sec1)));`

`echo bcsub($ec->decodeint(hex2bin($tx['transaction_data']['rct_signatures']['ecdhInfo'][0]['amount'])),  $ec->decodeint(hex2bin($sec2)));`


This sample code given a public spend key/private view key/transaction ID, checks whether any output belongs to us.

1- How are subaddresses handled? From this code so far my best guess is just looping through the public spend keys of all subaddresses but I assume that isn't correct.

2- How do I decode amounts? It seems like there's a few variations of it, I'd want to support both the 64 character long mask/amount as well as the 16 character long amount; most examples I've seen seem to be incomplete or missing portions of the code.

3- Is there a good way to sanity check the received transactions? (so we limit the 3rd party API to only possibly omitting transactions, but make sure they aren't able to send fake transactions). In Bitcoin both of these checks are easy to implement using the merkle hash system, and EC signatures likewise easy to validate.

4- Is there a proper anonymous light wallet API I could use to just query for the encrypted tx info from the network? similar to the Electrum rpc API for Bitcoin.

  • 1
    You can rent a VPS for $20/year to run a wallet on, and run a script on that machine to call a URL on your webapp server whenever it sees an incoming transaction to your wallet. It makes no sense to try and reimplement wallet functionality yourself, especially because Monero will continue to change and you will find it difficult to keep up. The answers to many of your questions are already on stackexchange and in the Zero-to-Monero guide.
    – knaccc
    Feb 13, 2020 at 12:58
  • the client is meant to work with 1 wallet. trying to have it work with multiple users who all have different keys automatically isn't much easier than reimplementing specific features, having tried both with subaddresses. however if we used the client, instead of just xor8(keccak() which is instantaneous we instead have to wait for the client to load the full wallet, even if we're only interested in 1 transaction. most of the hard forks deal with signatures/network protocols right? ecdhInfo has only changed once, subaddress keys haven't changed at all, assuming merkle tree is the same.
    – ieatpizza
    Feb 14, 2020 at 10:00
  • You should be letting the wallet validate the transactions properly, to avoid being the target of an attack. In addition, in the future, there might be an extra validation for the Janus attack, which you'd also have to understand and implement if you're not using a wallet to do that. You can probably very easily scale to at least tens of thousands of merchants by running lots of wallet processes simultaneously on the same cloud box (a standard one, not a cheap $20 one). Also look into the --tx-notify command, which you can use to ask each wallet instance to notify you on a tx received.
    – knaccc
    Feb 14, 2020 at 12:47

1 Answer 1


To answer your specific questions:

  1. The example code you posted is not handling subaddresses. It is not even correctly parsing tx_extra.
  2. xor8(e, keccak("amount"||Hs(8aR||i)) where a is the private view key, R is the tx public key, i is the output index, Hs is hash to scalar and xor8 will XOR the first 8 bytes of each parameter.
  3. I'm unsure what you would want to "sanity" check. If a transaction is on the blockchain, it is valid. Whether or not it's yours or pays the correct amount to you is a different matter.
  4. https://github.com/mymonero

As @knaccc points out in his comment though, "It makes no sense to try and reimplement wallet functionality yourself". You really should just run the monero-wallet-rpc if you need to programmatically check/make transactions - that is precisely what it is built for, it is part of the official releases and therefore will always stay current and work.

  • 1- This was the closest I could find to a working recent example that at least told me whether the output was mine correctly. Is there any better version? 2- thanks. is it the same thing for the longer amount values?
    – ieatpizza
    Feb 14, 2020 at 7:24
  • 3- just to make sure the values from the block explorer are valid. without actually downloading the entire chain we can't validate that it's on the chain. in Bitcoin, light wallets can download proof of work transaction proofs for particular transactions in the form of merkle proofs for a transaction, then can be assured that that given transaction is confirmed if the merkle proof is valid. in this case the actual "checking" is done by miners, who have the latest updates & are motivated by block rewards. i know similar mechanisms exist in other altcoins, wanted to see if its possible in xmr.
    – ieatpizza
    Feb 14, 2020 at 9:08
  • i'm not trying to re-implement large portions of the client. i don't even think the client is actually able to do this particular feature without a ton of work. i'm trying to write a function that given a particular transaction/viewkey/spendkey returns which if any vouts belong to that given viewkey/spendkey, and corresponding amounts. the closest you can do with the client is import as an account, wait for scanning, etc. and making it work with hundreds of wallet profiles. in comparison every ecdhDecode example i've seen is 1-2 lines long, i just want it to work with both address formats.
    – ieatpizza
    Feb 14, 2020 at 9:42
  • 4- doesn't mymonero require the private viewkey sent to the server?
    – ieatpizza
    Feb 14, 2020 at 9:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.