An Introduction to Payment Disclosure in Zcash

Gareth Davies
5 min readNov 27, 2017


Zcash is a privacy-focussed decentralised cryptocurrency. Whereas in Bitcoin all transactions are public on the blockchain, so anyone can see and verify them, in Zcash when using shielded transactions, all amounts in addition to the sender and receiver are encrypted, and verification is completed through the use of zero-knowledge proofs.

While this offers excellent privacy and anonymity properties as compared to Bitcoin what if you need to prove to someone that a specific transaction was indeed made? For example, consider the simple case of a user paying for an item on a merchant’s website and the merchant calling into question that the transaction was actually received. This could be seen as a major drawback towards the wider adoption of shielded transactions if there is no achievable way of resolving such disputes.

Fortunately, the latest version of Zcash (1.0.13) introduces payment disclosure which is a method of proving that a payment was sent to a shielded address without revealing any additional information about the user sending the funds. As the sender of the funds needs to generate this proof, and as it is entirely optional, it is also known as “selective disclosure”. Crucially, the proof generated may be verified by anyone who has access to the proof, such as a third party or an auditor and not only by the recipient of the transaction.

For the remainder of the article, we will walk through the process of generating a shielded transaction and then produce the corresponding payment disclosure, which we will verify.


To generate or validate payment disclosures you will need to be running a full Zcash node and at least version 1.0.13 of the software (which is the current version at the time of writing). As payment disclosure is currently an experimental feature, it will only work for nodes that opt-into the feature via launching zcashd with the respective flags enabled (you may also add these options to the zcash.conf file).

zcashd --daemon -experimentalfeatures -paymentdisclosure -debug=paymentdisclosure -txindex=1

Importantly, payment disclosure will only work from the time that payment disclosure is enabled, i.e. it is not possible to generate payment verifications for prior shielded transactions. If you are currently running a node without the -txindex flag then enabling it, as is presently required, will require resynching the entire blockchain which will naturally take some time to complete. Once the node has payment disclosure enabled it is time to generate a shielded transaction to use to demonstrate the payment disclosure functionality.

Creating the shielded transaction

For the sake of this article, I will simply send between two accounts on my node. I already have a shielded address with some funds at zcTZUfdQYM2dL3n1QMXDrBTkw2bsGMCsrm6epdZgB61Bd8CFAxZAVovn2KUUtXRhDwZvh3v9pdVHZmGRvbqtB7bd8LLgMuE, which is verifiable on my profile. I will save this address to a variable called $SENDER to represent the sender of the shielded transaction.

$ SENDER='zcTZUfdQYM2dL3n1QMXDrBTkw2bsGMCsrm6epdZgB61Bd8CFAxZAVovn2KUUtXRhDwZvh3v9pdVHZmGRvbqtB7bd8LLgMuE'

We need a second address to send to, which may be achieved with the command: zcash-cli z_getnewaddress. We’ll save this address to a variable named $MERCHANT to represent the imaginary merchant in this example to whom I’ll be sending some funds.

$ MERCHANT='zcPXW5q8RiF6D6NK4VZDp4kXYWagQ3KTnb7etYxroBBnD3gefZwmNdL7nTmCj3g2y3voQJq9ZY8p9JBHbK6HeDsnsziqW3h'

Finally, I’ll send an arbitrary 0.02 ZEC from my $SENDER account to this $MERCHANT account using z_sendmany and include the default fee (0.0001). Any change generated by the transaction will be returned to the $SENDER address.

zcash-cli z_sendmany "$SENDER" "[{\"address\": \"$MERCHANT\", \"amount\": 0.02}]"

It will take a minute or so for this transaction to complete. Notably also included in the 1.0.13 release is low memory proving which makes it possible to perform the z_sendmany operation with just 1.7 GB of RAM (down from the 3GB previously required). Using the command zcash-cli z_getoperationresult will return the result of the operation once complete. This result contains the txid that we will need later in the process. You can take that txid and check the public contents via the block explorer here. As expected it doesn’t reveal anything other than the transaction fee, which is always public.

"id": "opid-a07067e8-fcf9-4b47-85d0-767bddf063d5",
"status": "success",
"creation_time": 1511389423,
"result": {
"txid": "290c94d6ae8739b6f611c61cc4851bec5fa46a341cabcad67f5ac80c51ebef3e"
"execution_secs": 116.398691478,
"method": "z_sendmany",
"params": {
"fromaddress": "zcTZUfdQYM2dL3n1QMXDrBTkw2bsGMCsrm6epdZgB61Bd8CFAxZAVovn2KUUtXRhDwZvh3v9pdVHZmGRvbqtB7bd8LLgMuE",
"amounts": [
"address": "zcPXW5q8RiF6D6NK4VZDp4kXYWagQ3KTnb7etYxroBBnD3gefZwmNdL7nTmCj3g2y3voQJq9ZY8p9JBHbK6HeDsnsziqW3h",
"amount": 0.02
"minconf": 1,
"fee": 0.0001

Creating the Payment Disclosure

Now that we have our shielded transaction, should our imaginary merchant dispute this transaction, we can generate a payment disclosure to prove that it was indeed sent. To generate a payment disclosure, we need to follow the following syntax:

z_getpaymentdisclosure txid js_index output_index (message)

Unfortunately according to the documentation there is no simple way to get hold of the js_index or output_index we need to perform the payment disclosure and we need to extract this information from the debug.log file which may be found in the default zcash directory. Tailing the last few lines of that file tail -20 debug.log we can see two lines that contain the information we require:

2017-11-22 22:25:39 opid-a07067e8-fcf9-4b47-85d0-767bddf063d5: Payment Disclosure: js=0, n=0, zaddr=zcPXW5q8RiF6D6NK4VZDp4kXYWagQ3KTnb7etYxroBBnD3gefZwmNdL7nTmCj3g2y3voQJq9ZY8p9JBHbK6HeDsnsziqW3h
2017-11-22 22:25:39 opid-a07067e8-fcf9-4b47-85d0-767bddf063d5: Payment Disclosure: js=0, n=1, zaddr=zcTZUfdQYM2dL3n1QMXDrBTkw2bsGMCsrm6epdZgB61Bd8CFAxZAVovn2KUUtXRhDwZvh3v9pdVHZmGRvbqtB7bd8LLgMuE

The address of the merchant is zcPXW5q8RiF6D6NK4VZDp4kXYWagQ3KTnb7etYxroBBnD3gefZwmNdL7nTmCj3g2y3voQJq9ZY8p9JBHbK6HeDsnsziqW3h so we can see this transaction corresponds to js=0 and n=0 which are the values we require. The other transaction is to the sender address and as previously outlined is the change address, which when using shielded transactions is to the sender address as there are no privacy concerns over address reuse as in Bitcoin. The order in which these appear is not defined so there is currently no way of determining which values to use without reviewing the debug log.

Now we have everything we need we can finally construct the payment disclosure using the txid retrieved earlier.

zcash-cli z_getpaymentdisclosure 290c94d6ae8739b6f611c61cc4851bec5fa46a341cabcad67f5ac80c51ebef3e 0 0 "I am disclosing something to you!"

This returns a payment disclosure in the form of a hex string:


We can send this to the merchant or say a third-party arbiter to prove that we indeed sent 0.02 ZEC to this shielded address. For the sake of this article, we’ll also validate the payment disclosure on the same machine, but any node running with the payment disclosure options enabled will be able to validate this.

To validate we simply provide the hexstring generated previously in the following command:

z_validatepaymentdisclosure 706462ff004563c8d7e91bc2c58d79fede03e119de752c6b3b45ddfd79ea605c9de48430413eefeb510cc85a7fd6caab1c346aa45fec1b85c41cc611f6b63987aed6940c2900000000000000000074f322dbe2c5faeefb47512aded0551f4daf71095ee81fd02e421da2e74ab26d152f12ad9e4b325dbe886f2f6deef659463ce7f87ffc5168aea88d3253aa2f2c214920616d20646973636c6f73696e6720736f6d657468696e6720746f20796f752165d967eacccb3b85ed6c1c799294225c5c3d2376eabb11c0bad4842cb49d65d1a2adfb32642178cabb867e1a7c3d1c86267d5bada4a804b8d44fcd0257c8d207

This command returns the data related to the payment that we require to prove our payment.

"txid": "290c94d6ae8739b6f611c61cc4851bec5fa46a341cabcad67f5ac80c51ebef3e",
"jsIndex": 0,
"outputIndex": 0,
"version": 0,
"onetimePrivKey": "413084e49d5c60ea79fddd453b6b2c75de19e103defe798dc5c21be9d7c86345",
"message": "I am disclosing something to you!",
"joinSplitPubKey": "e9baefe52c63c07275822a8c58ecb9cb6ec268e707a208d0e40194670824bcaf",
"signatureVerified": true,
"paymentAddress": "zcPXW5q8RiF6D6NK4VZDp4kXYWagQ3KTnb7etYxroBBnD3gefZwmNdL7nTmCj3g2y3voQJq9ZY8p9JBHbK6HeDsnsziqW3h",
"memo": "f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"value": 0.02000000,
"commitmentMatch": true,
"valid": true

Note how all that we have revealed is that the same private key that performed the transaction has generated this payment disclosure via signatureVerified being true and that 0.02 ZEC was sent to the address in question a shielded transaction. Absolutely no other information is disclosed including even the sender address.


Payment disclosure is a much-anticipated feature in Zcash which will facilitate greater usability of shielded transactions. Currently it is an experimental feature and so there are serious limitations on how it is implemented, most notably the requirement to extract the values for js_index and output_index from the debug log.

As previously stated payment disclosures do not work retroactively as it requires caching data about a transaction that is otherwise ephemeral. It is assumed that eventually payment disclosure will be enabled by default and ready for any time the user needs it.

There is some further discussion on this topic in the Zcash forum



Gareth Davies

Technical writer, data wrangler and (former) full stack dev