Having trouble with reencryption

Hello,

I’m having some trouble with re-encrypting some data and getting the final result. I’m running my code through a console with node js (no browser). so I’m not sure how to set the signature. Following is my code
contract:

contract Variance{
address public manager;

constructor() {
    manager = msg.sender; //For contract developer specific functionalities.
} 

function calculateVarianceNumerator() public view returns (euint16) {
euint16 sequenceSum = TFHE.NIL16;
for(uint i=0; i<sequence.length; i++){
// sequenceSum = TFHE.add(sequenceSum, sequence[i]);
}
// address add = 0x4E755603d4DabD2C9bBd15993775073C581B54c7;
// TFHE.allow(sequenceSum, add);
return sequenceSum;
}

js:

const interact = async () => {
const instance = await getInstance();

const { publicKey, privateKey } = instance.generateKeypair();

const enc_variance = await contract.calculateVarianceNumerator();
console.log(instance.reencrypt(
enc_variance, // the encrypted balance
privateKey, // the private key generated by the dApp
publicKey, // the public key generated by the dApp
ethers.Signature, // the user’s signature of the public key
CONTRACT_ADDRESS, // The contract address where the ciphertext is
USER_ADDRESS, // The user address where the ciphertext is
));
};

I’m getting the error
Promise {
Error: You must provide the ACL address.
at Object.reencrypt (C:\Users\r\node_modules\fhevmjs\lib\node.cjs:2658:15)
at interact (C:\Users\r\Downloads\calculate_variancev2.js:76:24)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
}

I have tried TFHE.allow as well but that throws another error (sender not allowed)

How do I set the signature if I’m not using a browser and how to resolve the ACL issue? thanks

Hello!
An update of fhevmjs introduces the requirement of aclAddress, so you need to add it when the instance is created. You can take a look at the documentation.
The value to set is 0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92 :slight_smile:

Hi, thank you for the response. Adding aclAdress solved my problem but I’m having another issue.
When I run the re-encrypt method I get the error:
Error: User is not authorized to reencrypt this handle!

And when I add the following line in calculateVarianceNumerator method in my smart contract:

TFHE.allow(sequenceSum, msg.sender);

I get the error which says
error: {
code: -32000,
message: “rpc error: code = Unknown desc = execution reverted: sender isn’t allowed”
}

Is there something wrong with the reencryption request? I’m using ethers.Signature as a parameter for reencrypt()

A ciphertext (euintXX, ebool, eaddress) needs to be allowed to be manipulated, meaning only allowed addresses can use the ciphertext for

  • Computation
  • Decryption
  • Reencryption under a public key

To mark a ciphertext as “allowed”, you need to explicitly write it in your code. There are two way to allow a ciphertext to an address:

  • Permanently: TFHE.allow(ciphertext, address) will write in a contract that this address is allowed for this ciphertext
  • Transiently: TFHE.allowTransient(ciphertext, address) will write in transient storage that this address is allowed for this ciphertext. It means that, at the end of the transaction, the address has no more access to the ciphertext.
    Obviously, only allowed (permanently or transiently) addresses can give access to another address.

So here a quick example:

// Mapping to store user inputs
mapping(address => euint64) public userInputs;
euint64 public result;

// Function to handle encrypted user input and proofs
function myFunction(einput encryptedValue, bytes memory inputProof) public {
    // Verify the user input using the proof to derive a proper euint64
    // This value is transiently allowed for address(this)
    euint64 userInput = TFHE.asEuint64(encryptedValue, inputProof);

    // Store the verified user input in the mapping
    userInputs[msg.sender] = userInput;

    // Allow the user to use the encrypted input. The user will be able to do a reencrypt on this value if needed
    TFHE.allow(userInput, msg.sender);
    // Allow also the contract, otherwise the contract won't be able to use the ciphertext in the future
    TFHE.allowThis(userInput);

    // Perform an addition operation on the user input
    // The user (msg.sender) has no access to the result since it's a computation
    // The contract is transiently allowed on this value `sum`
    euint64 sum = TFHE.add(userInput, 1234);
    // add is transiently allowed to the contract
    result = TFHE.add(sum, userInput);

    // Allow permanently the contract since we want to store this value
    TFHE.allowThis(result);
    // The user can't reencrypt it since they're not allowed
}

Last thing: when you request a reencryption, the value needs to be allowed permanently for the user AND the contract. This is explained in the documentation.

Otherwise you can read the documentation for more info.

Hope it helps!

I decided to create a simple contract to get started with ACL and reencryption but I’m having some trouble with that as well. Can you please guide me with what I’m missing or doing wrong? Following is my code:

smart contract

import “./TFHE.sol”;

contract Test{
address public manager;
euint16 a;
euint16 b;

constructor() {
    manager = msg.sender; //For contract developer specific functionalities.
}

function receiveInput(einput inputA, einput inputB, bytes memory inputProof) public {
    a = TFHE.asEuint16(inputA, inputProof);
    b = TFHE.asEuint16(inputB, inputProof);
}

function calculateSum() public returns (euint16) {
    euint16 sum;
    sum = TFHE.add(a, b);
    return sum;
}

}

js

const getInstance = async () => {
return createInstance({
chainId: 9000,
networkUrl: “https://devnet.zama.ai/”,
gatewayUrl: “https://gateway.zama.ai”,
aclAddress: ‘0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92’,
});
};

const interact = async () => {
try{
const contract = new Contract(CONTRACT_ADDRESS, abi, signer);
const instance = await getInstance();
const { publicKey, privateKey } = instance.generateKeypair();

  const eip712 = instance.createEIP712(publicKey, CONTRACT_ADDRESS);

  // Request the user's signature on the public key
  const params = [USER_ADDRESS, JSON.stringify(eip712)];
  // const signature = await window.ethereum.request({ method: "eth_signTypedData_v4", params });

    const input = instance.createEncryptedInput(CONTRACT_ADDRESS, USER_ADDRESS);
    input.add16(5);
    input.add16(20);
    const encinputs = input.encrypt();
    let input_proof = encinputs.inputProof;

    const transaction = await contract.receiveInput(encinputs.handles[0], encinputs.handles[1], input_proof);
    await transaction.wait();

    const enc_sum = await contract.calculateSum();
    const dec_sum = await instance.reencrypt(
  	enc_sum , // the encrypted balance
  	privateKey, // the private key generated by the dApp
  	publicKey, // the public key generated by the dApp
  	ethers.Signature, // the user's signature of the public key
  	CONTRACT_ADDRESS, // The contract address where the ciphertext is
  	USER_ADDRESS, // The user address where the ciphertext is
    );

} catch(error){
console.log(“Initialization failed”, error);
}
};

interact()
.then(successful => {console.log(“success”); process.exit(0); })
.catch(wrong => {console.log(“something went wrong”, wrong); process.exit(0); });

With this code I’m getting execution reverted error. (probably because of TFHE.add() )
If I remove the addition from calculateSum() in the smart contract and simply return the variable a, I get an error that says TypeError: invalid BigNumberish value.

Thanks

Where do you deploy your contract? And why do you import ./TFHE.sol? You should use the template here GitHub - zama-ai/fhevm-hardhat-template: fhEVM hardhat template

I’m deploying my contract through remix (remix.ethereum.org) and metamask (zama network - devnet).
I installed fhevm though node.js and copied the files in remix.

I have been using fhevm before in this manner and was able to deploy contracts and perform calculations on encrypted data with previous versions so just continued.

Is this a devnet issue?

I think what you’re missing is allow. Since your contract doesn’t use allow, it can’t work. See the previous answer where I explain in details how TFHE.allow is working.
TFHE.allow was not implement in fhEVM 0.4, so maybe you tried fhEVM 0.4, but now we are using fhEVM 0.5.

In remix, you can type import "fhevm/lib/TFHE.sol"; and it will install the npm package :slight_smile:

Thank you clement. I’m importing TFHE.sol correctly now and have added the allow method as well.

I think that solved the problem but “invalid BigNumberish value” error still remains.

Can you help me figure it out as well ? Is there something wrong with the return type? Following is the error message:

Initialization failed TypeError: invalid BigNumberish value (argument=“value”, value={ “_type”: “TransactionReceipt”, “accessList”: [ ], “blockHash”: null, “blockNumber”: null, “chainId”: “9000”, “data”: “0x9dc0b491”, “from”: “0x6E7C2603d4DabD2C9bBd15993775073C581B54c7”, “gasLimit”: “313077”, “gasPrice”: null, “hash”: “0x75771ccbe103109f6dabdd768248610c003262ee3f37e0f0e8290410c0f6a436”, “index”: null, “maxFeePerGas”: “14”, “maxPriorityFeePerGas”: “0”, “nonce”: 136, “signature”: { “_type”: “signature”, “networkV”: null, “r”: “0x7ef6b77ff5e482aa8a093ae4c5a3054edd2956e096932d8fba3f3fccf3be2098”, “s”: “0x0dd8245bddb3e2ff4ac95cc13143be9c50d863f066a20f5553797a7473f924e1”, “v”: 28 }, “to”: “0xB02918060F7365a63685ec7683F67e1F42b5d962”, “type”: 2, “value”: “0” }, code=INVALID_ARGUMENT, version=6.10.0)
at makeError (C:\Users\ra_10\node_modules\ethers\lib.commonjs\utils\errors.js:122:21)
at assert (C:\Users\ra_10\node_modules\ethers\lib.commonjs\utils\errors.js:149:15)
at assertArgument (C:\Users\ra_10\node_modules\ethers\lib.commonjs\utils\errors.js:161:5)
at getBigInt (C:\Users\ra_10\node_modules\ethers\lib.commonjs\utils\maths.js:96:36)
at NumberCoder.encode (C:\Users\ra_10\node_modules\ethers\lib.commonjs\abi\coders\number.js:25:46)
at C:\Users\ra_10\node_modules\ethers\lib.commonjs\abi\coders\array.js:47:19
at Array.forEach ()
at pack (C:\Users\ra_10\node_modules\ethers\lib.commonjs\abi\coders\array.js:33:12)
at TupleCoder.encode (C:\Users\ra_10\node_modules\ethers\lib.commonjs\abi\coders\tuple.js:60:36)
at AbiCoder.encode (C:\Users\ra_10\node_modules\ethers\lib.commonjs\abi\abi-coder.js:170:15) {
code: ‘INVALID_ARGUMENT’,
argument: ‘value’,
value: ContractTransactionResponse {
provider: JsonRpcProvider {},
blockNumber: null,
blockHash: null,
index: undefined,
hash: ‘0x75771ccbe103109f6dabdd768248610c003262ee3f37e0f0e8290410c0f6a436’,
type: 2,
to: ‘0xB02918060F7365a63685ec7683F67e1F42b5d962’,
from: ‘0x6E7C2603d4DabD2C9bBd15993775073C581B54c7’,
nonce: 136,
gasLimit: 313077n,
gasPrice: undefined,
maxPriorityFeePerGas: 0n,
maxFeePerGas: 14n,
data: ‘0x9dc0b491’,
value: 0n,
chainId: 9000n,
signature: Signature { r: “0x7ef6b77ff5e482aa8a093ae4c5a3054edd2956e096932d8fba3f3fccf3be2098”, s: “0x0dd8245bddb3e2ff4ac95cc13143be9c50d863f066a20f5553797a7473f924e1”, yParity: 1, networkV: null },
accessList:
},
shortMessage: ‘invalid BigNumberish value’
}

You can’t do TFHE.add/allow/… in a view function. You should do something like that:

euint16 public sum;
euint16 a;
euint16 b;

function sum(einput inputA, einput inputB, bytes memory inputProof) public {
    a = TFHE.asEuint16(inputA, inputProof);
    b = TFHE.asEuint16(inputB, inputProof);
    sum = TFHE.add(a, b);
    TFHE.allowThis(sum);
    TFHE.allow(sum, msg.sender);
}

In your javascript you need to call sum() function as a transaction (similar to what you did with receiveInput) and then you can get enc_sum by calling the view function await contract.sum()

Hello,
about the same topic (issues with reencryption), i’m facing some different problems.
I’ve deployed a smart contract on the Zama Network and I want the web app interacts with the SC to show and use decrypted data.
The error is this:
“Uncaught (in promise) Error: An error occurred during decryption at Object.reencrypt”.
Can you please help me?
Thanks in advance.

I’ve deployed my contract through remix, I’m using metamask, and I’ve installed fhevm through node.js. So i’m working with javascript.

Can you create your own topic and give context (Contract code, test workflow, etc) ? Thank you!

Thank you. I’m facing another error now (hopefully the last one!):

Gateway didn’t response correctly

Following is my updated js code

const getInstance = async () => {
return createInstance({
chainId: 9000,
networkUrl: “https://devnet.zama.ai/”,
gatewayUrl: “https://gateway.zama.ai”,
aclAddress: ‘0x2Fb4341027eb1d2aD8B5D9708187df8633cAFA92’,
});
};

const interact = async () => {
try{
const contract = new Contract(CONTRACT_ADDRESS, abi, signer);
const instance = await getInstance();
const { publicKey, privateKey } = instance.generateKeypair();

  const eip712 = instance.createEIP712(publicKey, CONTRACT_ADDRESS);

  const signature = await signer.signTypedData(
  	eip712.domain,
  	{ Reencrypt: eip712.types.Reencrypt }, 
  	eip712.message,
  );

    const input = instance.createEncryptedInput(CONTRACT_ADDRESS, USER_ADDRESS);
    input.add16(5);
    input.add16(20);
    const encinputs = input.encrypt();
    let input_proof = encinputs.inputProof;
    
    const transaction = await contract.receiveInput(encinputs.handles[0], encinputs.handles[1], input_proof);
    await transaction.wait();

    const transaction_one = await contract.calculateSum();
    await transaction_one.wait();

  const enc_sum = await contract.returnSum();
    
  try {
  	const dec_sum = await instance.reencrypt(
  	enc_sum, // the encrypted balance
  	privateKey, // the private key generated by the dApp
  	publicKey, // the public key generated by the dApp
  	signature, // the user's signature of the public key
  	CONTRACT_ADDRESS, // The contract address where the ciphertext is
  	USER_ADDRESS, // The user address where the ciphertext is
    );
    console.log(dec_sum);
  } catch(error){
  	console.log(error);
  }

} catch(error){
console.log(“Initialization failed”, error);
}
};

Is there something wrong with the signature that I’m generating ?