QIP: 13
Layer: Applications
Title: Reusable Payment Codes
Author: wizeguyy <wizeguyy+qip@quai.org>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/quainetwork/qips/wiki/Comments:QIP-0013
Status: Draft
Type: Informational
Created: 2024-05-09
License: BSD-2-Clause
This specification adapts the payment code protocol from BIP-47 for use in the dual EVM+UTXO ledger of the Quai network. We propose a "mailbox" contract, which facilitates the BIP-47 notification messages. Using the data from this contract, the sender may derive a unique set of Qi addresses belonging to the designated recipient. This enables the sender to send complicated multi-output Qi transactions to a recipient, without requiring the recipient to share a unique address for each output.
The Qi ledger has fixed denominations and prohibits address reuse within a transaction. A consequence of these two facts, is that sending money for an arbitrary value may require the sender to construct several new UTXOs. Since each UTXO must be bound to a unique address belonging to the recipient, it can become complicated for the sender to determine how many addresses to request from the recipient.
For example, if Alice wants to send 18 Qi to Bob, the construction of her transaction will require creating multiple UTXOs for Bob, which add up to 18 Qi, such as: 1x 10 Qi, 1x 5Qi, 3x Qi UTXOs. Because each UTXO must be bound to a unique address, Alice requires 5 unique addresses from Bob. This number of addresses could be much larger, depending what how easily the value divides into fixed denominations.
Requiring Alice and Bob to agree on and share several unique addresses for every transaction leads to a very unpleasant user experience. To ease this pain, we seek to use a BIP-47 style payment code protocol, whereby Bob only needs to share his notification address, which Alice may send a notification message to (via a mailbox contract on Quai), and subsequently derive as many Qi addresses as she requires to send payments to Bob.
Much of this specification is borrowed from BIP-47, but modifications have been made where necessary to accomodate Quai protocol differences.
A payment code contains the following elements:
- Byte 0: version. required value: 0x01
- Byte 1: features bit field. All bits must be zero except where specified elsewhere in this specification
- Bit 0: Bitmessage notification
- Bits 1-7: reserved
- Byte 2: sign. required value: 0x02 or 0x03
- Bytes 3 - 34: x value, must be a member of the secp256k1 group
- Bytes 35 - 66: chain code
- Bytes 67 - 79: reserved for future expansion, zero-filled unless otherwise noted
The subsequent protocol depends on the use of a mailbox contract. The recipient may deploy his own mailbox contract, or use a publicly available contract. Whichever the case, he should not change which contract he uses, without alerting his contacts. Otherwise, he runs the risk of missing payment notifications from senders who still have the old notification contract.
To avoid confusion about which mailbox contract to use, we suggest that the community reach consensus on one contract to be used by everyone. Once consensus is reached, this specification should be updated to include the recommended mailbox contract address(es).
The mailbox contract should provide the following methods:
register()
- anyone can register to receive messages via this mailbox contract- must get sender pubkey from transaction signature (ec recover)
- must create a new mailbox for this pubkey to receive payment codes
notify(recipient: Address, paycode: PaymentCode)
- the sender can leave a notification message for the recipient- must fail if sender has already notified this recipient
- creates an entry in the recipient's mailbox, containing the sender's address and the provided payment code.
getNotifications() -> [(sender, paycode), ...]
- anyone may check for notification messages in their mailbox- must return the full list of
(Address, PaymentCode)
pairs for every notification message - must not delete entries from mailbox. Mailbox messages should remain available forever.
- must return the full list of
<TBD>
<TBD>
<TBD>
In the following examples, Alice and Bob are identities with corresponding payment codes. Alice initiates a transaction, and Bob is the recipient of the transaction.
It is assumed that Alice can easily obtain Bob's payment code via a suitable method outside the scope of the payment code protocol.
- Payment code: an extended public key and associated metadata which is associated with a particular identity/account
- Mailbox contract: an agreed upon contract where Bob will check for notification messages
- Notification transaction: a transaction where Alice leaves a notification message (her payment code) in Bob's mailbox.
- Designated pubkey: the public key necessary for the other party to perform ECDH exchange and derive your addresses using your payment code.
Bob must register his payment code with the mailbox contract, so that Alice may retrieve the information necessary to start sending coins.
- Bob selects a
Prior to the first time Alice initiates a transaction to Bob, Alice MUST inform Bob of her payment code via the following procedure: Prior to transacting, Bob must share his designated pubkey with Alice.
-
Alice determine's which mailbox contract to use
- Construct Bob's address from his designated pubkey
- Determine which shard that address belongs to
- Look up the mailbox contract address corresponding to that shard.
-
Alice derives a unique shared secret using ECDH:
- Alice selects the private key corresponding to the her designated pubkey:
a
- Alice selects Bob's designated public key associated with Bob's payment code:
B, where B = bG
- Alice calculates a secret point:
S = aB
- Alice calculates a 64 byte blinding factor:
s = HMAC-SHA512(o, x)
- "x" is the x value of the secret point
- "o" is the outpoint being spent by the designated input
- Alice selects the private key corresponding to the her designated pubkey:
-
Alice serializes her payment code in binary form.
-
Alice renders her payment code (P) unreadable to anyone except Bob:
- Replace the x value with x':
x' = x XOR (first 32 bytes of s)
- Replace the chain code with c':
c' = c XOR (last 32 bytes of s)
- Replace the x value with x':
-
Alice submits a transaction to call the mailbox
notify(...)
method and leave her payment code for Bob. -
Bob watches the mailbox contract for new notification messages
-
When a notification is received, if the first byte of the payload in a notification transaction is 0x01:
- Bob selects Alice's designated pubkey from her payment code:
A, where A = aG
- Bob selects the private key associated with designated pubkey from his payment code:
b
- Bob calculates a secret point:
S = bA
- Bob calculates the blinding factor:
s = HMAC-SHA512(x, o)
- "x" is the x value of the secret point
- "o" is the outpoint being spent by the designated input.
- Bob interprets the 80 byte payload as a payment code, except:
- Replace the x value with x':
x' = x XOR (first 32 bytes of s)
- Replace the chain code with c':
c' = c XOR (last 32 bytes of s)
- Replace the x value with x':
- If the updated x value is a member of the secp256k1 group, the payment code is valid.
- If the updated x value is not a member of the secp256k1 group, the payment code is ignored.
- Bob selects Alice's designated pubkey from her payment code:
Now that Bob's client has received Alice's payment code, it is possible for Alice to send payments (up to 232 payments) to Bob.
Alice will never again need to send a notification transaction to Bob.
Note: This notification transaction plays in the EVM, meaning Alice must be able to pay EVM gas. At time of writing, that implies Alice must have Quai, even if she is only trying to set the channel up to send Qi. In the future, it may be possible to pay EVM gas with Qi, at which time it would no longer be necessary for Alice to own Quai, and she may use these payment codes entirely in Qi.
-
Each time Alice wants to initiate a transaction to Bob, Alice derives unique Qi addresses for the transaction using ECDH using the following procedure to derive each:
- Alice selects the 0th private key derived from her payment code:
a
- Alice selects the next unused public key derived from Bob's payment code, starting from zero:
B, where B = bG
, which corresponds to a Qi address in the destination chain.- The "next unused" public key is based on an index specific to the Alice-Bob context, not global to either Alice or Bob
- Alice calculates a secret point:
S = aB
- Alice calculates a scalar shared secret using the x value of S:
s = SHA256(Sx)
- If the value of s is not in the secp256k1 group, Alice MUST increment the index used to derive Bob's public key and try again.
- Alice uses the scalar shared secret to calculate the ephemeral public key used to generate the P2PKH address for this transaction:
B' = B + sG
- Alice selects the 0th private key derived from her payment code:
-
Bob is watching for incoming payments on B' ever since he received the notification message from Alice.
- Bob calculates n shared secrets with Alice, using the 0th public key derived from Alice's payment code, and private keys 0 - n derived from Bob's payment code, where n is his desired lookahead window.
- Bob calculates the ephemeral deposit addresses using the same procedure as Alice:
B' = B + sG
- Bob calculate the private key for each ephemeral address as:
b' = b + s
Because Bob learns Alice's payment code as part of the process of receiving a payment, Bob has all the information he needs in order to send a refund to Alice.
A refund transaction is identical to a payment transactions, with only the roles of the participants switches.
Bob MUST send a notification transaction to Alice prior to the first time he sends funds to Alice, even if he has received transactions from her in the past.
This QIP licensed under the BSD 2-clause license.