diff --git a/src/RFC-0385_StableCoins.md b/src/RFC-0385_StableCoins.md index 226018e..874245d 100644 --- a/src/RFC-0385_StableCoins.md +++ b/src/RFC-0385_StableCoins.md @@ -4,7 +4,7 @@ ![status: draft](theme/images/status-draft.svg) -**Maintainer(s)**: [Cayle Sharrock](https://github.com/CjS77) +**Maintainer(s)**: [Cayle Sharrock](https://github.com/CjS77), [Aaron Feickert](https://github.com/AaronFeickert) # Licence @@ -32,9 +32,10 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## Language -The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", -"NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in -[BCP 14](https://tools.ietf.org/html/bcp14) (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", +"NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in +[BCP 14](https://tools.ietf.org/html/bcp14) (covering RFC2119 and RFC8174) when, and only when, they appear in all +capitals, as shown here. ## Disclaimer @@ -48,87 +49,95 @@ technological merits of the potential system outlined herein. ## Goals -This Request for Comment (RFC) describes the a possible manifestation of a privacy-preserving -stablecoin on the Tari Digital Assets Network (DAN). +This Request for Comment (RFC) describes the a possible manifestation of a privacy-preserving +stablecoin on the Tari Digital Assets Network (DAN). ## Related Requests for Comment ## Evaluation of existing stablecoins -The top two stablecoins by issuance, or "total value locked" (TVL) are Tether USD (USDT, under various contracts) and +The top two stablecoins by issuance, or "total value locked" (TVL) are Tether USD (USDT, under various contracts) and USD Coin (USDC). -As of August 2023, these two coins accounted for [87% of the total stablecoin market](https://defillama.com/stablecoins). +As of August 2023, these two coins accounted +for [87% of the total stablecoin market](https://defillama.com/stablecoins). -Both coins are fully collateralised and the peg is maintained by the centralised issuer. +Both coins are fully collateralised and the peg is maintained by the centralised issuer. -Although Tether is under scrutiny by authorities, both stablecoins have been in operation for several years. One -might reasonably assume that the intersection of the feature set of the two coins' contracts represent a minimal set of +Although Tether is under scrutiny by authorities, both stablecoins have been in operation for several years. One +might reasonably assume that the intersection of the feature set of the two coins' contracts represent a minimal set of requirements for legal operation. What follows is a brief summary of the features of the two coins. ### Tether USD (USDT) -The Tether USD ERC-20 contract is deployed at address +The Tether USD ERC-20 contract is deployed at address [`0xdac17f958d2ee523a2206206994597c13d831ec7`](https://etherscan.io/address/0xdAC17F958D2ee523a2206206994597C13D831ec7). -The contract code for this contract is presented in [Appendix A](#appendix-a---tether-usd-contract). As of August +The contract code for this contract is presented in [Appendix A](#appendix-a---tether-usd-contract). As of August 2023, USDT 39B was help in this contract. The contract has the following key features: - + #### Administration + The following monetary functions can only be called by the contract owner: + * `issue(amount)` - issues new tokens to the contract owner's account. * `redeem(amount)` - redeems tokens from the contract owner's account. * `setParams(...)` - Allows owner to set or change fees for transfers. Currently set to zero. The owner has access to the following fraud/AML functions: -* `addBlackList(address)` - Adds an address to the blacklist. Blacklisted addresses are not allowed to send tokens + +* `addBlackList(address)` - Adds an address to the blacklist. Blacklisted addresses are not allowed to send tokens (but they can receive them). * `removeBlackList(address)` - Removes an address from the blacklist. * `destroyBlackFunds(address)` - Destroys all tokens in the blacklisted address, reducing the total supply. -Finally, the owner has access to the following contract management functions: +Finally, the owner has access to the following contract management functions: + * `deprecate(address)` - Deprecates the contract and supplies the upgraded contract address. * `pause` - pauses the entire contract, preventing any transfers. * `unpause` - unpauses the contract. * `transferOwnership(address)` - transfers ownership of the contract to a new address. #### Account owners -The Tether contract records balances through a simple map of standard wallet addresses to amount. + +The Tether contract records balances through a simple map of standard wallet addresses to amount. Any ethereum address is eligible to hold a USDT balance by virtue of the ERC-20 contract. Account owners (ie the address matching the transaction `sender`) have the following abilities: + * `transfer(to, amount)` - transfers tokens to another address. Fees get sent to the owner's account. -* `transferFrom(from, to, value)` - allows a 3rd party to transfer tokens from the `from` account to the `to` account. - The 3rd party must have been authorised by the `from` account to do so using `approve` and amount must be less +* `transferFrom(from, to, value)` - allows a 3rd party to transfer tokens from the `from` account to the `to` account. + The 3rd party must have been authorised by the `from` account to do so using `approve` and amount must be less than or equal to `allowance(from, sender)`. -* `approve(spender, amount)` - authorises a 3rd party to transfer tokens from the owner's account to another account. +* `approve(spender, amount)` - authorises a 3rd party to transfer tokens from the owner's account to another account. The 3rd party must call `transferFrom` to perform the transfer. #### Public read-only functions - + The following functions are available to the public: + * `totalSupply` - returns the total supply of minted tokens. The unit is in millionths of a USD. * `balanceOf(address)` - returns the balance of the given address. -* `allowance(owner, spender)` - returns the amount of tokens that the spender is allowed to spend on behalf of the +* `allowance(owner, spender)` - returns the amount of tokens that the spender is allowed to spend on behalf of the owner. * `getBlackListStatus(address)` - returns whether the given address is blacklisted. * `getOwner` - returns the owner of the contract. ### Circle USD (USDC) - -The primary Circle USD contract address is -[`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48). -This is a proxy contract that relays calls to a secondary contract. This is ostensibly done to allow transparent -upgrades to the contract, but it does imply additional risk, since the contract code that actually runs and secures +The primary Circle USD contract address is +[`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48). + +This is a proxy contract that relays calls to a secondary contract. This is ostensibly done to allow transparent +upgrades to the contract, but it does imply additional risk, since the contract code that actually runs and secures your funds is not actually immutable anymore. - + The current proxied contract is presented in [Appendix B](#appendix-b---circle-usd-contract). As of August 2023, USDC 24B was held in this contract. -It is an ERC-20 contract like Tether, but adds the ability to carry out signature-based operations. Fees are not +It is an ERC-20 contract like Tether, but adds the ability to carry out signature-based operations. Fees are not supported in this contract. The contract has the following key features: @@ -136,6 +145,7 @@ The contract has the following key features: #### Administration The owner has access to the following contract management functions: + * `updatePauser(address)` - gives the `Pause` role to a new address. * `updateBlacklister(address)` - gives the `Blacklist` role to a new address. * `transferOwnership(address)` - transfers ownership of the contract to a new address. @@ -143,45 +153,52 @@ The owner has access to the following contract management functions: * `updateRescuer(address)` - gives the `Rescuer` role to a new address. The address with the `Pauser` role has access to the following functions: + * `pause` - pauses the entire contract, preventing any transfers. Caller must have the `Pauser` role. -* `unpause` - unpauses the contract. Caller must have the `Pauser` role. +* `unpause` - unpauses the contract. Caller must have the `Pauser` role. The address with the `Blacklist` role has access to the following functions: -* `blacklist(address)` - Adds an address to the blacklist. Blacklisted addresses are not allowed to send tokens - (but they can receive them). + +* `blacklist(address)` - Adds an address to the blacklist. Blacklisted addresses are not allowed to send tokens + (but they can receive them). * `unBlacklist(address)` - Removes an address from the blacklist. Caller must have the `Blacklist` role. The address with the `MasterMinter` role has access to the following functions: -* `configureMinter(address, amount)` - Allows the `MasterMinter` to add a new minter. The minter is allowed to mint + +* `configureMinter(address, amount)` - Allows the `MasterMinter` to add a new minter. The minter is allowed to mint up to the given amount of tokens. New minters have the `Mint` role. -* `removeMinter(address)` - Removes a minter. The address will no longer be able to mint tokens. +* `removeMinter(address)` - Removes a minter. The address will no longer be able to mint tokens. Addresses with the `Mint` role have access to the following functions: -* `mint(address, amount)` - Mints tokens to the given address. The amount must be less than or equal to the amount + +* `mint(address, amount)` - Mints tokens to the given address. The amount must be less than or equal to the amount that the minter is allowed to mint. Unlike in Tether, USDC mints can be injected directly into arbitrary accounts. * `burn(amount)` - Burns tokens from the minter's address. Minter must not be blacklisted. The address with the `Rescuer` role has access to the following function: -* `rescueERC20(contract, address, amount)` - Unconditionally transfers `amount` funds from the contract to `address`. - This is ostensibly a backdoor that allows the owner to recover funds in the event of a bug. + +* `rescueERC20(contract, address, amount)` - Unconditionally transfers `amount` funds from the contract to `address`. + This is ostensibly a backdoor that allows the owner to recover funds in the event of a bug. #### Account owners + The Tether contract records balances through a simple map of standard wallet addresses to amount. Any ethereum address is eligible to hold a USDT balance by virtue of the ERC-20 contract. Account owners (ie the address matching the transaction `sender`) have the following abilities: -* `transfer(to, amount)` - transfers tokens to another address. Fees get sent to the owner's account. Neither party + +* `transfer(to, amount)` - transfers tokens to another address. Fees get sent to the owner's account. Neither party may be blacklisted. * `transferFrom(from, to, value)` - allows a 3rd party to transfer tokens from the `from` account to the `to` account. The 3rd party must have been authorised by the `from` account to do so using `approve` and amount must be less than or equal to `allowance(from, sender)`. Neither `from`, `to` or the `sender` may be blacklisted. * `approve(spender, amount)` - authorises a 3rd party to transfer tokens from the owner's account to another account. - The 3rd party must call `transferFrom` to perform the transfer. Neither the 3rd party or the authorising account + The 3rd party must call `transferFrom` to perform the transfer. Neither the 3rd party or the authorising account may be blacklisted. -* `in(de)creaseAllowance(spender, amount)` - increases (decreases) the amount that the spender is allowed to spend on +* `in(de)creaseAllowance(spender, amount)` - increases (decreases) the amount that the spender is allowed to spend on behalf of the owner. Neither the 3rd party or the authorising account may be blacklisted. -* `transferWithAuthorization(to, value, authParams..)` - transfers tokens to another - address based on the signature provided. This allows clients to batch transfers and save on gas, or services to pay +* `transferWithAuthorization(to, value, authParams..)` - transfers tokens to another + address based on the signature provided. This allows clients to batch transfers and save on gas, or services to pay gas on behalf of clients. * `cancelAuthorization(authParams..)` - cancels a pending transferWithAuthorization. * `permit(owner, spender, value, ...)` - Similar to `approve` but authorisation is provided by a bearer signature. @@ -189,6 +206,7 @@ Account owners (ie the address matching the transaction `sender`) have the follo #### Public read-only functions The following functions are available to the public: + * `totalSupply` - returns the total supply of minted tokens. The unit is in millionths of a USD. * `balanceOf(address)` - returns the balance of the given address. * `allowance(owner, spender)` - returns the amount of tokens that the spender is allowed to spend on behalf of the @@ -198,7 +216,6 @@ The following functions are available to the public: * `minterAllowance(address)` - returns the amount of tokens that the given address is allowed to mint. * `isMinter(address)` - returns whether the given address is a minter. - ## Feature Comparison of Tether and Circle USD | Feature | Tether | Circle USD | Minimal requirements | @@ -217,507 +234,463 @@ The following functions are available to the public: | Contract pause | Yes | Yes | N/A | ## Description - + ### Key assumptions and requirements + 1. The stablecoin is account-based. -2. Issuance and redemptions of the stablecoin tokens are performed by a centralised entity, the `issuer`. The stability -of the token and its peg are completely dependent on the issuer's ability to maintain the peg. The issuer is +2. Issuance and redemptions of the stablecoin tokens are performed by a centralised entity, the `issuer`. The stability + of the token and its peg are completely dependent on the issuer's ability to maintain the peg. The issuer is required to act responsibly and issue and redeem tokens in a timely manner in order to engender confidence in the coin and maintain the peg. -3. Aside from the administrator privileges conferred on the `issuer` by the stablecoin contract, the coin is operated - in a decentralised manner, and transfers are facilitated by the Tari network, and are not processed by any centralised +3. Aside from the administrator privileges conferred on the `issuer` by the stablecoin contract, the coin is operated + in a decentralised manner, and transfers are facilitated by the Tari network, and are not processed by any + centralised entity, including the `issuer`. 4. The `issuer` has the following "administrator" powers: 1. Create and authorise new accounts. - 2. Issue new tokens. The new tokens are credited to the `issuer`'s account. - 3. Redeem (burn) existing tokens. The burnt tokens are debited from the `issuer`'s account. - 4. Have access to the full list of account ids. + 2. Issue new tokens. The new tokens are credited to the `issuer`'s account. The transactions are i the clear so + that anyone can verify the total circulating supply of the stablecoin. + 3. Redeem (burn) existing tokens. The burnt tokens are debited from the `issuer`'s account. These transactnios + are in the clear. + 4. Have access to the full list of account ids. 5. Blacklist an account. Blacklisted accounts are not allowed to send, or receive, tokens. 6. Remove an account from the blacklist. 5. Account-holders have the following abilities: - 1. View their own balance. - 2. If their account is not blacklisted, transfer funds to any other (non-blacklisted) account. + 1. View their own balance. + 2. If their account is not blacklisted, transfer funds to any other (non-blacklisted) account. 6. General users: - 1. Cannot see the balance of any account (other than their own). - 2. Can see the total supply of tokens in circulation. - 3. Can apply for a new account by interacting with the `issuer`. + 1. Cannot see the balance of any account (other than their own). + 2. Can see the total supply of tokens in circulation. + 3. Can apply for a new account by interacting with the `issuer`. 7. The possibility of the issuer charging a fee for transfers is not considered in this design. 8. The issuer can view the balance of account holders. 9. The issuer cannot unilaterally spend or seize funds from any account holder. Validator nodes validate all stablecoin transactions. In particular: + 1. Validator nodes cannot determine the value of any transaction. -2. However, they _are_ able to, and MUST verify that - 1. no coins are created or destroyed in the transaction, i.e. the sum of the parties' balances before and after - the transfer are equal. - 2. the transfer value is positive, - 3. the sender has a positive balance after the transaction, - 4. the sender has authorised the transaction, - 5. the sending party holds a valid account, - 6. the sending party is not on the blacklist, - 7. the receiving party holds a valid account, - 8. the receiving party is not on the blacklist. -3. If any of the conditions in 3 are not met, the transaction is invalid and the validator node MUST reject the +2. However, they _are_ able to, and MUST verify that + 1. no coins are created or destroyed in the transaction, i.e. the sum of the parties' balances before and after + the transfer are equal. + 2. the transfer value is positive, + 3. the sender has a positive balance after the transaction, + 4. the sender has authorised the transaction, + 5. the sending party holds a valid account, + 6. the sending party is not on the blacklist, + 7. the receiving party holds a valid account, + 8. the receiving party is not on the blacklist. +3. If any of the conditions in 3 are not met, the transaction is invalid and the validator node MUST reject the transaction. ## Implementation - + The broad strategy is as follows: -* Balances are stored in Pedersen commitments. The blinding factor is updated after every transaction and is a - function of a shared secret between the account holder and the issuer, and a public nonce, that is stored in the + +* The issuer mints stablecoin tokens in equal quantity to the amount of fiat currency deposited with the issuer. + These mint transactions are in the clear and anyone can ascertain the total circulating supply of the stablecoin. +* Issuers can transfer tokens to any account. These transfers are also in the clear. +* Transfers between account holders are confidential. +* The issuer can carry out confidential transfers by first transferring tokens from the + cleartext issuer account to another, standard account controlled by the issuer, and then performing a confidential + transfer from there to the final destination account. +* Balances are stored in Pedersen commitments. The blinding factor is updated after every transaction and is a + function of a shared secret between the account holder and the issuer, and a public nonce, that is stored in the account metadata. -* Users create a new account by interacting with the issuer. The issuer provides an account id that can be compared - against a white- and/or blacklist for the purposes of determining whether the account is valid. -* The full list of accounts form part of a whitelist. Only the issuer can modify the whitelist. -* The blacklist, which only the issuer can modify. +* Users create a new account by interacting with the issuer. The issuer provides an account id that can be compared + against a blacklist for the purposes of determining whether the account is valid or not. The issuer may choose to + conduct a KYC procedure out-of-band as part of the account creation process. +* The full list of accounts form part of a whitelist. Only the issuer can modify the whitelist. +* A blacklist, which only the issuer can modify, comprises a list of accounts that may no longer participate in + stablecoin transactions. * Spending authority rests solely with the account owner and required knowledge of the account private key. -* Every account contains a signature signed by both the issuer and account owner. This prevents blacklisted accounts - from copying signatures from other accounts to avoid sanction. -* Transfers are done in two-steps, via the issuance of an e-cheque by the sender, followed by the claiming of the +* Transfers are done in two-steps, via the issuance of an e-cheque by the sender, followed by the claiming of the e-cheque by the recipient. - -The remainder of this section describes the implementation in detail. - -### Accounts - -An account is described by the following struct: - -```rust,ignore -pub type AccountId = PublicKey; - -/// All fields are immutable, except for `owner`, `nonce` and `pending_deposits`. -struct StableCoinAccount { - // The account owner's public key. Conveys spend authority - owner: PublicKey, - // A counter that is incremented after every transaction. Used to prevent replay attacks and to update the - // balance commitment. - nonce: u64, - // A Pedersen commitment for the actual balance in the account - balance: Commitment, - // The secret key for the account id is shared between the issuer and account owner - account_id: AccountId, - // A signature proving knowledge of the account private key and binding the account id to the account owner. - issuer_signature: Signature, - // A collection of pending deposits. Accounts owners must `claim` a deposit in order to update their balance. - pending_deposits: Vec, -} -``` - -Global management is handled by the `StableCoin` struct: - -```rust,ignore -struct StableCoin { - // A distributed hashmap of accounts, keyed by the account id - accounts: DistributedCollection, - blacklisted_accounts: DistributedSet, - issuer: PublicKey, - account_public_key: PublicKey, - symbol: String, - issuer_balance: u64, - total_supply: u64, - nonce: u64, - const ACCOUNT_DOMAIN: &'static [u8] = b"StableCoinAccount"; - const BALANCE_DOMAIN: &'static [u8] = b"StableCoinBalance"; -} -``` +* Issuers are able to view account balances by holding a shared encryption key with every user. This key is used to + encrypt the memo field of the account. The issuer can decrypt the memo field and determine the value of the + transfer. Every account also possesses an equivalence proof, which shows that the balance commitment is equal to + the value in the memo field. -Transfers are facilitated by e-cheques, which are written to recipients by senders and can be claimed by recipients -at their convenience. -```rust,ignore -struct ECheque { - sender: Pubkey, - amount: Commitment, - encrypted_memo: Vec, - signature: Signature, - nonce: u64, - rejected: bool, - rejection_signature: Signature, -} -``` +The remainder of this section describes the implementation in more detail. -Accounts form a `DistributedCollection` object on the main `StableCoin` contract. The `DistributedCollection` is -keyed by the `account_id` field. +
+This design is experimental and not yet suitable for production. +
-An account MUST be created by called the `new_account` method on the `StableCoin` contract. No other instantiations of -an entry in the distributed collection is permitted. +## Mathematical furniture -### Balances -The j-th balance commitment for an account, updated after every transaction, `C_j` is of the form +As a point of notation, we sometimes use superscripts in mathematical notation in this note; unless otherwise indicated, +this is symbolic and not to be interpreted as indicating exponentiation. -```text - C_j = κ_j.G + v_j.H -``` +### Hash functions and ciphers -where +We require the use of multiple cryptographic hash functions, which must be sampled independently. +It is possible to do this by carefully applying domain separation to a single cryptographic hash function. +We further require that when data is provided as input to such a hash function, it is done safely in a manner that is +canonical and cannot induce collisions. +We use a comma notation to indicate multiple input values, as in $H(a, b, c, \ldots)$. -* `κ_j` is the j-th blinding factor of the account, -* `v_j` is the balance of the account, -* and `G` and `H` are the standard generators. +We also require the use of a key-committing AEAD (authenticated encryption with additional data) construction. +It is possible to extend an arbitrary AEAD design in this manner by including a safe cryptographic hash of a derived +key. -### Derivation of blinding factor -The balance blinding factor, `κ_j` is derived as +Let $H_{\text{AEAD}}$ be a cryptographic hash function whose output is the AEAD key space, suitable for $H_ +{\text{AEAD}}$ to operate as a key derivation function for the AEAD. -```text -κ_j = H(BALANCE_DOMAIN || account_id_secret || j) -``` +### Proving systems -where `j` must always equal the current `nonce` of the account. +The design will require several zero-knowledge proving systems that allow validators to assert correctness of operations +without revealing protected data. -As described below the account id secret is a shared secret between the account holder and the issuer. As such only -the issuer and the account holder can derive the blinding factor. +### Verifiable encryption -The account owner typically knows the balance of the account, while the -issuer can determine the balance by brute-forcing the discrete logarithm of the `v_i.H` term. Since `v_i` is bounded -by zero and `total_supply`, this is a feasible operation. Moreover, there are additional heuristics available to -make this operation even more straightforward, but they are not covered now. - -### The owner key -When creating a new account, a user generates a new random secret, `u_i`. This must be kept secure and is known only -to the owner. They use the `owner` public key, `U_i = u_i.G` when requesting a new account via the `new_account` -method. +We require the use of a verifiable ElGamal encryption proving system that asserts the value bound to a Pedersen +commitment matches the value encrypted to a given public key. +This will be used to assert that the issuer can decrypt account balances without knowing the opening to the account's +balance commitment. -### The account id -The issuer must maintain a list of all accounts that have been created on the stablecoin contract. When a user -wishes to create a new account, the user must request one from the issuer using the `new_account` method, providing -their [owner public key](#the-owner-key) along with the request. +The proving relation is -```rust,ignore - pub fn new_account(user_pubkey: PublicKey) -> Result {} -``` +$\\{ (C, E, R, P); (v, m, r) | C = vG + mH, E = vG + rP, R = rG \\}$. -The issuer creates a new account id using a Diffie-Hellman key exchange with the user's public key, `U_i` and the -issuer account secret, `a`. +* The prover samples $x_v, x_m, x_r$ uniformly at random. +* It computes $C' = x_v G + x_m H$, $E' = x_v G + x_r P$, and $R' = x_r G$ and sends them to the verifier. +* The verifier samples nonzero $e$ uniformly at random and sends it to the prover. +* The prover computes $s_v = ev + x_v$, $s_m = em + x_m$, and $s_r = er + x_r$ and sends them to the verifier. +* The verifier accepts the proof if and only if $eC + C' = s_v G + s_m H$, $eE + E' = s_v G + s_r P$, and $eR + R' = s_r +G$. -```text -a_i = H(ACCOUNT_DOMAIN || a.U_i) -``` +The proof can be made non-interactive using the Fiat-Shamir technique. +It is a sigma protocol for the relation that is complete, 2-special sound, and special honest-verifier zero knowledge. -The user can derive their account id secret, `a_i` using the same Diffie-Hellman key exchange with their secret, -`u_i` and the `account_public_key`, `A`. +### Value equality -The public `account_id` is used -* as an identifier for the account in the distributed account hashmap, -* along with the nonce, to derive the balance blinding factors. - -### New account creation +We require the use of a value equality proving system that asserts two Pedersen commitments bind to the same value. +This will be used to assert that commitments used in transfers retain balance. -Upon a successful call to `new_account`, the issuer creates a new account entry in the distributed hashmap. The new -account is initialised with the following values: +The proving relation is -* `owner` - set to the user's public key, `U_i`. -* `nonce` - set to zero. -* `balance` - a commitment to zero using `κ_0` as blinding factor. -* `account_id` - set to the public account id, `A_i`. -* `issuer_signature` - the issuance signature, described below. -* `pending_deposits` - an empty vector. +$\\{ (C_1, C_2); (v, m_1, m_2) | C_1 = vG + m_1 H, C_2 = vG + m_2 H \\}$. -The `issuer_signature` is a signature proving that the issuer has authorised the account creation and binds the -account id to the user's public key. Validators MUST verify this signature before processing and transfers or claims -from the account. The signature is a Schnorr signature, `(s,R)` signed by the `issuer` secret key of the message +* The prover samples $x_v, x_{m_1}, x_{m_2}$ uniformly at random. +* It computes $C_1' = x_v G + x_{m_1} H$ and $C_2' = x_v G + x_{m_2} H$ and sends them to the verifier. +* The verifier samples nonzero $e$ uniformly at random and sends it to the prover. +* The prover computes $s_v = ev + x_v$, $s_{m_1} = em_1 + x_{m_1}$, and $s_{m_2} = em_2 + x_{m_2}$ and sends them to the +verifier. +* The verifier accepts the proof if and only if $eC_1 + C_1' = s_v G + s_{m_1} H$ and $eC_2 + C_2' = s_v G + s_{m_2} H$. -```text -msg = H(NEW_ACCOUNT_DOMAIN || A_i || U_i) -``` +The proof can be made non-interactive using the Fiat-Shamir technique. +It is a sigma protocol for the relation that is complete, 2-special sound, and special honest-verifier zero knowledge. -Validators can validate the signature by checking that +### Schnorr representation -```text - s.G == R + msg.P -``` +We require the use of a Schnorr representation proving system that asserts knowledge of a discrete logarithm. +This will be used to sign messages, as well as to assert that a Pedersen commitment binds to a given value. -where `P` is the `issuer_public_key`. - -### Transfers - -Transfers are carried out in two parts. -In the first part, the sender debits their own account by an `amount` and issues a bearer token in the name of the -recipient. This token is added to the `pending_deposits` vector of the recipient's account. - -In the second part, the recipient claims the deposit, which updates their balance commitment and destroys the bearer -token. - -In this way, the taken acts like an e-cheque that can only be claimed by the recipient. The two-step approach is -required because neither the sender, nor any validator has sufficient information to update the recipients balance. -Only the recipient or in some implementations of the stablecoin contract, the issuer. - -However, this model does have some benefits: -* The sender and recipient do not need to interact directly. -* Privacy is preserved. -* Before the recipient claims the deposit, the transaction is potentially reversible, making this stablecoin model - behave more like traditional financial systems. - -#### Sending - -A transfer is initiated by the sender calling the `transfer` method on the stablecoin contract. - -```rust,ignore -pub fn transfer( - &mut self, // The sender's account - recipient: AccountId, - amount: Commitment, - /// An enrypted message to the recipient, containing: - /// * the blinding factor for `amount` = κ_(j+1) - κ_j - /// * the value of the transfer, up to 2^64 - /// * the length of the optional message field, up to 256 bytes - /// * an optional message for the recipient, of length bytes. - encrypted_memo: Vec, - signature: Signature, - /// An aggregated ZK proof of: - /// * the amount committing to a value in the range [0, 2^64] - /// * the new balance committing to a value in the range [0, 2^64] - /// * κ_j = H(BALANCE_DOMAIN || account_id_secret || j), where j = nonce+1 - aggregated_range_proof: Proof, -) {} -``` +The proving relation is -The `signature` is a Schnorr signature, `(s,R)` signed by the `owner` secret key of the message. +$\\{ P; p | P = pG \\}$. -```text - msg = H(TRANSFER_DOMAIN || owner || recipient || amount || encrypted_memo || new_nonce) -``` +* The prover samples $x_p$ uniformly at random. +* It computes $P' = x_p G$ and sends it to the verifier. +* The verifier samples nonzero $e$ uniformly at random and sends it to the prover. +* The prover computes $s_p = ep + x_p$ and sends it to the verifier. +* The verifier accepts the proof if and only if $eP + P' = s_p G$. +The proof can be made non-interactive using the Fiat-Shamir technique. +It is a sigma protocol for the relation that is complete, 2-special sound, and special honest-verifier zero knowledge. +To use this proving system to assert that a commitment $C$ binds to a given value $v$, we set $P = C - vG$ and use the +Schnorr representation proving system on this statement using the generator $H$ instead of $G$, and being careful to +bind $v$ into the Fiat-Shamir transcript. -Validators MUST verify the following as part of validating the instruction: -* The sender's account is valid. -* The sender's account is not blacklisted. -* The recipient's account is valid. -* The recipient's account is not blacklisted. -* The `signature` is valid and signed by `owner`'s secret key. -* That `amount` is positive via the included range proof. -* That `new_nonce` is equal to the sender's `nonce` + 1. -* Calculate `new_balance` = `balance` - `amount`. -* That `new_balance` is positive via the included range proof. -* That the blinding factor for `new_balance` is correct, i.e. `κ_j = H(BALANCE_DOMAIN || account_id_secret || j)`, - where `j` is the nonce of the sender's account plus one via the attached zero-knowledge proof. +### Commitment range -If validation succeeds, the sender's account is updated as follows: -* `nonce` is incremented by one. -* `balance` is updated to `new_balance`. +We require the use of a commitment range proving system that asserts that all Pedersen commitments in a set bind to +values in a specified range. +This will be used to prevent balance underflow and overflow that would inflate supply. +We assume the intended range is $[0, 2^n)$ for some globally-fixed $n$; in practice, $n = 64$ is typically used (since +it is often the case that $n$ must itself be a power of two). -The recipient's account is updated as follows: -* A new `ECheque` is created using the sender's public key, `amount`, `encrypted_memo`, `signature` and `new_nonce`. -* The `pending_deposits` vector is appended with the newly created e-cheque. +The proving relation is -#### Claiming +$\\{ (C_j)\_{j=0}^{m-1}; (v_j, m_j)_{j=0}^{m-1} : C_j = v_j G + m_j H, v_j \in [0, 2^n) \forall j \\}$. -A transfer is only considered complete once the recipient has claimed the deposit. +The popular and efficient Bulletproofs and [Bulletproofs+](https://github.com/tari-project/bulletproofs-plus) range +proving systems may be used for this purpose. -An account holder can claim a pending deposit by calling the `claim` method on the stablecoin contract, which -destroys the supplied e-cheque and credits the user's balance with amount. +## Design -If the `rejected` field on the e-cheque is `false`, ONLY the recipient may redeem the e-cheque. -If the `rejected` field on the e-cheque is `true`, ONLY the sender may redeem the e-cheque. +We now describe the stablecoin design. -The `encrypted_memo` is an encrypted message to the recipient, containing: -* the blinding factor for `amount` = κ_(j+1) - κ_j -* the value of the transfer, up to 2^64 -* the length of the optional message field, up to 256 bytes -* an optional message for the recipient, of length bytes. +### Issuer -The memo is encrypted using a symmetric cipher, with a key derived from the shared secret between the sender and -recipient, and the transfer metadata: +The stablecoin is instantiated by defining the issuer. -```text -encryption_key = H(MEMO_DOMAIN || sender_secret*recipient_owner || amount || encrypted_memo || new_nonce) -= H(MEMO_DOMAIN || sender_secret*recipient_owner || amount || encrypted_memo || new_nonce) -``` - -Claiming follows this procedure: - -1. The recipient's wallet decrypted the `encrypted_memo` using the shared secret between the sender and recipient. -2. The recipient's wallet verifies that the blinding factor and value opens the commitment. If it does not, the - e-cheque is invalid and MAY be rejected. However, to avoid an attack vector where a malicious sender can flood a - victim with invalid e-cheques, forcing them to pay fees to reject them, the recipient SHOULD simply ignore the - deposit. A transfer is only considered complete once the recipient has claimed the deposit. -3. The recipient may use the memo's message, if present to match payments to invoices, for example. -4. The recipient creates a new commitment, using their own `nonce` and blinding factor `κ_(nonce+1) - κ_nonce`. -5. The recipient invokes the `claim` method to finalise the claim. - -The `claim` method is defined as follows: - -```rust,ignore -pub fn claim( - &mut self, - e_cheque: ECheque, - amount: Commitment, - /// An aggregated ZK proof of: - /// * the value in the commitment matches the value in the e-cheque - /// * the blinding factor is κ_(nonce+1) - κ_nonce - aggregated_proof: Proof, -) {} -``` +The issuer samples its secret key $p$ uniformly at random, and computes the corresponding public key $P = pG$. +It produces a Schnorr representation proof $\Pi_P$ using statement $P$ and witness $p$. +It sets up the stablecoin as follows: -Validators MUST verify the following as part of validating the instruction: -* The recipient's account is valid. -* The recipient's account is not blacklisted. -* The `rejected` field on the e-cheque is `false`. -* That value in `amount` is equal tp the value in the e-cheque commitment. -* That the blinding factor for `amount` is correct, i.e. `κ_(nonce+1) - κ_nonce` via the attached zero-knowledge proof. +- Public key: $P$ +- Public key proof: $\Pi_P$ -If validation succeeds, the recipient's account is updated as follows: -* `nonce` is incremented by one. -* `balance` is updated to `balance` + `amount`. -* The e-cheque is removed from `pending_deposits`. +Validators check this operation by verifying $\Pi_P$. -#### Rejecting +The issuer also sets up an account for itself using the structure described below for users. -An account holder can reject a pending deposit by calling the `reject` method on the stablecoin contract, which -effectively returns the e-cheque to the sender. The sender can then claim the deposit back into their account. +### Users -The recipient provides a signature when rejecting the deposit, which is verified by validator nodes when processing -the transaction. The signature is a Schnorr signature, `(s,R)` signed by the `owner` secret key of the message. +A user who wishes to open an account interacts with the issuer via a side channel. +The user samples its secret key $k$ uniformly at random, and computes the corresponding public key $K = kG$. +It produces a Schnorr representation proof $\Pi_K$ using statement $K$ and witness $k$. +The issuer checks that $K$ has not been used in any other approved account, and verifies $\Pi_K$. -```text - msg = H(REJECT_DOMAIN || sender || recipient || amount || encrypted_memo || new_nonce) -``` +If the issuer approves the account, it signs $K$ by generating a Schnorr representation proof $\Pi_{P, K}$ using +statement $P$ and witness $p$, binding $K$ into the Fiat-Shamir transcript. -* Once a deposit has been rejected, it cannot be un-rejected. -* The recipient's nonce is _not_ incremented when rejecting a deposit. +The issuer sets up the account structure as follows: -Validators MUST verify the following as part of validating the instruction: -* The sender's account is valid. -* The sender's account is not blacklisted. -* The recipient's account is valid. -* The recipient's account is not blacklisted. -* The `signature` is valid and signed by the recipient. -* The `rejected` field on the e-cheque is `false`. -* That the `amount`, `sender`, `encrypted_memo` and `new_nonce` fields match the original e-cheque. +- Public key: $K$ +- Public key proof: $\Pi_K$ +- Issuer proof: $\Pi_{P, K}$ +- State nonce: 0 +- Balance commitment: 0 (identity group element) +- Issuer-encrypted balance: None +- User-encrypted balance: None +- Pending e-cheques: None -### Blacklisting +* The *state nonce* is a value used to track changes to the account and avoid replaying messages. +* The *issuer-encrypted balance* field will be populated with a verifiable encryption of the balance that can be + decrypted by the issuer. +* The *user-encrypted balance* field will be populated with an authenticated encryption of the value and mask +corresponding to the balance commitment that can be decrypted by the user for account recovery purposes. +* The *pending e-cheques* field will be populated with *e-cheques* representing transfers that are destined for the + account, but that the user has neither approved nor rejected. -The issuer may blacklist an account by calling the `set_blacklist` method on the stablecoin contract. +Validators check this operation by verifying $\Pi_K$ and $\Pi_{P, K}$, asserting that $K$ does not appear in any other +account, and asserting the other constant values are as expected. -```rust,ignore -pub fn blacklist(&mut self, account_id: AccountId, signature: Signature) {} -``` +### Cheques -Validators MUST verify the following as part of validating the instruction: -* The signature is valid and signed by `issuer`'s private key. -* `account_id` exists as a valid account. -* `account_id` is not already blacklisted. +When a user wishes to transfer funds to another user, it does so by generating an *e-cheque*. +Once validators check the e-cheque, it is added to the recipient's pending e-cheques list and the +sender's account is updated. -If validation succeeds, the stablecoin object is updated to include `account_id` in the `blacklisted_accounts` set. +The e-cheque remains until the recipient approves or rejects it, or until the e-cheque becomes abandoned, as +described later. -Removing an account from the blacklist follows a similar procedure: +Suppose the sender wishes to transfer value $v^\Delta$ to a recipient. +* Let $K_s = k_s G$ and $K_r$ be the sender and recipient keys, respectively. +* Let $C = vG + mH$ be the sender balance commitment. +* Let $i$ be the sender state nonce. -```rust,ignore -pub fn unblacklist(&mut self, account_id: AccountId, signature: Signature) {} -``` +The sender does the following: -Validators MUST verify the following as part of validating the instruction: -* The signature is valid and signed by `issuer`'s private key and contains the correct nonce. -* `account_id` exists as a valid account. -* `account_id` is blacklisted. +- Samples a scalar $m_s^\Delta$ uniformly at random and uses it to generate a commitment $C_s^\Delta = v^\Delta G + + m_s^\Delta H$ to the transfer value. +- Samples a scalar $m^\Delta$ uniformly at random and uses it to generate a commitment $C^\Delta = v^\Delta G + m^\Delta + H$ to the transfer value. +- Generates a proof of value equality $\Pi_\Delta$ on the statement $(C_s^\Delta, C^\Delta)$ and witness $(v^\Delta, + m_s^\Delta, m^\Delta)$. +- Samples a scalar $r$ uniformly at random, and computes $R = rG$. +- Generates an AEAD key $H_{\text{AEAD}}(r K_r)$ and uses it to encrypt the tuple $(v^\Delta, m^\Delta)$, producing + authenticated ciphertext $c$. +- Sets $E = (v - v^\Delta) G + rP$ as an ElGamal encryption of its new balance, and generates a verifiable encryption + proof $\Pi_{\text{enc}}$ on the statement $(C - C_s^\Delta, E, R, P)$ and witness $(v - v^\Delta, m - m_s^\Delta, r)$. +- Generates a range proof $\Pi_{\text{range}}$ on the statement $\\{ C_s^\Delta, C - C_s^\Delta \\}$ and witness $\\{ ( + v^\Delta, m_s^\Delta), (v - v^\Delta, m - m_s^\Delta) \\}$. +- Generates an AEAD key $H_{\text{AEAD}}(k_s)$ and uses it to encrypt the tuple $(v - v^\Delta, m - m_s^\Delta)$, + producing authenticated ciphertext $c_s$. +- Sets the e-cheque to be the tuple $t = (K_s, K_r, C, i, C_s^\Delta, C^\Delta, E, R, \Pi_\Delta, \Pi_{\text{enc}}, \Pi_ + {\text{range}}, c_s, c_{sr})$. +- Signs the e-cheque by generating a Schnorr representation proof $\Pi_t$ using statement $K_s$ and witness $k_s$, binding + $t$ into the Fiat-Shamir transcript. -If validation succeeds, the stablecoin object is updated to remove `account_id` from the `blacklisted_accounts` set. - -Both operations increase the nonce of the issuer's account by one to prevent replay attacks. +Prior to accepting the e-cheque as valid, validators perform the following checks: -### Minting and burning +- Assert that neither $K_s$ nor $K_r$ appear on the blacklist. +- Verify the proof $\Pi_t$. +- Look up the sender's account using $K_s$ and assert that $C$ and $i$ match the corresponding values in the account. +- Verify the proofs $\Pi_\Delta$, $\Pi_{\text{enc}}$, and $\Pi_{\text{range}}$. -The issuer may mint new tokens by calling the `mint` method on the stablecoin contract. -Similarly, the issuer may burn tokens by calling the `burn` method on the stablecoin contract. +If these checks pass, validators add the e-cheque to the recipient's pending e-cheques list, and update the sender's account +as follows: -```rust,ignore -pub fn mint(&mut self, amount: u64, nonce: u64, signature: Signature) {} -pub fn burn(&mut self, amount: u64, nonce: u64, signature: Signature) {} -``` +- Increment the state nonce. +- Set the balance commitment to $C - C_s^\Delta$. +- Set the issuer-encrypted balance to the tuple $(E, R)$. +- Set the user-encrypted balance to $c_s$. -In both cases, the signature signs the amount to be minted or burnt, and the nonce of the stablecoin account. +When the recipient sees the e-cheque, it can either *endorse* or *void* it. -Validators MUST verify the following as part of validating the instruction: -* The signature is valid and signed by `issuer`'s private key and contains the correct nonce. -* The `amount` is positive. -* When burning, the `amount` is less than or equal to the balance of the issuer's account. +Endorsement means the recipient intends to accept the funds and wishes to have its account updated accordingly. +Voiding means the recipient does not intend to accept the funds and wishes for the sender to be able to claim them back. +Prior to making this determination, the recipient does the following: -After validation, the issuer's account is updated as follows: -* The `nonce` is incremented by one. -* When minting, the `balance` is increased by `amount`. -* When burning, the `balance` is decreased by `amount`. -* The `total_supply` is adjusted by `amount`, depending on whether it is a burn or mint. +- Generates an AEAD key $H_{\text{AEAD}}(k_r R)$ and uses it to authenticate and decrypt $c$, producing the tuple $( + v^\Delta, m^\Delta)$. +- Checks that $C^\Delta = v^\Delta G + m^\Delta H$. -### Issuance +If these checks pass, it may choose to endorse or void the e-cheque. +If they fail, it must void the e-cheque. -Transfers from the issuer to user accounts follows a different process to inter-account transfers because the issuer -balance is always stored in the clear. +### Voiding an e-cheque -Since the issuer has access to the blinding factor of all accounts, they can unilaterally deposit funds into any -account, assuming that the Issuer is granted write-access to the relevant `StableCoin` account. +Suppose the recipient wishes to void an e-cheque $t$. -This is done with the `issue` method: +It does the following: -```rust,ignore -pub fn issue( - &mut self, - recipient: &mut StableCoinAccount, - issuer_nonce: u64, - recipient_nonce: u64, - value: u64, - /// the recipient's blinding factor for amount, equal to κ_(r+1) - κ_r, where r is the current recipient nonce - blinding_factor: PublicKey, - signature: Signature, - proof: AggregatedProof) {} -``` +- Sets the voiding to be the tuple $t_{\text{void}} = (t)$. +- Signs the voiding by generating a Schnorr representation proof $\Pi_t$ using statement $K_r$ and witness $k_r$, + binding $t_{\text{void}}$ into the Fiat-Shamir transcript. -Validators will confirm the following: -* `issuer_balance - amount` is greater than or equal to zero. -* The signature is valid and signed by `issuer`'s private key and contains the correct nonces for both the issuer - account and the recipient account. -* A proof in zero-knowledge that `recipient.balance + amount` has a value in the range [0, 2^64] and that the blinding - factor is correct, i.e. `H(BALANCE_DOMAIN || account_id_secret || r+1)`. - -If verification succeeds, the stablecoin account is then modified as follows: -* The `issuer_nonce` is incremented by one. -* The `issuer_balance` is decreased by `amount`. +Prior to accepting the voiding as valid, validators perform the following checks: -The validator will then update the recipient's account as follows: -* The recipient's `nonce` is incremented by one. -* The recipient's `balance` is set to `balance + amount`. +- Assert that neither $K_s$ nor $K_r$ appear on the blacklist. +- Verify the proof $\Pi_t$. -### Redemptions +If these checks pass, validators annotate $t$ in the recipient's pending e-cheques list to indicate the voiding. -Redemptions are performed using the `redeem` function on the StableCoin object. Redemptions are similar to transfers, -except that the amount is in the clear, and the recipient is always the issuer. -The updating of the issuer's balance is carried out in a similar fashion to the [issuance](#issuance) process. -Taking custody of the underlying currency must happen off-chain and therefore is not covered in this document. +### Endorsing an e-cheque -Usually a claim to the underlying currency can be made by pointing to a finalised transaction on-chain or through -the inspection of event logs. +Suppose the recipient wishes to endorse an e-cheque $t$ with $C^\Delta = v^\Delta G + m^\Delta H$ from its pending e-cheques +list. -## Final notes +Now let $C = vG + mH$ be the recipient balance commitment. + +Let $i$ be the recipient state nonce. + +The recipient does the following: + +- Samples a scalar $m_r^\Delta$ uniformly at random, and uses it to generate a commitment $C_r^\Delta = v^\Delta G + + m_r^\Delta H$ to the transfer value. +- Generates a proof of value equality $\Pi_\Delta$ on the statement $(C_r^\Delta, C^\Delta)$ and witness $(v^\Delta, + m_r^\Delta, m^\Delta)$. +- Samples a scalar $r$ uniformly at random, and computes $R = rG$. +- Sets $E = (v + v^\Delta) G + rP$ as an ElGamal encryption of its new balance, and generates a verifiable encryption + proof $\Pi_{\text{enc}}$ on the statement $(C + C_r^\Delta, E, R, P)$ and witness $(v + v^\Delta, m + m_r^\Delta, r)$. +- Generates an AEAD key $H_{\text{AEAD}}(k_r)$ and uses it to encrypt the tuple $(v + v^\Delta, m + m_r^\Delta)$, + producing authenticated ciphertext $c_r$. +- Sets the endorsement to be the tuple $t_{\text{end}} = (t, C, i, C_r^\Delta, E, R, \Pi_\Delta, \Pi_{\text{enc}}, + c_r)$. +- Signs the endorsement by generating a Schnorr representation proof $\Pi_t$ using statement $K_r$ and witness $k_r$, + binding $t_{\text{end}}$ into the Fiat-Shamir transcript. + +It is also possible for the original sender of an e-cheque to endorse it as well. +This can arise in two cases: + +- The recipient of the e-cheque has voided it. +- The recipient of the e-cheque has neither accepted nor voided it, and a protocol-specified period of time has passed. + +The process is the same as above, with the sender now playing the role of the recipient. + +Prior to accepting the endorsement as valid, validators perform the following checks: + +- Assert that neither $K_s$ nor $K_r$ appear on the blacklist. +- Verify the proof $\Pi_t$. +- Look up the recipient's account using $K_r$ and assert that $C$ and $i$ match the corresponding values in the account, + and that $t$ appears in the pending e-cheques list. +- Verify the proofs $\Pi_\Delta$ and $\Pi_{\text{enc}}$. + +If these checks pass, validators remove the e-cheque $t$ from the recipient's pending e-cheques list, and update the +recipient's account as follows: + +- Increment the state nonce. +- Set the balance commitment to $C + C_r^\Delta$. +- Set the issuer-encrypted balance to the tuple $(E, R)$. +- Set the user-encrypted balance to $c_r$. + +### Issuer balance visibility + +The issuer can privately view any user's balance at any time using ElGamal decryption. +Suppose it wishes to view the balance of a user whose issuer-encrypted balance is $(E, R)$. + +It does the following: + +- Sets $V = E - pR$. +- Finds $v$ such that $V = vG$; this is the user's balance. -There are several variations of this contract that could be implemented. +Because the search space for balances is limited, the issuer can optimize this process. +For example, it could produce a lookup table mapping $vG \mapsto v$ for reasonable values $v$, or simply use brute force +on the search space. -For example, if commitment blinding factors -are not enacted via shared secrets, then the ability of the issuer to view balances unilaterally can be removed from -the design. +### User account recovery -The `transfer` function can be augmented to include dummy inputs and outputs, using a strategy similar to that used in -[Lelantus Spark](https://ia.cr/2021/1173). This would obfuscate the parties in a transfer, dramatically -improving privacy. +If the user loses access to their account balance, they can recover the opening to their balance commitment to regain +access, provided they still hold the private key $k$. -The use of e-cheques as the primary value transfer vehicle opens up many possibilities for adding additional margin -of error to on-chain financial transactions. For example, e-cheques could have a holding time associated with them, -to allow parties to validate payments out-of-band. They could have additional claim constraints, which would -simplify escrow contracts and swap contracts while improving security. +Suppose such a user with key $k$ queries validators for its account's user-encrypted balance. -The ability for the Issuer to unilaterally deposit (or confiscate) funds into any account is typically controlled by -Tari's access control system via bearer tokens. If this design were to be implemented on a different smart contract -platform, then the issuer's ability to unilaterally modify account balances would need to be addressed specifically. +It does the following: +- Generates an AEAD key $H_{\text{AEAD}}(k)$ and uses it to authenticate and decrypt the user-encrypted balance, + producing the tuple $(v, m)$; this is the opening to their balance commitment. +### Issuer transfers + +When transferring funds from the issuer to a user, or from a user to the issuer, it is required that the value be +publicly visible for transparency purposes. +However, we wish to reuse as much of the existing design as possible, in order to simplify the design and reduce +engineering risk. + +If the issuer wishes to transfer funds to a user, it produces an e-cheque with the following modifications: + +- It sets $r = 0$. +- It uses a zero key to produce $c_s$. + +When validating such an e-cheque, validators additionally do the following: + +- Assert that $R = 0$. +- Assert that $c_s$ decrypts using a zero key, and that the resulting opening is valid. +- Decrypt $c_s$ and assert the resulting opening is valid. +- Decrypt $c$ using a zero key and assert the resulting opening is valid. + +If a user wishes to transfer funds to the issuer, it produces an e-cheque with the following modifications: + +- It sets $r = 0$. + +When validating such an e-cheque, validators additionally do the following: + +- Assert that $R = 0$. +- Decrypt $c$ using a zero key and asserting the resulting opening is valid. + +If the issuer wishes to accept transfer funds from a user, it produces an endorsement with the following modifications: + +- It uses a zero key to produce $c_r$. + +When validating such an endorsement, validators additionally do the following: + +- Assert that $c_r$ decrypts using a zero key, and that the resulting opening is valid. + +This design allows for transparent analysis of the issuer's balance and e-cheques. + +## Final notes + +There are several variations of this contract that could be implemented. + +For example, removing the encrypted balance fields would make user balances opaque to the issuer as well, while also +simplifying the design considerably. + +The transfer process can be augmented to include dummy inputs and outputs, using a strategy similar to that +used in [Lelantus Spark](https://ia.cr/2021/1173). This would obfuscate the parties in a transfer, dramatically +improving privacy. + +The use of e-cheques as the primary value transfer vehicle opens up many possibilities for adding additional margin +of error to on-chain financial transactions: +* For example, e-cheques could have a holding time associated with them, to allow parties to validate payments + out-of-band. +* They could have additional claim constraints, which would simplify escrow contracts and swap contracts while + improving security. # Appendix A - Tether USD contract - + ```js {{#include assets/tether.sol}} ``` # Appendix B - Circle USD contract -The Circle USD contract runs as a proxy contract. Therefore the code that is actually active can be changed at any -time. The following is the contract code that was active as of 25 August 2023, deployed to address +The Circle USD contract runs as a proxy contract. Therefore the code that is actually active can be changed at any +time. The following is the contract code that was active as of 25 August 2023, deployed to address [`0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf`](https://etherscan.io/address/0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf). ```js