diff --git a/tutorials/acl-usage-examples.mdx b/tutorials/acl-usage-examples.mdx index a4124ec..8760f52 100644 --- a/tutorials/acl-usage-examples.mdx +++ b/tutorials/acl-usage-examples.mdx @@ -23,11 +23,14 @@ The contract that creates the value for the first time will automatically get ow ```solidity // Contract A -function doAdd(InEuint32 input1, InEuint32 input2) { - euint32 handle1 = FHE.asEuint32(input1); // Contract A gets temporary ownership of handle1 - euint32 handle2 = FHE.asEuint32(input2); // Contract A gets temporary ownership of handle2 - - euint32 result = FHE.add(handle1, handle2); // possible because Contract A has ownership of handle1 and handle2 +function doAdd(externalEuint32 input1, externalEuint32 input2, bytes calldata inputProof) { + // Both inputs share one batch signature, so convert them together + externalEuint32[] memory inputs = new externalEuint32[](2); + inputs[0] = input1; + inputs[1] = input2; + euint32[] memory handles = FHE.asEuint32s(inputs, inputProof); // Contract A gets temporary ownership of both + + euint32 result = FHE.add(handles[0], handles[1]); // possible because Contract A has ownership of both } ``` @@ -44,11 +47,14 @@ contract A { private euint32 result; private euint32 handle1; - function doAdd(InEuint32 input1, InEuint32 input2) { - handle1 = FHE.asEuint32(input1); // Contract A gets temporary ownership of handle1 - euint32 handle2 = FHE.asEuint32(input2); // Contract A gets temporary ownership of handle2 + function doAdd(externalEuint32 input1, externalEuint32 input2, bytes calldata inputProof) { + externalEuint32[] memory inputs = new externalEuint32[](2); + inputs[0] = input1; + inputs[1] = input2; + euint32[] memory handles = FHE.asEuint32s(inputs, inputProof); // temporary ownership of both - result = FHE.add(handle1, handle2); // Contract A gets temporary ownership of result + handle1 = handles[0]; + result = FHE.add(handles[0], handles[1]); // Contract A gets temporary ownership of result FHE.allowThis(result); // result is allowed for future transactions } @@ -71,8 +77,8 @@ To decrypt a ciphertext offchain via the decryption network, the issuer must be contract A { private mapping(address => euint32) balances; - function transfer(InEuint32 _amount, address to) { - euint32 amount = FHE.asEuint32(_amount); + function transfer(externalEuint32 _amount, bytes calldata inputProof, address to) { + euint32 amount = FHE.asEuint32(_amount, inputProof); balances[msg.sender] = FHE.sub(balances[msg.sender], amount); balances[to] = FHE.add(balances[to], amount); @@ -97,8 +103,8 @@ You can also allow other contracts to use your ciphertexts, either persistently ```solidity contract A { - function doAdd(InEuint32 input1) { - euint32 handle1 = FHE.asEuint32(input1); // Contract A gets temporary ownership of handle1 + function doAdd(externalEuint32 input1, bytes calldata inputProof) { + euint32 handle1 = FHE.asEuint32(input1, inputProof); // Contract A gets temporary ownership of handle1 FHE.allowTransient(handle1, addressB); // Contract B is allowed to use handle1 in this transaction alone // or @@ -120,8 +126,8 @@ Use `FHE.allowTransient()` when you only need to grant access for a single trans When modifying encrypted values that users need to access: ```solidity -function updateBalance(address user, InEuint32 amount) public { - euint32 encryptedAmount = FHE.asEuint32(amount); +function updateBalance(address user, externalEuint32 amount, bytes calldata inputProof) public { + euint32 encryptedAmount = FHE.asEuint32(amount, inputProof); balances[user] = FHE.add(balances[user], encryptedAmount); // Allow contract to use in future transactions @@ -137,8 +143,8 @@ function updateBalance(address user, InEuint32 amount) public { A common pattern is to allow the message sender: ```solidity -function submitEncryptedData(InEuint32 data) public { - euint32 encryptedData = FHE.asEuint32(data); +function submitEncryptedData(externalEuint32 data, bytes calldata inputProof) public { + euint32 encryptedData = FHE.asEuint32(data, inputProof); storedData[msg.sender] = encryptedData; FHE.allowThis(storedData[msg.sender]); @@ -151,8 +157,8 @@ function submitEncryptedData(InEuint32 data) public { For values that should be accessible to everyone: ```solidity -function setPublicValue(InEuint32 value) public onlyOwner { - publicValue = FHE.asEuint32(value); +function setPublicValue(externalEuint32 value, bytes calldata inputProof) public onlyOwner { + publicValue = FHE.asEuint32(value, inputProof); FHE.allowPublic(publicValue); // Everyone can now access this value } ``` diff --git a/tutorials/adding-fhe-to-existing-contract.mdx b/tutorials/adding-fhe-to-existing-contract.mdx index 43aa887..690adb9 100644 --- a/tutorials/adding-fhe-to-existing-contract.mdx +++ b/tutorials/adding-fhe-to-existing-contract.mdx @@ -151,7 +151,7 @@ The first thing that we need to do is import `FHE.sol` from the `cofhe-contracts ```solidity pragma solidity ^0.8.25; -import {FHE, euint64, InEuint64} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; +import {FHE, euint64, externalEuint64} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; contract VotingExample { ``` @@ -231,9 +231,9 @@ contract VotingExample { } ``` -### Step 4: Handle user votes with `InEuint8` +### Step 4: Handle user votes with `externalEuint8` -We now need to handle the user's vote casting. The first thing that we need to do is hide which option the user is voting for. We can do this by replacing the `vote` function parameter `uint256 _optionIndex` with `InEuint8 memory _optionIndex`. `InEuint8` is an encrypted input type. We then need to convert the `InEuint8` to an `euint8` for use in computation. +We now need to handle the user's vote casting. The first thing that we need to do is hide which option the user is voting for. We can do this by replacing the `vote` function parameter `uint256 _optionIndex` with two parameters: `externalEuint8 _optionIndex`, the encrypted input handle, and `bytes calldata inputProof`, the proof that authenticates it. `FHE.asEuint8(_optionIndex, inputProof)` then converts the pair into an `euint8` for use in computation. Encrypting inputs requires the use of the [**Client SDK**](/client-sdk/introduction/overview) (`@cofhe/sdk`). @@ -242,8 +242,8 @@ Read more about [**encrypted inputs**](/client-sdk/guides/encrypting-inputs). ```solidity -function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { - euint8 optionIndex = FHE.asEuint8(_optionIndex); +function vote(uint256 _proposalId, externalEuint8 _optionIndex, bytes calldata inputProof) external { + euint8 optionIndex = FHE.asEuint8(_optionIndex, inputProof); ``` ### Step 5: Constant time computation @@ -251,8 +251,8 @@ function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { To preserve the confidentiality of the user's vote, we must make sure that we aren't leaking any information about the user's choice. If we only updated the voting option that the user has selected, then a user's vote could be deduced by simply watching which vote counter changes. Therefore, we must update _all_ the vote counters to hide the user's true vote: ```solidity -function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { - euint8 optionIndex = FHE.asEuint8(_optionIndex); +function vote(uint256 _proposalId, externalEuint8 _optionIndex, bytes calldata inputProof) external { + euint8 optionIndex = FHE.asEuint8(_optionIndex, inputProof); Proposal storage proposal = proposals[_proposalId]; if (!proposal.exists) revert ProposalNotFound(); @@ -338,8 +338,8 @@ By default, access and computation on an encrypted variable is blocked, and tryi - `FHE.allowPublic(ctHash)` - grants access to everyone, useful for things like an encrypted totalSupply variable, which everyone should have access to ```solidity -function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { - euint8 optionIndex = FHE.asEuint8(_optionIndex); +function vote(uint256 _proposalId, externalEuint8 _optionIndex, bytes calldata inputProof) external { + euint8 optionIndex = FHE.asEuint8(_optionIndex, inputProof); Proposal storage proposal = proposals[_proposalId]; if (!proposal.exists) revert ProposalNotFound(); @@ -433,7 +433,7 @@ const signatures = []; for (const option of proposal.options) { const result = await client .decryptForTx(option.votes) - .withoutPermit() + .withoutACP() .execute(); decryptedVotes.push(result.decryptedValue); signatures.push(result.signature); @@ -528,7 +528,7 @@ The resulting contract provides the same functionality as the original, but with pragma solidity ^0.8.25; -import {FHE, euint64, InEuint8} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; +import {FHE, euint64, externalEuint8} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; contract FHEVotingExample { struct Option { @@ -607,8 +607,8 @@ contract FHEVotingExample { return proposalId; } - function vote(uint256 _proposalId, InEuint8 memory _optionIndex) external { - euint8 optionIndex = FHE.asEuint8(_optionIndex); + function vote(uint256 _proposalId, externalEuint8 _optionIndex, bytes calldata inputProof) external { + euint8 optionIndex = FHE.asEuint8(_optionIndex, inputProof); Proposal storage proposal = proposals[_proposalId]; if (!proposal.exists) revert ProposalNotFound(); diff --git a/tutorials/migrating-from-fhe-decrypt.mdx b/tutorials/migrating-from-fhe-decrypt.mdx index 55908de..946607d 100644 --- a/tutorials/migrating-from-fhe-decrypt.mdx +++ b/tutorials/migrating-from-fhe-decrypt.mdx @@ -45,7 +45,7 @@ The client requests decryption from the Threshold Network, which returns the pla ```typescript const result = await client .decryptForTx(ctHash) - .withoutPermit() // use .withPermit() if FHE.allow was used instead of allowPublic + .withoutACP() // use .withACP() if FHE.allow was used instead of allowPublic .execute(); // result.decryptedValue — the plaintext (bigint) @@ -135,7 +135,7 @@ await counter.allow_counter_publicly(); const ctHash = await counter.counter(); const result = await client .decryptForTx(ctHash) - .withoutPermit() + .withoutACP() .execute(); // 3. Publish on-chain with proof @@ -231,7 +231,7 @@ const ctHash = /* ... from UnshieldedERC20 event ... */; // 3. Decrypt off-chain const result = await client .decryptForTx(ctHash) - .withoutPermit() + .withoutACP() .execute(); // 4. Claim with proof diff --git a/tutorials/your-first-fhe-contract.mdx b/tutorials/your-first-fhe-contract.mdx index 3de6564..ccbe7c2 100644 --- a/tutorials/your-first-fhe-contract.mdx +++ b/tutorials/your-first-fhe-contract.mdx @@ -13,7 +13,7 @@ Let's take a look at a simple contract that uses FHE to encrypt a counter, and b // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -import {FHE, euint64, InEuint64} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; +import {FHE, euint64, externalEuint64} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; contract SimpleCounter { address owner; @@ -46,8 +46,8 @@ contract SimpleCounter { FHE.allowThis(counter); } - function reset_counter(InEuint64 calldata value) external onlyOwner { - counter = FHE.asEuint64(value); + function reset_counter(externalEuint64 value, bytes calldata inputProof) external onlyOwner { + counter = FHE.asEuint64(value, inputProof); FHE.allowThis(counter); } @@ -79,10 +79,10 @@ contract SimpleCounter { To start using FHE, we need to import the FHE library. -In this example, we're importing the types `euint64` and `InEuint64` from the [FHE library](/fhe-library/reference/fhe-sol/overview). +In this example, we're importing the types `euint64` and `externalEuint64` from the [FHE library](/fhe-library/reference/fhe-sol/overview). ```solidity -import {FHE, euint64, InEuint64} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; +import {FHE, euint64, externalEuint64} from "@fhenixprotocol/cofhe-contracts/FHE.sol"; ``` We want to keep the counter encrypted at all times, so we'll use the `euint64` type. @@ -151,9 +151,9 @@ FHE.allowThis(counter); ### Reset Function -In the `reset_counter` function, we receive an `InEuint64` value, which is a type that represents an encrypted value that can be used to reset the counter. +In the `reset_counter` function, we receive an `externalEuint64` handle and the `bytes` proof that authenticates it. Together they represent an encrypted value that can be used to reset the counter, and `FHE.asEuint64(value, inputProof)` converts the pair into a `euint64` the contract can compute on. -This value is an encrypted value that we created client-side using the SDK (read more about it [here](/client-sdk/guides/encrypting-inputs)). +Both come from the client, created with the SDK (read more about it [here](/client-sdk/guides/encrypting-inputs)). ### Decryption: Allow Public and Reveal @@ -178,7 +178,7 @@ const countCtHash = await counter.counter(); const result = await client .decryptForTx(countCtHash) - .withoutPermit() + .withoutACP() .execute(); ``` @@ -236,7 +236,7 @@ const countCtHash = await counter.get_encrypted_counter_value(); const result = await client .decryptForView(countCtHash) - .withPermit() + .withACP() .execute(); console.log(`Counter value (private): ${result.decryptedValue}`);