# Protocol Overview

In this section, we provide an overview of the underlying cryptographic protocol for the confidential Token extension. An understanding of the details that are discussed in the following subsections is not needed to actually use the confidential extension. We refer to the previous section for a quick start guide.

We note that this overview exists mainly to provide the design intuition behind the underlying cryptography that is used in the confidential extension. Some parts of the description of the protocol in the overview could differ from the actual implementation. We refer to the subsequent subsections, the source code, and the documentation within for the precise details of the underlying cryptography.

## Tokens with Encryption and Proofs

The main state data structures that are used in the Token program are `Mint`

and
`Account`

. The `Mint`

data structure is used to store the global information for
a class of tokens.

`/// Mint data.`

struct Mint {

mint_authority: Option<Pubkey>,

supply: u64,

... // other fields omitted

}

The `Account`

data structure is used to store the token balance of a user.

`/// Account data.`

struct Account {

mint: Pubkey,

owner: Pubkey,

amount: u64,

... // other fields omitted

}

Users can initialize these two data structures with the `InitializeMint`

and
`InitializeAccount`

instructions. There are a number of additional instructions
that users can use to modify these states. For this overview, we focus on the
`Transfer`

instruction. For the sake of simplicity in this section, let us model
a `Transfer`

instruction with the following structure.

`/// Transfer instruction data`

///

/// Accounts expected:

/// 0. `[writable]` The source account.

/// 1. `[writable]` The destination account.

/// 2. `[signer]` The source account's owner.

struct Transfer {

amount: u64,

}

### Encryption

Since an `Account`

state is stored on chain, anyone can look up the balance that
is associated with any user. In the confidential extension, we use the most
basic way to hide these balances: encrypt them using a public key encryption
scheme (PKE). For simplicity, let us model a public key encryption scheme with
the following syntax.

`trait PKE<Message> {`

type SecretKey;

type PublicKey;

type Ciphertext;

keygen() -> (SecretKey, PublicKey);

encrypt(PublicKey, Message) -> Ciphertext;

decrypt(SecretKey, Ciphertext) -> Message;

}

Now, consider the following example of an `Account`

state.

`Account {`

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 5vBrLAPeMjJr9UfssGbjUaBmWtrXTg2vZuMN6L4c8HE6,

amount: 50,

...

}

To hide the balance, we can encrypt the balance under the account owner's public key before storing it on chain.

`Account {`

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 5vBrLAPeMjJr9UfssGbjUaBmWtrXTg2vZuMN6L4c8HE6, // pubkey_owner

amount: PKE::encrypt(pubkey_owner, 50), // amount encrypted

...

}

We can similarly use encryption to hide transfer amounts in a transaction. Consider the following example of a transfer instruction. To hide the transaction amount, we can encrypt it under the sender's public key before submitting it to the chain.

`Transfer {`

amount: PKE::encrypt(pubkey_owner, 10),

}

By simply encrypting account balances and transfer amounts, we can add confidentiality to the Token program.

### Linear homomorphism

One problem with this simple approach is that the Token program cannot deduct or
add transaction amounts to accounts as they are all in encrypted form. One way
to resolve this issue is to use a class of encryption schemes that are *linearly
homomorphic* such as the ElGamal encryption scheme. An encryption scheme is
linearly homomorphic if for any two numbers `x_0`

, `x_1`

and their encryptions
`ct_0`

, `ct_1`

under the same public key, there exist ciphertext-specific add
and subtract operations such that

`let (sk, pk) = PKE::keygen();`

let ct_0 = PKE::encrypt(pk, x_0);

let ct_1 = PKE::encrypt(pk, x_1);

assert_eq!(x_0 + x_1, PKE::decrypt(sk, ct_0 + ct_1));

In other words, a linearly homomorphic encryption scheme allows numbers to be
added and subtracted in encrypted form. The sum and the difference of the
individual encryptions of `x_0`

, `x_1`

results in a ciphertext that is
equivalent to an encryption of the sum and the difference of the numbers `x_0`

and `x_1`

.

By using a linearly homomorphic encryption scheme to encrypt balances and transfer amounts, we can allow the Token program to process balances and transfer amounts in encrypted form. As linear homomorphism holds only when ciphertexts are encrypted under the same public key, we require that a transfer amount be encrypted under both the sender and receiver public keys.

`Transfer {`

amount_sender: PKE::encrypt(pubkey_sender, 10),

amount_receiver: PKE::encrypt(pubkey_receiver, 10),

}

Then, upon receiving a transfer instruction of this form, the token program can subtract and add ciphertexts to the source and destination accounts accordingly.

`Account {`

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 5vBrLAPeMjJr9UfssGbjUaBmWtrXTg2vZuMN6L4c8HE6, // pubkey_sender

amount: PKE::encrypt(pubkey_sender, 50) - PKE::encrypt(pubkey_sender, 10),

...

}

`Account {`

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7, // pubkey_receiver

amount: PKE::encrypt(pubkey_receiver, 50) + PKE::encrypt(pubkey_receiver, 10),

...

}

### Zero-knowledge proofs

Another problem with encrypting account balances and transfer amounts is that the token program cannot check the validity of a transfer amount. For example, a user with an account balance of 50 tokens should not be able to transfer 70 tokens to another account. For regular SPL tokens, the token program can easily detect that there are not enough funds in a user's account. However, if account balances and transfer amounts are encrypted, then these values are hidden to the token program itself, preventing it from verifying the validity of a transaction.

To fix this, we require that transfer instructions include zero-knowledge proofs
that validate their correctness. Put simply, zero-knowledge proofs consist of
two pair of algorithms `prove`

and `verify`

that work over public and private
data. The `prove`

algorithm generates a "proof" that certifies that some
property of the public and private data is true. The `verify`

algorithm checks
that the proof is valid.

`trait ZKP<PublicData, PrivateData> {`

type Proof;

prove(PublicData, PrivateData) -> Proof;

verify(PublicData, Proof) -> bool;

}

A special property of a zero-knowledge proof system is that a proof does not reveal any information about the actual private data.

In a transfer instruction, we require the following special classes of zero-knowledge proofs.

*Range proof*: Range proofs are special types of zero-knowledge proof systems that allow users to generate a proof`proof`

that a ciphertext`ct`

encrypts a value`x`

that falls in a specified range`lower_bound`

,`upper_bound`

:- For any
`x`

such that`lower_bound <= x < upper_bound`

:

`let ct = PKE::encrypt(pk, x);`

let public_data = (pk, ct);

let private_data = (sk, x);

let proof = RangeProof::prove(public_data, private_data);

assert_eq!(RangeProof::verify(public_data, proof), true);- Let
`x`

be any value that falls out of the bounds. Then for any`proof: Proof`

:

`let ct = PKE::encrypt(pk, x);`

let public_data = (pk, ct);

assert_eq!(RangeProof::verify(public_data, proof), false);The zero-knowledge property guarantees that the generated proof does not reveal the actual value of the input

`x`

, but only the fact that`lower_bound <= x < upper_bound`

.In the confidential extension, we require that a transfer instruction includes a range proof that certifies the following:

The proof should certify that there are enough funds in the source account. Specifically, let

`ct_source`

be the encrypted balance of a source account and`ct_transfer`

be the encrypted transfer amount. Then we require that`ct_source - ct_transfer`

encrypts a value`x`

such that`0 <= x < u64::MAX`

.The proof should certify that the transfer amount itself is a positive 64 bit number. Let

`ct_transfer`

be the encrypted amount of a transfer. Then the proof should certify that`ct_transfer`

encrypts a value`x`

such that`0 <= x < u64::MAX`

.

- For any
*Equality proof*: Recall that a transfer instruction contains two ciphertexts of the transfer value`x`

: a ciphertext under the sender public key`ct_sender = PKE::encrypt(pk_sender, x)`

and one under the receiver public key`ct_receiver = PKE::encrypt(pk_receiver, x)`

. A malicious user can encrypt two different values for`ct_sender`

and`ct_receiver`

.Equality proofs are special types of zero-knowledge proof systems that allow users to prove that two ciphertexts

`ct_0`

,`ct_1`

encrypt a same value`x`

. In the confidential extension program, we require that a transfer instruction contains an equality proof that certifies that the two ciphertexts encrypt the same value.The zero-knowledge property guarantees that

`proof_eq`

does not reveal the actual values of`x_0`

,`x_1`

but only the fact that`x_0 == x_1`

.

We formally model and specify these algorithms in the subsequent sections.

## Usability Features

### Encryption key

In the previous section, we used the public key of the account owner to encrypt the balance of an account. In the actual implementation of the confidential extension program, we use a separate account-specific encryption key to encrypt the account balances.

`Account {`

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 5vBrLAPeMjJr9UfssGbjUaBmWtrXTg2vZuMN6L4c8HE6,

encryption_key: mpbpvs1LksLmdMhCEzyu5UEWEb3dsRPbB5, // pke_pubkey

amount: PKE::encrypt(pke_pubkey, 50),

...

}

The account-specific `encryption_key`

can be set by the owner of the account
using the `ConfidentialTransferInstruction::ConfigureAccount`

instruction. A
corresponding secret key can be stored privately on a client-side wallet or
can also be deterministically derived from an owner signing key.

In general, a direct re-use of a signing key for encryption is discouraged for potential vulnerabilities. The confidential extension is designed to be as general as possible. Separate dedicated keys for signing transactions and decrypting transaction amounts allow for a more flexible interface.

In a potential application, the decryption key for specific accounts can be
shared among multiple users (e.g. regulators) that should have access to an
account balance. Although these users can decrypt account balances, only the
owner of the account that has access to the owner signing key can sign a
transaction that initiates a transfer of tokens. The owner of an account can
update the account with a new encryption key using the `ConfigureAccount`

.

### Global auditor

As separate decryption keys are associated with each user accounts, users can
provide read access to balances of *specific* accounts to potential auditors.
The confidential extension also allows a *global* auditor feature that can be
optionally enabled for mints. Specifically, in the confidential extension, the
mint data structure maintains an additional global auditor encryption key. This
auditor encryption key can be specified when the mint is first initialized and
updated via the `ConfidentialTransferInstruction::ConfigureMint`

instruction. If
the transfer auditor encryption key in the mint is not `None`

, then any transfer
instruction must additionally contain an encryption of the transfer amount under
the auditor's encryption key.

`Transfer {`

amount_sender: PKE::encrypt(pke_pubkey_sender, 10),

amount_receiver: PKE::encrypt(pke_pubkey_receiver, 10),

amount_auditor: PKE::encrypt(pke_pubkey_auditor, 10),

range_proof: RangeProof,

equality_proof: EqualityProof,

...

}

This allows any entity with a corresponding auditor secret key to be able to decrypt any transfer amounts for a particular mint.

Similarly to how a dishonest sender can encrypt inconsistent transfer amounts
under the source and destination keys, it can encrypt inconsistent transfer
amount under the auditor encryption key. If the auditor encryption key is not
`None`

in the mint, then the token program requires that a transfer amount in a
transfer instruction contain additional zero-knowledge proof that certifies that
the encryption is done consistently.

### Pending and available balance

One way an attacker can disrupt the use of a confidential extension account is
by using *front-running*. Zero-knowledge proofs are verified with respect to the
encrypted balance of an account. Suppose that a user Alice generates a proof
with respect to her current encrypted account balance. If another user Bob
transfers some tokens to Alice, and Bob's transaction is processed first, then
Alice's transaction will be rejected by the Token program as the proof will not
verify with respect to the newly updated account state.

Under normal conditions, upon a rejection by the program, Alice can simply look
up the newly updated ciphertext and submit a new transaction. However, if a
malicious attacker continuously floods the network with a transfer to Alice's
account, then the account may theoretically become unusable. To prevent this
type of attack, we modify the account data structure such that the encrypted
balance of an account is divided into two separate components: the *pending*
balance and *available* balance.

`let ct_pending = PKE::encrypt(pke_pubkey, 10);`

let ct_available = PKE::encryption(pke_pubkey, 50);

Account {

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 5vBrLAPeMjJr9UfssGbjUaBmWtrXTg2vZuMN6L4c8HE6,

encryption_key: mpbpvs1LksLmdMhCEzyu5UEWEb3dsRPbB5,

pending_balance: ct_pending,

account_balance: ct_available,

...

}

Any outgoing funds from an account is subtracted from its available balance. Any incoming funds to an account is added to its pending balance.

As an example, consider a transfer instruction that moves 10 tokens from a sender's account to a receiver's account.

`let ct_transfer_sender = PKE::encrypt(pke_pubkey_sender, 10);`

let ct_transfer_receiver = PKE::encrypt(pke_pubkey_receiver, 10);

let ct_transfer_auditor = PKE::encrypt(pke_pubkey_auditor, 10);

Transfer {

amount_sender: ct_transfer_sender,

amount_receiver: ct_transfer_receiver,

amount_auditor: ct_transfer_auditor,

range_proof: RangeProof,

equality_proof: EqualityProof,

...

}

Upon receiving this transaction and after verifying, the Token program subtracts the encrypted amount from the sender's available balance and adds the encrypted amount to the receiver's pending balance.

`Account {`

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 5vBrLAPeMjJr9UfssGbjUaBmWtrXTg2vZuMN6L4c8HE6,

encryption_key: mpbpvs1LksLmdMhCEzyu5UEWEb3dsRPbB5,

pending_balance: ct_sender_pending,

available_balance: ct_sender_available - ct_transfer_sender,

...

}

This modification removes the sender's ability to change the receiver's available balance of a source account. As range proofs are generated and verified with respect to the available balance, this prevents a user's transaction from being invalidated due to a transaction that is generated by another user.

An account's pending balance can be merged into its available balance via the
`ApplyPendingBalance`

instruction, which only the owner of the account can
authorize. Upon receiving this instruction and after verifying that the owner of
the account signed the transaction, the token program adds the pending balance
into the available balance.

`Account {`

mint: Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB,

owner: 5vBrLAPeMjJr9UfssGbjUaBmWtrXTg2vZuMN6L4c8HE6,

encryption_key: mpbpvs1LksLmdMhCEzyu5UEWEb3dsRPbB5,

pending_balance: ct_pending_receiver - ct_transfer_receiver,

available_balance: ct_available_receiver,

...

}

## Cryptographic Optimizations

### Dealing with discrete log

A well-known limitation of using linearly-homomorphic ElGamal encryption is the
inefficiency of decryption. Even with a proper secret key, in order to recover
the originally encrypted value, one must solve a computational problem called
the *discrete logarithm*, which requires an exponential time to solve. In the
confidential extension program, we address this issue in the following two ways:

- Transfer amounts are restricted to 48-bit numbers.
- Transfer amounts and account pending balances are encrypted as two independent ciphertexts.
- Account available balances are additionally encrypted using a symmetric encryption scheme.

We refer to the subsequent sections and the documentation in the source code for additional details.

### Twisted ElGamal encryption

A key challenge in designing any private payment system is minimizing the size
of a transaction. In the confidential extension, we make a number of
optimizations that reduces the transaction size. Among these optimizations, a
significant amount of savings stem from the use of the *twisted* ElGamal
encryption (formulated in CMTA19). The
twisted ElGamal encryption is a simple variant of the standard ElGamal
encryption scheme where a ciphertext is divided into two components:

- A
*Pedersen commitment*of the encrypted message, which is independent of any ElGamal public key. - A
*decryption handle*that encodes the encryption randomness with respect to a specific ElGamal public key, and is independent of the encrypted message.

We provide the formal details of the twisted ElGamal encryption in the subsequent sections.