UTXOs are a data storage model that are key to Panther's Protocol.
Learn about:
The UTXO model
Encryption and decryption of UTXOs
Or return to a high-level abstraction of the UTXO and learn about zAssets.
Status | Entrypoint |
---|---|
In development on testnet
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 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 an 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.
A high-level overview of how the Protocol follows.
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 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 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 which 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
UTXOs underpin the Protocol's privacy-enhancing solution. A UTXO is the hash of a data package committed to a leaf of a Merkle Tree. 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.
It is the commitment of a UTXO data package on-chain that provides the immutable record of a transaction. However, it is only possible to access data contained within the UTXO 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.
zAssets 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 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 a non-tracable 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
the derived identity is entitled to spend the UTXO
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
In the case that the sender chose to use a Relayer service to increase the privacy set of the transaction, further UTXO updates are implemented to pay the Relayer fee.
Merkle trees
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 the 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 relects 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 UTXO's to be processed individually to enable near instant spending of UTXO’s. This removes the wait time required to process a batch with an extra cost tradeoff.
Status | Entrypoint |
---|---|
In development on testnet