From b11f7f73d6c61761edccd2b4fad95729fecb837b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joseph-Andr=C3=A9=20Turk?= Date: Wed, 7 Feb 2024 16:54:16 +0100 Subject: [PATCH] minimal bug --- contracts/A.sol | 16 ++++ contracts/B.sol | 21 +++++ contracts/Oracle.sol | 93 ----------------------- contracts/TestAsyncDecrypt.sol | 35 --------- test/testAsyncDecrypt/testAsyncDecrypt.ts | 35 +++------ 5 files changed, 48 insertions(+), 152 deletions(-) create mode 100644 contracts/A.sol create mode 100644 contracts/B.sol delete mode 100644 contracts/Oracle.sol delete mode 100644 contracts/TestAsyncDecrypt.sol diff --git a/contracts/A.sol b/contracts/A.sol new file mode 100644 index 0000000..a8e77d6 --- /dev/null +++ b/contracts/A.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +pragma solidity ^0.8.20; + +import "fhevm/lib/TFHE.sol"; + +contract A { + + constructor() {} + + function requestDecryption(euint32 ct) external { + require(TFHE.isInitialized(ct), "Ciphertext is not initialized"); + euint32 ct_r = TFHE.shl(ct, 0); + } + +} diff --git a/contracts/B.sol b/contracts/B.sol new file mode 100644 index 0000000..ba06de9 --- /dev/null +++ b/contracts/B.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +pragma solidity ^0.8.20; + +import "fhevm/lib/TFHE.sol"; +import "./A.sol"; + +contract B { + euint32 x; + A a; + + constructor(address _a) { + a = A(_a); + x = TFHE.asEuint32(32); + } + + function request() public { + a.requestDecryption(x); + } + +} diff --git a/contracts/Oracle.sol b/contracts/Oracle.sol deleted file mode 100644 index 5178e2f..0000000 --- a/contracts/Oracle.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause-Clear - -pragma solidity ^0.8.20; - -import "fhevm/lib/TFHE.sol"; -import "@openzeppelin/contracts/access/Ownable2Step.sol"; - -contract Oracle is Ownable2Step { - struct DecryptionRequest { - euint32 ct; - address callbackAddress; - bytes callbackdata; - uint256 msgValue; - uint256 maxTimestamp; - } - - uint256 counter; // tracks the number of decryption requests - - mapping(address => bool) isRelayer; - mapping(uint256 => DecryptionRequest) decryptionRequests; - mapping(uint256 => bool) isFulfilled; - - constructor() Ownable(msg.sender) {} - - event EventDecryption( - uint256 indexed requestID, - euint32 ct, - address callbackAddress, - bytes callbackdata, - uint256 msgValue, - uint256 maxTimestamp - ); - - event AddedRelayer(address indexed realyer); - - event RemovedRelayer(address indexed realyer); - - event ResultCallback(uint256 indexed requestID, bool success, bytes result); - - function addRelayer(address relayerAddress) external onlyOwner { - require(!isRelayer[relayerAddress], "Address is already relayer"); - isRelayer[relayerAddress] = true; - emit AddedRelayer(relayerAddress); - } - - function removeRelayer(address relayerAddress) external onlyOwner { - require(isRelayer[relayerAddress], "Address is not a relayer"); - isRelayer[relayerAddress] = false; - emit RemovedRelayer(relayerAddress); - } - - // Requests the decryption of ciphertext `ct` with the result returned in a callback. - // During callback, callbackAddress is called with [callbackdata,decrypt(ct)] as calldata. - // Meant to be called in transactions. - function requestDecryption( - euint32 ct, - address callbackAddress, - bytes memory callbackdata, - uint256 msgValue, // msg.value of callback tx, if callback is payable - uint256 maxTimestamp - ) external { - require(TFHE.isInitialized(ct), "Ciphertext is not initialized"); - euint32 ct_r = TFHE.shl(ct, 0); // this is similar to no-op, except it would fail if ct is a "fake" handle, - // not corresponding to a verified ciphertext in privileged memory - DecryptionRequest memory decryptionReq = DecryptionRequest( - ct_r, - callbackAddress, - callbackdata, - msgValue, - maxTimestamp - ); - decryptionRequests[counter] = decryptionReq; - emit EventDecryption(counter, ct_r, callbackAddress, callbackdata, msgValue, maxTimestamp); - counter++; - } - - function fulfillRequest(uint256 requestID, uint32 decryptedCt) external payable onlyRelayer { - require(!isFulfilled[requestID], "Request is already fulfilled"); - DecryptionRequest memory decryptionReq = decryptionRequests[requestID]; - require(block.timestamp <= decryptionReq.maxTimestamp, "Too late"); - bytes memory calldataComplete = abi.encode(decryptionReq.callbackdata, decryptedCt); - (bool success, bytes memory result) = (decryptionReq.callbackAddress).call{ value: decryptionReq.msgValue }( - calldataComplete - ); - emit ResultCallback(requestID, success, result); - isFulfilled[requestID] = true; - } - - modifier onlyRelayer() { - require(isRelayer[msg.sender], "Not relayer"); - _; - } -} diff --git a/contracts/TestAsyncDecrypt.sol b/contracts/TestAsyncDecrypt.sol deleted file mode 100644 index 489bc8a..0000000 --- a/contracts/TestAsyncDecrypt.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause-Clear - -pragma solidity ^0.8.20; - -import "fhevm/lib/TFHE.sol"; -import "./Oracle.sol"; - -contract TestAsyncDecrypt { - euint32 x; - uint32 y; - Oracle oracle; - - modifier onlyOracle() { - require(msg.sender == address(oracle)); - _; - } - - constructor(address _oracle) { - oracle = Oracle(_oracle); - x = TFHE.asEuint32(32); - } - - function request(uint32 input1, uint32 input2) public { - bytes memory callbackdata = abi.encodeWithSignature("callback(uint32,uint32)", input1, input2); - oracle.requestDecryption(x, address(this), callbackdata, 0, block.timestamp + 100); - } - - // Transfers an encrypted amount from the message sender address to the `to` address. - function callback(uint32 userInput1, uint32 userInput2, uint32 decryptedResult) public onlyOracle returns (uint32) { - unchecked { - uint32 result = userInput1 + userInput2 + decryptedResult; - return result; - } - } -} diff --git a/test/testAsyncDecrypt/testAsyncDecrypt.ts b/test/testAsyncDecrypt/testAsyncDecrypt.ts index 5a5a3f0..00203b5 100644 --- a/test/testAsyncDecrypt/testAsyncDecrypt.ts +++ b/test/testAsyncDecrypt/testAsyncDecrypt.ts @@ -1,37 +1,24 @@ -import { expect } from "chai"; import { ethers } from "hardhat"; - -import { createInstances } from "../instance"; import { getSigners, initSigners } from "../signers"; -describe("TestAsyncDecrypt", function () { +describe("Test", function () { before(async function () { await initSigners(); this.signers = await getSigners(); }); beforeEach(async function () { - const oracleFactory = await ethers.getContractFactory("Oracle"); - this.oracle = await oracleFactory.connect(this.signers.alice).deploy(); // Alice is the Oracle Admin - await this.oracle.waitForDeployment(); - - const tx1 = await this.oracle.addRelayer(this.signers.bob); // Bob is an Oracle Relayer - await tx1.wait(); + const aFactory = await ethers.getContractFactory("A"); + this.a = await aFactory.connect(this.signers.alice).deploy(); // Alice is the Oracle Admin + await this.a.waitForDeployment(); - const oracleAddress = await this.oracle.getAddress(); - const contractFactory = await ethers.getContractFactory("TestAsyncDecrypt"); - this.contract = await contractFactory.connect(this.signers.alice).deploy(oracleAddress); + const aAddress = await this.a.getAddress(); + const bFactory = await ethers.getContractFactory("B"); + this.b = await bFactory.connect(this.signers.alice).deploy(aAddress); }); - it("test async decrypt", async function () { - const tx2 = await this.contract.connect(this.signers.carol).request(5, 15); - await tx2.wait(); - - const filter = this.oracle.filters.EventDecryption; - const events = await this.oracle.queryFilter(filter, -1); - const event = events[0]; - - const args = event.args; - console.log(args); + it("test b", async function () { + const tx = await this.b.connect(this.signers.carol).request(); + await tx.wait(); }); -}); +}); \ No newline at end of file