Testnet
Update (04.14.23)
The Proof-of-Concept version is compatible with condition evaluation on Mumbai (Polygon testnet) via the Threshold node-facing Tapir testnet.
An additional node-facing testnet (Oryx) now points to Polygon mainnet. This means access conditions can be checked and verified by Threshold nodes if they pertain to state on either of the Polygon networks. Note that it is not possible to evaluate conditions on both Mumbai and Polygon mainnet in a single runtime, because pre-configuration requires choosing between Tapir and Oryx.
This tutorial is a quick way for developers to learn about the Threshold Access Control service by building with the Proof-of-Concept (PoC) version of Conditions-Based Decryption.
Note that the underlying trust assumptions vary between versions and technologies; these are explained in detail for the CBD Proof-of-Concept, CBD Mainnet, and PRE Mainnet versions.
To begin, we need to install the
nucypher-ts
library:yarn add @nucypher/nucypher-ts
For this tutorial we'll need a few extra packages:
yarn add ethers @metamask/detect-provider
Next, we will create a
Cohort
based on our risk preferences. A Cohort
is a group of nodes that work together to control access to data. Threshold and Shares are two parameters used to create a Cohort
. For example, a 3-of-5 Cohort
needs at least 3 of the 5 members to provide shares to access the original data.To create a
Cohort
, use the following code:import { Cohort } from '@nucypher/nucypher-ts';
const config = {
threshold: 3,
shares: 5,
porterUri: 'https://porter-tapir.nucypher.community',
};
const newCohort = await Cohort.create(config);
Notice that we provided a
porterUri
parameter. Porter is a web-based service that interacts with nodes on the network on behalf of applications. It acts as an "Infura for Threshold Access Control". In this example, we used a Porter endpoint for the tapir
testnet.Conditions are the requirements for a data requester or recipient to access the plaintext data – i.e. what they will need to prove later to gain decryption rights.
The
ERC721Ownership
condition checks the owner of a given token ID. It can be customized by using the ownerOf
contract method and comparing it with the requestor's signature. For more information, see the References section.CBD allows developers to enforce conditional access at various runtime stages, depending on what makes the most sense for the use case. At this point, we will add the default Conditions, which will only gate-keep access if no other conditions are included later at encryption time. This is explained further in Condition Hierarchies.
We will now specify the conditions that must be met to access the data. In this tutorial, we will require that the requester owns an ERC721 token with a token ID of 5954.
import { Conditions } from '@nucypher/nucypher-ts';
const NFTOwnership = new Conditions.ERC721Ownership({
contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',
chain: 5, // Tapir network uses Görli testnet
parameters: [5954],
});
There are multiple Condition types and it is possible to combine multiple conditions into a ConditionSet.
import { Conditions, ConditionSet } from '@nucypher/nucypher-ts';
const conditions = new ConditionSet([
NFTOwnership,
// Other conditions can be added here
]);
We will now combine the
Cohort
, ConditionSet
, and any other necessary parameters into a Strategy
. Strategies are a convenient way to bundle together frequently used configurations, including specific combinations of network parameters and conditionality.import { Strategy } from '@nucypher/nucypher-ts';
const newStrategy = Strategy.create(newCohort, conditions);
Next, we will deploy this
Strategy
to the Threshold Network. To do that, we're going to transact on Polygon Mumbai:import detectEthereumProvider from '@metamask/detect-provider';
import { providers } from 'ethers';
const MMprovider = await detectEthereumProvider();
const mumbai = providers.getNetwork(80001);
const web3Provider = new providers.Web3Provider(MMprovider, mumbai);
const newDeployed = await newStrategy.deploy('test', web3Provider);
Deploying a
Strategy
requires writing to the blockchain. This requires a wallet funded with testnet MATIC and connection to the blockchain via a provider
(e.g. MetaMask).For more information about customizing and reusing
Cohort
, Condition
, and Strategy
objects, see the References page in the documentation.We can now encrypt data using the newly deployed
Strategy
. At this point, we can also specify new conditions on which data access will be predicated. These will take a higher precedence and override the default conditions contained in the Strategy. For this example, let's make it so the overriding requirement is that the requester's wallet hold a minimum number (three) of NFTs. The NFTs are from the same collection that we specified in Step 3. Note that Threshold nodes will check this new condition using the balanceOf
method.To encrypt the plaintext:
const NFTBalanceConfig = {
contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D',
standardContractType: 'ERC721',
chain: 5,
method: 'balanceOf',
parameters: [':userAddress'],
returnValueTest: {
comparator: '>=',
value: 3,
},
};
const NFTBalance = new Conditions.Condition(NFTBalanceConfig);
const encrypter = newDeployed.encrypter;
const plaintext = 'this is a secret';
const encryptedMessageKit = encrypter.encryptMessage(
plaintext,
new ConditionSet([NFTBalance])
);
The resulting
encryptedMessageKit
contains the encrypted data and associated condition(s).Finally, we will test the conditional access control service by requesting decryption rights:
const decrypter = newDeployed.decrypter;
const conditionContext = conditions.buildContext(web3Provider);
const decryptedMessage = await decrypter.retrieveAndDecrypt(
[encryptedMessageKit],
conditionContext
);
At decryption time, the requester will be asked to verify their address by signing a message in MetaMask. This is where
conditionContext
comes into play – if the requester's address controls the minimum number (or greater) of the specified NFT, they are eligible to receive the requisite number of decryption fragments. By assembling these fragments, they are able to decrypt and view the plaintext encrypted in the previous step. Note that the requester does not need to manually sign the next time they seek access to the data, as their client can temporarily cache their signature. Fresh plaintexts encrypted under any conditions involving the same wallet address are automatically accessible to any requester who has signed at least once, provided they still fulfill the (new) conditions, and the cached signature has not expired.
The following samples showcase integrations with React-based web apps, and serve as an 'end-to-end' reference for creating conditions-based encryption & decryption:
Last modified 1mo ago