This ZIP describes a method of proving that a payment was sent to a shielded address. In the typical case, this means enabling a sender to present a proof that they transferred funds to a recipient's shielded address. The method described will be compatible with the Zcash Protocol [PROTOCOL] deployed at the launch of the Zcash network.
This document follows the common conventions defined for ZIPs in [ZIP-1].
Payment disclosure is useful in a number of situations, for example:
* A sender may need to prove that their payment was sent and received by a recipient. For example, a customer paid too much for an item and would like to claim a refund from the vendor.
* A third party needs to verify that a payment between sender and recipient was executed successfully. For example, a regulator needs to confirm a transfer of funds between two parties.
When a transaction involves only transparent addresses, the sender and recipient can verify payment by examining the blockchain. A third party can also perform this verification if they know the transparent addresses of the involved parties.
However, if the transaction involves shielded addresses, the blockchain by itself does not contain enough information from which a record of the payment can be reconstructed and verified.
Let us examine the types of transaction that might occur and when a method of payment disclosure would be useful:
transparent --> transparent
The source, destination and amount are visible on the public blockchain. Payment disclosure is not required.
transparent --> shielded
The destination address and amount sent to that address cannot be confirmed. Payment disclosure is required.
shielded --> transparent
The recipient can see the amount received at their destination address, but cannot identify the sender. Payment disclosure is required.
shielded --> shielded
The sender, recipient and amount are unknown. Payment disclosure required.
TODO: Perhap use a table to make this easier to understand, from perspective of sender,recipient and third party?
Design Overview
---------------
Allow the sender to automatically or optionally create a payment disclosure when creating a transaction involving shielded addresses.
The sender or a third party can present this payment disclosure to the recipient to prove that funds were sent to their address.
Example
-------
A group of coworkers go out for dinner and decide to split the bill. The restaurant only accepts physical cash. Alice ends up paying the entire bill as her coworkers have no cash on them.
The next day Bob sends 1 ZEC to Alice's shielded address for his share of the bill. Other coworkers do the same. A week later Alice remembers that she is owed money so she checks her shielded address. Alice sees that the balance is less than what it should be, so she asks her coworkers to confirm their payments.
Bob retrieves his payment disclosure and emails it to Alice. Alice is travelling and only has access to her smartphone. She visits a Zcash blockchain explorer and copies the payment disclosure into the search box. The explorer confirms that Bob paid his share.
Charlie attempts to retrieve the payment disclosure but can't find one. Charlie had used the command line to send his funds to Alice, but never checked the status of the z_sendmany operation. Checking the debug logs he discovers that the operation failed due to an incorrect parameter. Charlie tells Alice that he will re-send his payment.
Design Considerations
---------------------
The payment disclosure does not prove that the party presenting the payment disclosure is the sender.
To mitigate against a replay attack, the recipient could enforce a policy where the first person to present a given payment disclosure is the honest sender.
[TODO: Example?] To prevent a man-in-the-middle attack, the recipient could pose an interactive challenge involving some out-of-band secret which only the sender would pass successfully.
#. The sender includes data, such as a refund address, in the memo field of a payment to a shielded address. The memo field is non-malleable as it is part of the data signed by the sender when creating JoinSplitSig [PROTOCOL] §4.1.6 Signatures. Currently, the public key to verify the signature is stored in the blockchain along with the transaction.
#. When presenting the payment disclosure to a recepient, the sender signs the payment disclosure with the JoinSplitSig private key which only they would know. The JoinSplitSig key pair is ephemeral and normally discarded but the method described in this ZIP will retain the private portion. The recipient can then use the public key to verify the signed disclosure.
Using the JoinSplitSig key pair in this way also means that a sender can make payments without having to first provide a refund address or other details at the time of the original payment. If at a later date the sender and recipient need to confirm the payment, the sender can sign and send any data required to the recipient, who can in turn verify it.
Both plaintext outputs of a joinsplit are currently encrypted with symmetric keys derived from the same ephemeral secret key, as discusshed here: https://github.com/zcash/zcash/issues/558#issuecomment-167819936
This means that a payment disclosure which includes the ephemeral secret key intended to decipher the note ciphertext belonging to a particular joinsplit output, creates a privacy leak by also deciphering the other joinsplit output and revealing it's amount and payment address.
Note the current implementation of the ``z_sendmany`` RPC call:
- When sending from a transparent address to shielded addresses, each shielded address will occupy one output of a joinsplit. Thus a payment address to a different recipient could be revealed.
- When sending involves chained joinsplits, for each joinsplit, one output is for payment to a recipient's address, while the other output is used as change back to the sender. Thus the change address will be revealed, which is the sender's own shielded address.
A proposal to prevent information leakage of change addresses is currently under development [KDFT]. TODO: Does this block ZIP approval?
Also note that an independent third party cannot know for sure if the sender and recipient are not colluding to hide value transfer. JoinSplits have two inputs and two outputs. The recipient may identify their receiving address for one output, but not disclose the fact that the other output also belongs to them.
When a shielded transaction is created sucessfully and accepted into the local mempool with ``z_sendmany`` or ``z_shieldcoinbase``, the payment disclosure database is updated with information necessary to create a payment disclosure. However, the transaction itself may never be mined and confirmed in the blockchain, rendering the database entry itself redundant and available for purging at a later date e.g. garbage collection.
When creating a shielded transaction, for each JoinSplit output, a data structure is created to record the following fields:
- transaction id
- index [0..len-1] of JoinSplit in array of JoinSplits contained in transaction
- index [0..1] of JoinSplit output
- recipient's payment address is a shielded address ``(a_pk, pk_enc)`` [PROTOCOL] §3.1 Payment Addresses and Keys
- symmetric key used to encrypt the note, also referred to as the ephemeral secret key ``esk`` [PROTOCOL] §4.10.1 Generate a new KA (public, private) key pair ``(epk, esk)``.
- JoinSplitSig private key used to sign the JoinSplit transaction ``joinSplitPrivKey`` [PROTOCOL] §4.4 Sending Notes
The payment disclosure data should be persisted to disk or a database so it can be retrieved later.
When persisting, third party applications should expect serialization of payment disclosure to follow Zcash and upstream convention.
::
ADD_SERIALIZE_METHODS;
...
READWRITE(txid);
...
READWRITE(joinSplitPrivKey);
A new RPC call will be introduced to allow the sender to retrieve the payment disclosure data for a given shielded output index.
The sender wants the payment disclosure to be non-malleable, to prevent an attacker modifying details like the refund address. To achieve this, the sender will sign the payment disclosure with the JoinSplitSig private key and append the signature to the end of the payment disclosure data.
Otherwise an error field is returned explaining why the payment disclosure is invalid:
::
{
valid : false,
...
error : "The payment disclosuse is invalid because..."
}
Implementation
--------------
TODO: Link to commits in Github
A reference implementation will be added to zcashd as an experimental feature. To enable payment disclosure, set the following two options to true:
*`-paymentdisclosure=1`
*`-experimentalfeatures=1`
A third party trying to validate payment disclosure must track all transactions and thus enable the option:
*`-txindex=1`
In this implementation we will assume that once the feature is enabled, the node will create and store a payment disclosure for every transaction sent which involves a shielded address.
When the sender calls RPC call ``z_sendmany`` and creates a shielded transaction in ``asyncrpcoperation.cpp``:
#. retain the ephemeral joinSplitPrivKey used to sign the transaction
#. retain the ephemeral secret key used for symmetric encryption of the note plaintext
Record relevant data in a struct (or class) defined as follows:
::
struct PaymentDisclosureKey {
uint256 txid // primitives/transaction.h
size_t js; // Index into CTransaction.vjoinsplit
uint8_t n; // Index into JSDescription fields of length ZC_NUM_JS_OUTPUTS
If the sender needs to provide a payment disclosure to the recipient or a third party, the sender will use RPC call ``z_getpaymentdisclosure`` to generate a Payment Disclosure.
#. Serialize ``PaymentDisclosurePayload`` and sign the raw data using the ``joinSplitPrivKey`` to generate a signature ``payloadSig``. Sample C++ code to do this:
#. Return to the caller the hex string of the serialized PaymentDisclosure. If there were errors generating the payment disclosure, return a standard JSON-RPC error with an appropriate error message.