Confidential Payments Overview

Oracle Blockchain Platform Digital Assets Edition uses Pedersen commitments to keep data secure.

Zero-Knowledge Proofs

Zero-knowledge proofs (ZKPs) are cryptographic protocols that let an honest prover demonstrate a statement's truth to a verifier without revealing any additional information. A dishonest prover cannot convince a verifier that a false statement is true. ZKPs ensure that no sensitive data is revealed beyond the validity of the claim. ZKPs make it possible to prove specific properties of a secret value, such as confirming that it falls within a range or is the result of a specific computation, without revealing the value itself.

Pedersen Commitments

Zero-knowledge proofs depend on commitment schemes, where someone can commit to a certain value while keeping it hidden from others. Oracle Blockchain Platform Digital Assets Edition uses Pedersen commitments. Pedersen commitments use random values known as blinding factors along with elliptic-curve cryptography to ensure both secrecy and immutability. Pedersen commitments are homomorphic; in other words, they support performing mathematical operations such as addition and subtraction on commitments while preserving the relationships between the underlying values. Pedersen commitments are used in private transaction protocols, secure multiparty computation, and anonymous credential systems.

Working with Confidential Payments

You use the Confidential-Transaction header in a transaction request to enable the confidential payments feature.

When you send the Confidential-Transaction header set to true in a request, the REST proxy in Oracle Blockchain Platform generates a unique random number (the blinding factor required for a Pedersen commitment) and passes it in the transient map. This is used by the chaincode to process the input and to store the sensitive information in a private data collection rather than the state database.

Transaction Information

All token operations (account creation, minting, transfer, burning, and so on) generate a transaction record so that transaction history can be tracked. Transaction data is stored in two place: the public ledger (state database) and a private data collection. With confidential payments, the sensitive data is stored in clear text only in the private data collections of the organizations involved in the transaction. The following table shows where transaction values are stored for any given transaction ID.

Public Ledger (State Database) Private Data Collection
  • assetType
  • transaction_id
  • token_id
  • from_account_id
  • from_account_balance (encrypted)
  • from_account_onhold_balance (encrypted)
  • from_account_onhold_burn_balance (encrypted)
  • to_account_id
  • to_account_balance (encrypted)
  • to_account_onhold_balance: (encrypted)
  • to_account_onhold_burn_balance (encrypted)
  • transaction_type
  • amount (encrypted)
  • timestamp
  • number_of_sub_transactions
  • holding_id
  • sub_transaction
  • sub_transaction_type
  • category
  • description
  • global_transaction_id
  • assetType
  • transaction_id
  • from_account_balance
  • from_account_onhold_balance
  • from_account_onhold_burn_balance
  • to_account_balance
  • to_account_onhold_balance
  • to_account_onhold_burn_balance
  • amount
  • blindingFactor
  • version

Token Account Information

Similarly, information about token accounts is stored in the public ledger and a private data collection. The following table shows where transaction values are stored for any given token account ID.

Public Ledger (State Database) Private Data Collection
  • assetType
  • account_id
  • org_id
  • token_type
  • token_id
  • token_name
  • balance (encrypted)
  • onhold_balance (encrypted)
  • onhold_burn_balance (encrypted)
  • application_groups
  • bapAccountVersion
  • assetType
  • account_id
  • user_id
  • balance
  • onhold_balance
  • onhold_burn_balance
  • balanceBlindingFactor
  • onHoldBalanceBlindingFactor
  • onHoldBurnBalanceBlindingFactor
  • max_daily_amount
  • daily_amount
  • max_daily_transactions
  • daily_transactions
  • current_date
The following data is stored in the public ledger:
  • The account_id key, which is unique for every user in the system. Typicall it is the SHA-256 hash of the orgId (MSP ID) and userId (user name or email) combined with other prefixes or suffixes.
  • The public ledgers stores all of the account keys of any user. Users can have multiple fungible token accounts, one for each token.
  • Token details are stored in the token_type, token_id and token_name fields.
  • The balance and onhold_balance fields hold the Pedersen commitment hash that represents the actual text value of balance that is stored in the private data collection.
The following data is stored in the private data collection:
  • The account_id key, described previously.
  • The balance and onhold_balance fields hold the actual text value of those balances.
  • The balance_blinding_factor and on_hold_balance_blinding_factor fields store the random key used for creating the balance and onhold_balance commitments stored in the public ledger.
  • Daily transaction details are stored in the max_daily_amount, daily_amount, max_daily_transactions, daily_transactions, and current_date fields.

Typical Operations with Confidential Payments

The workflow when you mint, transfer, hold, or burn tokens while using confidential payments includes additional steps to ensure data integrity.

Minting Tokens

To mint tokens, a user with the minter role calls the issueTokens API.
  1. In the confidential payments case, the issueTokens method takes the token_id value as input, and also requires a blinding factor, which is sent from the REST proxy along with the token quantity passed in the transient map.
  2. After verification, the Pedersen commitment is generated from the blinding factor and the quantity of tokens to mint.
  3. The overall quantity is then updated in both the public ledger and the private data collection. In the public ledger, the Pedersen commitment that represents the existing balance is increased with the Pederson commitment that represents the quantity of tokens minted (issued). The actual value of the balance stored in the private data collection is increased accordingly.
  4. The blinding factor is also updated, so that at any time the balance and blinding factor in the private data collection can be used to verify the corresponding commitment stored in the public ledger.

Transferring Tokens Within an Organization

To transfer tokens within an organization, the sender calls the transferTokens API.
  1. In the confidential payments case, the transferTokens method takes the token_id and optional parameters as input, and also requires a blinding factor, which is sent from the REST proxy along with the userId and orgId values for the receiver and the token quantity passed in the transient map.
  2. After verification, the Pedersen commitment is generated from the blinding factor and the quantity of tokens to transfer.
  3. The quantity of tokens in the sender's account is decreased and the quantity in the receiver's account is increased, in both the public ledger and the private data collection. In the public ledger, the Pedersen commitment that represents the sender's existing balance is decreased by the Pedersen commitment that represents the quantity of tokens transferrerd. For the receiver, the balance is increased in the same way. The actual values of the sender's balance and the receiver's balance are updated in the private data collection.
  4. The blinding factor is also updated, so that at any time the balance and blinding factor in the private data collection can be used to verify the corresponding commitment stored in the public ledger.

Transferring Tokens Between Organizations

To transfer tokens between organizations, the sender calls the holdTokens API to extract the transfer amount and place it on hold.
  1. In the confidential payments case, the holdTokens method takes the token_id, operation_id, and expiration_time values as input, and also requires a blinding factor, which is sent from the REST proxy along with the notary_user_id, notary_org_id, to_user_id, and to_org_id values and the token quantity passed in the transient map.
  2. After verification, the Pedersen commitment is generated from the blinding factor and the quantity of tokens to hold.
  3. A hold object and a sender object are created.
  4. The balance commitments, actual balances, and transaction objects are saved to the public ledger and to the private data collections for both organizations, which updates all of the balances accordingly.

Holding Tokens

As mentioned previously, hold operations are used to transfer tokens between organizations. Hold operations use hold, sender, and receiver objects, similar to the transaction and token account objects that store data in both the public ledger and the private data collection. Hold operations can also be used for transfers in the same organization, in which case sender and receiver objects are not created and the transfer occurs as a regular Token Taxonomy Framework operation using Pedersen commitments. The following table shows where transaction values are stored for any given hold transaction ID.

In confidential mode, transfers between organizations involve two private data collections. Instead of modifying the account key/value pair, for debits a sender object is created during the hold operation and for credits a receiver object is created (when the executeHoldReceiver API runs). The balance is placed in the sender object. The credited amount is placed in the receiver object, which is assigned to the recipient. When the debit operation is complete, the sender object is no longer in use and can be deleted. Similarly, after the balance moves from the receiver object to the recipient's account, the receiver object can be deleted.

Public Ledger (State Database) Private Data Collection
  • operation_id
  • token_name
  • operation_type
  • status
  • assetType
  • holding_id
  • from_account_id
  • from_org_id
  • to_account_id
  • notary_account_id
  • token_id
  • quantity (encrypted)
  • time_to_expiration
  • sender_id
  • category
  • description
  • quantity
  • blinding_factor
  • assetType
  • holding_id

The following table shows the information for the sender object that is created for inter-organizational transfers and holds.

Public Ledger (State Database) Private Data Collection
  • assetType
  • sender_id
  • operation_id
  • account_id
  • transaction_id
  • quantity (encrypted)
  • version
  • assetType
  • sender_id
  • blindingFactor

The following table shows the information for the receiver object that is created for inter-organizational transfers and holds.

Public Ledger (State Database) Private Data Collection
  • assetType
  • receiver_id
  • operation_id
  • account_id
  • transaction_id
  • quantity (encrypted)
  • version
  • assetType
  • receiver_id
  • blindingFactor
  1. In the confidential payments case, the holdTokens method takes the token_id and optional parameters as input, and also requires a blinding factor, which is sent from the REST proxy along with the userId and orgId values for the receiver and the token quantity passed in the transient map.
  2. The hold operation must be approved or rejected by a notary user.
    • If the notary user approves the hold operation, the quantity is debited from the sender and hold objects and a transaction object is created. A receiver object is created as well and the quantity is credited to the receiver object.
    • If the notary user rejects the hold, the held quantity is credited back to the sender's account and a transaction object is created.
  3. The balance commitments, actual balances, and transaction objects are saved to the public ledger and to the private data collection, which updates all of the balances accordingly.

Burning Tokens

To burn tokens, a user with the burner role calls the burnTokens API.
  1. In the confidential payments case, the burnTokens method takes the token_id value as input, and also requires a blinding factor, which is sent from the REST proxy along with the token quantity passed in the transient map.
  2. After verification, the Pedersen commitment is generated from the blinding factor and the quantity of tokens to burn.
  3. The overall quantity is then updated in both the public ledger and the private data collection. In the public ledger, the Pedersen commitment that represents the existing balance is decreased with the Pederson commitment that represents the quantity of tokens burned. The actual value of the balance stored in the private data collection is decreased accordingly.
  4. The blinding factor is also updated, so that at any time the balance and blinding factor in the private data collection can be used to verify the corresponding commitment stored in the public ledger.