Merkle trees
In development on testnet
Panther’s Shielded Pool architecture includes the following Merkle trees:
Bus processes UTXOs collectively by batching them
Taxi processes UTXOs individually
Ferry manages UTXO data in the multi-chain Panther ecosystem
In the dApp, users can choose between batched processing (taking the bus) and fast processing (taking the taxi) from the toggle available under the Fee section when submitting a transaction.
Within the “Bus” tree, each leaf is a commitment to:
An “asset” UTXO representing a zAsset (or zNFTs) (essentially an “IOU” for the corresponding underlying collateral locked on the Panther Vault)
“zAccount” UTXO, a record that keeps information about the owner of a zAccount
The name Bus Tree refers to the UTXOs being committed to the tree in batches of up to 64 UTXOs rather than individually. To get on the “bus”, a UTXO needs to be queued and wait until a zMiner processes that queue.
The “bus” approach makes the process more cost-efficient as all UTXOs in a queue share processing costs. As such, the individual cost for a transaction per user is much lower. On the other hand, users are dependent on the zMiner to process the queue. Just as users of a blockchain incentivize miners to pick up their transactions by defining how much they are willing to pay for “gas”, Panther Protocol users define the reward (denominated in $zZKP) that they are willing to pay to the zMiner who processes their UTXOs.
Taxi tree has been released with Stage 5 of the testnet dApp.
To avoid "waiting for the bus," the Taxi tree allows UTXOs to be processed individually to enable near-instant spending. This removes the wait time required to process a batch, with an extra cost tradeoff.
Panther's UTXO encryption/decryption model
Spender encrypts a message to recipient that is published on-chain
All zAccount holders scan the chain for UTXO commitments and attempt to read the message
Only the intended recipient, the account holding the cryptographic key, may read the message and assume spend rights to a UTXO
An essential requirement of Panther’s Shielded Pools is to ensure the untracability of transactions to stakeholders who do not hold the correct keys. To achieve this, the mainnet beta Protocol supports non-interactive transactions. That is, the spender can pass assets to the receiver's zAccount using the receiver’s public read key and the public root spending key available on a public lookup registry maintained by the smart contract.
Every zAsset UTXO has one “owner” or zAccount able to spend it. This is achieved by including a unique public (spending) key in the generation of the UTXO commitment, for which the corresponding private (spending) key is only known by the recipient.
In order to spend a UTXO the owner needs to prove (in Zero-Knowledge) that they hold the spending private key.
The following conventions are applied to formulae:
lowercase letters in formulae bellow denote prime field elements ("scalars") - i.e. private keys
capital letters denote points on the elliptic curve - i.e. the public keys
'*' denotes the multiplication of an elliptic curve point by a scalar, i.e. scalar multiplication
The Protocol uses a shared symmetric key for encryption/decryption of messages with secrets
Secrets. M
are UTXO’s "opening values" for recipients and data for spenders to track past transactions
Messages passed to the smart contract are encrypted:
spender to receiver
spender to self
Sender publishes the Ephemeral key, E
and ciphertext M'
on-chain — formalizing a transaction
Recipient scans chain to extract M
(from M'
using E
) to take ownership of a UTXO
Spender can re-create history by decrypting messages to self, M'
Although spender knows the UTXO’s public key, only the recipient who holds the root spending private key may spend the UTXO
Next, let’s take a closer look at how the cryptography behind this Protocol is implemented.
A "key pair" is a pair composed of a private key and its corresponding public key.
The private key is a big integer ("scalar") from the prime field defined by the Baby Jubjub elliptic curve.
The public key, P
corresponding to the private key, p
is a point on the curve, such that the following equation holds:
P
=p
*G
Where, P
and p
are the public and private keys, and G
is the generator of the group of elliptic curve points.
The so-called Base8
point is used as the generator. This point generates a "commutative group" of curve points that cryptographers call the "Baby Jubjub subgroup".
The ECDH key agreement protocol (over the Baby Jubjub elliptic curve) is used to share the symmetric key and to derive the spending keys of UTXOs.
The following key pairs are essential to the transfer of zAssets in the form of UTXO updates:
Recipient reading key pair (w
, W
= w
* G
): allows the recipient to decode messages with opening values of UTXOs, i.e. knowledge of the private key is needed to decrypt a message.
Root spending key pair (s
, S
= s
* G
): enables spending of a UTXO by the owner, i.e., no matter who generates a UTXO, only the holder of the private key may spend the UTXO.
The term "root" applies as spending keys for UTXOs are "derived" from this key pair.
Nullifier key (n
, N
= n
* G
): required in order to generate the nullifiers for UTXOs.
This key enables compliance, by encoding the nullifier so that information in the Data Safe may reveal whether the UTXO has been spent.
Next, let's consider how spending and encryption keys are derived.
When a zAsset is spent, an output, i.e. a new UTXO is created.
Spender selects two randoms, r
and e
, to derive the spending and message encryption keys.
Spender derives the spending public key S'
for the new UTXO from the recipient's root spending public key S
and the random r
:
S'
=r
*S
Spender creates an ephemeral key using the random (e
) from Step 1:
E
=e
*G
Spender creates the shared key, K
to encrypt the message to the recipient with, based on the recipient’s public reading key, W
and the random, e
:
K
=e
*W
Spender composes the message to the recipient, M
and encrypts the message into the ciphertext, M'
.
5.1 Message composition. The message,
M
contains the information needed to spend the UTXO: random,r
required for the recipient to generate the spending key,S'
and other opening values (such aszAssetId
and the value the UTXO represents).5.2 Message encryption. The spender encrypts the message to the recipient with the shared key,
K
applying the symmetric encryption (applying the methodAES-128-cbc
):M'
= Enc(M
,K
)
Spender calls the Shielded Pool contract to publish the new UTXO as well as the encrypted message to the recipient.
6.1 Publishes the ephemeral key,
E
and the ciphertextM'
.6.2 Using the same encryption key derivation method, but with own reading key instead of the recipient's reading key, the spender encrypts the message "to self". This encrypted message (which only the spender may decrypt) contains data required to reconstruct an audit trail of the spender's transactions.
Taking ownership of a UTXO
The following steps detail how a recipient is able to take ownership of the input UTXO.
1. Scan chain for ciphertexts
Each zAccount holder behaves as it is a potential recipient and scans the chain for ciphertexts, M'
and ephemeral keys, E
.
2. Attempt to decrypt every ciphertext
2.1 Compute the shared key, K
K
=w
*E
Note, if the spender encrypted the message with the recipient’s public reading key (i.e. the message is intended for the recipient), the recipient derives the same shared key that the spender used:
K
=w
*E
=w
* (e
*G
) =e
* (w
*G
) =e
*W
2.2 Decrypt the ciphertext using that key
M
= Dec(M'
,K
)
2.3 Analyze whether the decrypted message, M
is meaningful
If an encrypted message (i.e. the ciphertext and the ephemeral key) was intended for a different recipient, and thus the non-recipient computed the wrong encryption key, the decryption algorithm still returns some random (meaningless) decrypted text. However, the true recipient can easily distinguish if the decrypted text, M
contains properly formed or meaningless data.
So, IF
M
is invalid, message is ignored. IFM
is valid, recipient extracts fromM
the random,r
and other opening values required to spend the UTXO.
2.4 Derive the private spending key for the UTXO
The recipient derives the private spending key for the UTXO:
s'
=r
∙s
here, '∙' denotes multiplication of integers (in the prime field).
Note, that the private spending key derived this way indeed corresponds to its public key that the spender derived:
S'
= (r
∙s
) *G
=r
* (s
*G
) =r
*S
This method provides an efficient encryption/decryption mechanism and a unique spending public key that is unlinkable to other transactions by the same recipient, even given that E is publicly accessible on-chain.
Learn how UTXOs represent assets locked in the Panther vault, zAssets
Understand how users enter a Shielded Pool to create a zAccount and transact with other account holders and the wider DeFi ecosystem
Panther's unique UTXO model
UTXOs are used to represent:
Digital assets (zAssets)
zAccounts
To spend or open a zAsset UTXO the spender must provide a private key
This requirement for a unique private key for each UTXO contributes to the Protocol's privacy layer
underpin the Protocol's privacy-enhancing solution. A UTXO is the of a data package committed to a leaf of a . The commitment of a UTXO on-chain and the data contained therein, is verifiable thanks to a SNARK proof generated at the time of the commit.
The commitment of a UTXO data package on-chain provides an immutable record of a transaction. However, data contained within the UTXO can only be accessed by providing the correct cryptographic solution.
UTXOs are used in different ways within the Protocol, which makes the "unspent transaction" a misnomer in certain instances. Let’s first consider the model that does create "change" or an unspent amount from a transaction by creating a UTXO worth the difference between its value and the amount spent: the zAsset UTXO.
represent digital assets as UTXOs. Every UTXO has one “owner” or zAccount able to spend it. This is achieved by including a public (spending) key in the generation of the UTXO commitment, for which the corresponding private (spending) key is only known by the recipient.
Spending UTXOs
During a spend event, the zAccount holder is either receiving an input UTXO, as in they are the recipient of a digital asset; or they are creating an output UTXO, as in they are spending/sending a digital asset.
When Alice transfers a UTXO to Bob, she transfers the UTXO's read and spend rights to Bob. This means that only Bob's zAccount may open and read the UTXO or spend the UTXO.
If a zAccount holder receives a UTXO, they must successfully decrypt the UTXO’s commitment. If they are able to calculate the child private spending key (childPrivSpendingKey
), then they may spend the UTXO in the future, i.e. transfer ownership of all or part of the value of that UTXO.
This is how the Protocol protects the transactional privacy of the recipient. A transfer of a zAsset from sender to recipient is a non-interactive transfer i.e. the sender and recipient don’t have to communicate in order to facilitate the transaction. The only prerequisite is that the sender knows the recipient’s two root public keys (both spending and reading).
To access the receiver’s public keys, the sender’s zAccount looks up the user's zAccount registry. On zAccount activation, this registry establishes a link between the public root spending key, public reading key, and an EOA (externally owned account) i.e. a wallet address associated with the user. This allows an untracable method for the sender to access the receiver's data.
Shielded Pool contract responsibilities on spend
The Shielded Pool contract performs several checks when Alice sends a UTXO to Bob, it:
Verifies that there is no double-spend of the UTXO
nullifierHash is calculated correctly and has not been used before
Verifies the ZK proof:
the opening of the UTXO commitment is correct
the Merkle proof is correct
Child Public Spending Key is derived from the Child Private Spending Key
Child Private Spending Key is the same input used in the opening of the UTXO commitment
A zAsset transfer is a non-interactive, asynchronous spend/receive event that involves two UTXO models, the zAsset and the zAccount.
Let's consider the outcome of a spend event that results in the transfer of 1 zAsset UTXO.
The Shielded Pool supports the spending and creation of multiple UTXOs, all within a single transaction using a single proof.
As a result of the spender initiating a zAsset transfer to a different zAccount, at least 2 new UTXOs are committed to the Merkle Tree:
Token of transaction: zAsset UTXO created to the value of the transfer amount
With a unique key based on the intended recipient's public key
Token of transaction: zAsset UTXO representing any change, if a 0 amount i.e. no "unspent" value remains, then no UTXO is created
If Alice has a UTXO representing 5 zETH and sends Bob 2 zETH, the unspent amount is represented by at least 1 UTXO to the value of 3 zETH
zAccount UTXO: the sender's zAccount UTXO is updated to deduct the value of asset sent from balance, fee deduction in zZKP, and an increment to their PRP reward
As a result of the receiver (asynchronously) unlocking the UTXO with their key:
zAccount UTXO: receivers zAccount UTXO is updated to reflect the value of asset received
The dApp maintains the user's balance in one UTXO for optimal clarity and efficiency. The preference is to have a single large UTXO instead of numerous smaller ones. This structure allows users to engage any part of their balance in just one transaction, mitigating the necessity for a supplementary MASP transaction to consolidate smaller UTXOs. However, certain circumstances don't permit the merging of UTXOs. For instance, when another user transfers zAsset to the user, the sender generates an independent UTXO, regardless of the recipient's existing UTXOs in the zAsset sent. Similarly, in a zSwap, the user always gets a new UTXO, irrespective of any existing UTXOs in the received asset. In such scenarios, no attempt to merge UTXOs takes place. When handling a deposit transaction, the dApp merges the available largest by amount UTXO with the deposit amount, rather than initiating a new UTXO. As a result, the new UTXO displays the total amount collected. During both deposit and regular transactions, advanced settings should allow users to select the input UTXO for the merge. This becomes particularly relevant when a user intends to transfer tokens to a CEX/DEX, and they must supply a 'proof of innocence' to demonstrate the origin of the funds.
UTXOs are a data storage model that is key to Panther’s Protocol.
Learn about:
The
of UTXOs
Or return to a high-level abstraction of the UTXO and learn about .
the derived identity is entitled to spend the
In the case that the sender chooses to use a service to increase the privacy set of the transaction, further UTXO updates are implemented to pay the Relayer fee.
In development on testnet