Instead of a bash script being used to interact directly with the FairyRing network, this tutorial uses a bash script that interacts with a newly deployed rust smart contract to prepare transactions, generates unique tIBE-related ids for them with FairyRing, and carries out the typical encryption process. This is all happening within the FairyRing chain. Decryption is carried out such that the keyshares from the FairyRing validators are encrypted with a specific public key associated to a user’s wallet.
feat-auction.
What is the Difference Between Public and Private Decryption?
A core concept within Fairblock is “Public, and Private, Decryption.” Assuming that you have gone through the foundational parts of the docs, you know that the FairyRing validators generate keyshares that are ultimately aggregated to create the Master Decryption Key for a respective encrypted transaction. The default methodology has the Master Decryption Key fully public to anyone watching the FairyRing network, which is fine in certain applications. Some applications may want the Master Decryption Key to be encrypted for one specific user. When this is the case, the keyshares that are ultimately aggregated and used to create the Master Decryption Key, are actually encrypted by the calling user’s wallet public key. The user then can decrypt the keyshares, aggregate them to get the Master Decryption Key, without anyone else onchain knowing. This step can be handled by the front-end so the user doesn’t have to go through extra steps. Thus we have the following definitions:- Public Decryption - The typical user flow where encrypted messages have their Master Decryption Key exposed as soon as it is generated to anyone listening to the FairyRing.
- Private Decryption - The user flow where the validator keyshares, that are used to construct the Master Decryption Key ultimately, are encrypted using the calling user’s wallet’s public key.
What cApps can be Made With Private Decryption and General Conditions?
There really is an endless design space for cApps using Private Decryption and General Conditions with FairyRing. Some prime examples could include:- Private Data Marketplaces for AI Models (user-owned data for AI models) - AI models need data, institutions and users do not want to give data without certain conditions (payment, ethical integrity of the AI model or company, etc.). Private decryption and general conditions provides the tools to create market places where data sets can be encrypted and given conditions before it can be decrypted and used within an AI model.
- Content Behind Conditions and/or Paywalls - Example Idea: FairyFans: cApps where subscribers can pay and/or carry out certain conditions in order to decrypt access to content or the content itself. Imagine all your favorite crypto twitter content creators providing end to end encrypted access to their content, all using decentralized, onchain, tech.
- Trusted Solvers for Intent Systems - cApps that leverage intent systems but require that the solvers used follow specific conditions based on the prepared intent-based transaction. We already see intent apps provide the option to specify to use certain solvers, but that is all anchored in centralized systems. Private decryption and general conditions with fairblock unlocks this feature in a decentralized way.
Key Lessons from this Demo
1. Understood the high level difference between cApp design using “General Conditions” with:- Public Decryption (a future tutorial)
- Private Decryption (this tutorial)
- cApp providing a service, such as generating loot boxes, that are encrypted using FairyRing, and more specifically “FairyRing private decryption,” where the contract sends encrypted keyshares (ultimately the decryption key) to users that pay the required service fee.
- User pays fee, and is able to request the transaction to be decrypted.
- Encrypted keyshares are sent to the user, where the user’s wallet private key is used to encrypt the keyshares. The user then takes the encrypted keyshares, locally decrypts them using their wallet private key, and aggregates them, resulting in the decryption key needed to decrypt the unique passwords offered by the cApp to get through the payment gate.
The common payment-gate pattern is just one example for conditional decryption features. This is really up to you as the cApp developer!
Demo Quick Start
Imagine that you’re building a FairyRing cApp that provides gamers unique loot boxes. These loot boxes are of course unknown until certain conditions are hit, players finally get 100 mushrooms in a side quest, etc. you know how it is. Once the condition is hit and the FairyRing validators are notified, the keyshares are generated for the respective loot box. Since each loot box is unique, having a cApp design that uses private decryption makes sense. The gamer who earns the loot box will provide their public address, and that will be used to encrypt the newly generated keyshares. This way those keyshares won’t be used to create the aggregated keyshare and thus the decryption key unless it is the appropriate gamer with the public-private key pairing for said wallet. This is a powerful smart contract lego piece that pushes cApps into even more exciting territory. To run this demo, simply download this repo, and switch to this specific feature branch,contracts.
make devnet-up spins up a local FairyRing chain on your machine using docker. The same devnet wallets are spun up everytime. As well, the rust smart contract contract.rs is deployed on your local devnet, also at the same address everytime in these tests. That is the smart contract we will be interacting with, we’ll call it the lootbox contract.
Upon running the ./privateDecryptionFairyRingTutorial.sh script, you will see transactions carried out on your devnet in the CLI. It is basically walking through the transaction flow for a lootbox creator encrypting the message containing details about the lootbox, and then a gamer coming along and decrypting it after passing some set of conditions (in this case, a payment).
The steps of the cApp are as follows:
All function calls outlined below are internal to the smart contract, and are done through the public function execute().
- The smart contract function
execute_request_identity()is called. - FairyRing is queried to obtain the identities associated to the LootBox contract.
- The CLI will prompt you for the newly generated
pubkeythat will be used to encrypt the LootBox details. In this example, we simply represent it as a string. - The script then encrypts the transaction using FairyRing. This is an offchain event and it is usually carried out by a front end. The encrypted result will then be output to the CLI.
- The encrypted tx will then be input to the LootBox function
store_encrypted_data(). - Now a user comes along who has earned the LootBox, and thus can call the LootBox contract function
execute_request_keyshare(). In order to do this, the unique id for the respective lootbox needs to be known. You are asked for the pubkey to encrypt the keyshares with, and that will be the public address of your wallet. Simply pick a wallet from the fairyring wallet opsion spun up within your devnet. - The encrypted keyshares will be obtained.
- These keyshares are then locally decrypted using your respective wallet’s private key, and then aggregated to obtain the LootBox decryption key.
- Now the script will call the contract function
decrypt()which ultimately interacts with FairyRing to decrypt the transaction.
- The actual details of how this LootBox would work, and the actual exchanging of tokens to the LootBox smart contract are not in scope for this tutorial. That is for the app developer, but we’re happy to discuss ideas!
Typical Transactions Carried Out by a Smart Contract Using this Pattern
Digging further into the underlying code within this tutorial, let’s go over the general functions within this tutorial and how they can be used in a general sense for your own cApp.Step 1: Generate Unique IDs for Each Transaction
Within this tutorial, the rust contract creates a unique ID for each transaction each time the functionexecute_request_identity() is called.
Unique IDs are generated by creating whatever nomenclature you would like for your specific smart contract transaction, and then communicating with the pep module within FairyRing to obtain further name appendices to ensure it is unique in FairyRing.
Unique IDs can be generated as needed, or they can be generated ahead of time. This is a design decision for the cApp developer.
Key lesson for devs: cApp contracts will need a function that communicates with the
pep module to generate unique IDs for encrypted contract transactions.request_identity() function is shown below:
Place holder values for education purposes:
privateDecryptionFairyRingTutorial.sh bash script:
Step 2: Query Contract for the Identity Supplied and Register the Contract Against the Identity
Now that private IDs can be generated from the smart contract, the next portion of the design flow for a developer is to obtain the public address. This is more-so done by the front end team likely. They would query the chain for the respective ids generated from the smart contract. Place holder values for education purposes:privateDecryptionFairyRingTutorial.sh bash script:
- Register the contract against the identity
privateDecryptionFairyRingTutorial.sh bash script:
Step 3: Local Step: Encrypt Data with Pubkey and Identity
Typically handled by the Front End team, once the transaction details are prepared for said cApp. Imagine the UX consisting of a user interacting with a cApp via metamask, and simply submitting their details, this next step would happen during this time. The transaction details would be encrypted against the master public key, obtained from communicating with the FairyRing chain, to encrypt the transaction. In this tutorial, we simply carry out the transaction in bash commands. Place holder values for education purposes:privateDecryptionFairyRingTutorial.sh bash script:
Step 4: Upload Encrypted Data to Contract:
Recall that in this example, we are imagining a LootBox detail, perhaps the link to its metadata, is encrypted and requires a payment to the contract before a user can access it.Key developer step: Now that the ciphertext is obtained, it can finally be uploaded to the contract to await a condition for it to be decrypted.
privateDecryptionFairyRingTutorial.sh bash script:
Step 5: A Gamer Comes Along and Wants to Purchase the LootBox and Therefore the Specific Encrypted Transaction’s Decryption Key
At this point, we have an encrypted transaction stored within our smart contract and registered properly with FairyRing. Now the condition is simply that a payment is needed to the smart contract. Of course any conditions are up to the smart contract designer.Key developer point: Throughout the smart contract design, it is up to the developer to create conditions and the true custom implementations for their own cApp. Once the condition is hit, the key steps highlighted in this function are to request the private keyshare.Since we are working with private IDs, users who successfully pass the conditions of the smart contract and request the Decryption Key do so in a way where the keyshares that make up the aggregate Decryption Key are encrypted using that user’s wallet public key.
secp_pubkey from devnet FairyRing directly.
secp_pubkey and all other details of the transaction we have been working with up to this point, you can run the below manual bash commands to interact with the smart contract. Really though, the key again here is that the private keyshares are obtained within a rust function for the user. Within said function, there are key calls to FairyRing to obtain the keyshares in an encrypted manner.
Place holder values for education purposes:
privateDecryptionFairyRingTutorial.sh bash script:
Manually Query Contract to See Encrypted KeyShare Details
You can use the below CLI command to query for the encrypted keyshare details. Place holder values for education purposes:privateDecryptionFairyRingTutorial.sh bash script:
Step 6: Decrypt Encrypted Keyshares with Your Private Key and Aggregate Them to Create Decryption Key
Now that the user finally has the encrypted keyshares, they can locally user their wallet’s private key, decrypt the keyshares and aggregate them using FairyRing. This would be another feature that the cApp would provide to the user likely through their front end. Place holder values for education purposes:privateDecryptionFairyRingTutorial.sh bash script:
Step 7: Finally Decrypt the Encrypted Data with Decryption Key from Step 6
Again, this would likely be something offered by the front end for the user. At this point, the user has obtained the decryption key specific to their unique encrypted transaction they have paid for. They now just need to decrypt it, which would simply work with FairyRing behind the scenes. Place holder values for education purposes:privateDecryptionFairyRingTutorial.sh bash script:
Next Steps
Congratulations! You have now seen an example of using a smart contract to encrypt transactions with general conditions and private decryption (private IDs). There are several next steps you can take now.- Check out our other quick starts to see how to build with other networks integrating into FairyRing in the Quick Start.
- Join our Discord and discuss new build ideas! We host frequent developer hours to encourage more novel ideas with confidential computation.
Conditional Encryption/Decryption
In the overview, we saw a general flow for how to interact withfairyring.
In particular, the chain provides functionality on requesting specific encryption keys corresponding to conditions set by the developer.
We provide a more detailed explanation in this section via an example.
Suppose one wishes to create a sealed-bid auction application.
A sealed-bid auction is an auction mechanism in which bids submitted to the auction are not made available to the participants during the course of the auction.
Using fairyring as a mechanism for encrypting the bids, a developer wanting to create this application will take the following steps:
- For each auction, request a new condition id from
fairyring. This condition id will be crucial for encrypting the bids. - Provide users with some functionality to encrypt their bids. This can be done using our
encryptertool. - Accept and store the encrypted bids within the application. This logic will primarily be the responsibility of the application developer.
- Have a way to notify
fairyringthat the auction has concluded. This can be built into the application itself (meaning the chain initiates the request for decryption tofairyring) or can be a simple keeper that notifiesfairyringwhen a certain condition has been met (end of auction). - Relay the decryption key from
fairyringto the application. This can be done using FairyPort, the relayer described in the architecture overview.
Requesting a new condition id
Any user can request a new condition id by running the commandtesting123is the custom identity, it can be anything you want, but it can only be used once per address
Querying identities
You can also query the chain to find the identities that have been generated via the commandRequesting a decryption key
Once encrypted data has been committed by users of your application, you need to notifyfairyring to generate the decryption key.
To start the generation process, one can run the command
req-id is different from the identity, req-id is: {YOUR_ADDRESS}/{IDENTITY}
Let’s say you address is fairy18hl5c9xn5dze2g50uaw0l2mr02ew57zkynp0td and you requested identity testing123
Then your request id would be fairy18hl5c9xn5dze2g50uaw0l2mr02ew57zkynp0td/testing123
- The generated decryption key can only be queried by the identity creator
Decrypting and executing
Using the generated decryption key, we are now able to decrypt previously encrypted data. Thex/pep module provides some functionality to automatically handle the decryption and execution of transactions once a particular decryption key is made available.