-
Notifications
You must be signed in to change notification settings - Fork 74
Description
问题描述:
FHECounter 合约和脚本能100%跑通
ConfidentialFungibleTokenMintableBurnable 合约和脚本(全流程用同一 signer)依然报 fromExternal 授权失败
依赖版本:
@fhevm/hardhat-plugin: 0.0.1-6
fhevm: 0.6.2
@openzeppelin/confidential-contracts: 0.2.0-rc.1
复现步骤:
- 启动 hardhat node
- 运行如下最小测试用例,我的合约代码以及测试脚本如下:
ConfidentialFungibleTokenMintableBurnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {FHE, externalEuint64, ebool, euint64} from "@fhevm/solidity/lib/FHE.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ConfidentialFungibleToken} from "@openzeppelin/confidential-contracts/token/ConfidentialFungibleToken.sol";
import {
IConfidentialFungibleToken
} from "@openzeppelin/confidential-contracts/interfaces/IConfidentialFungibleToken.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
contract ConfidentialFungibleTokenMintableBurnable is ConfidentialFungibleToken, Ownable {
using FHE for *;
mapping(uint256 requestId => address) private _receivers;
IConfidentialFungibleToken private _fromToken;
IERC20 private _toToken;
constructor(
address owner,
string memory name,
string memory symbol,
string memory uri
) ConfidentialFungibleToken(name, symbol, uri) Ownable(owner) {}
function mint(address to, externalEuint64 amount, bytes memory inputProof) public onlyOwner {
_mint(to, FHE.fromExternal(amount, inputProof));
}
function burn(address from, externalEuint64 amount, bytes memory inputProof) public onlyOwner {
_burn(from, FHE.fromExternal(amount, inputProof));
}
function swapConfidentialForConfidential(
IConfidentialFungibleToken fromToken,
IConfidentialFungibleToken toToken,
externalEuint64 amountInput,
bytes calldata inputProof
) public virtual {
require(fromToken.isOperator(msg.sender, address(this)));
euint64 amount = FHE.fromExternal(amountInput, inputProof);
FHE.allowTransient(amount, address(fromToken));
euint64 amountTransferred = fromToken.confidentialTransferFrom(msg.sender, address(this), amount);
FHE.allowTransient(amountTransferred, address(toToken));
toToken.confidentialTransfer(msg.sender, amountTransferred);
}
}
ConfidentialFungibleTokenMintableBurnable.ts
import { ConfidentialFungibleTokenMintableBurnable, ConfidentialFungibleTokenMintableBurnable__factory } from "../types";
import { FhevmType } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers, fhevm } from "hardhat";
import { ethers as ethersjs } from "ethers";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture(deployerAddress: string) {
const factory = (await ethers.getContractFactory("ConfidentialFungibleTokenMintableBurnable")) as ConfidentialFungibleTokenMintableBurnable__factory;
const contract = (await factory.deploy(
deployerAddress, // owner address
"ConfidentialToken",
"CTK",
"https://example.com/metadata"
)) as ConfidentialFungibleTokenMintableBurnable;
const contractAddress = await contract.getAddress();
return { contract, contractAddress };
}
describe("ConfidentialFungibleTokenMintableBurnable", function () {
let signers: Signers;
let contract: ConfidentialFungibleTokenMintableBurnable;
let contractAddress: string;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
await fhevm.initializeCLIApi();
({ contract, contractAddress } = await deployFixture(await signers.deployer.getAddress()));
});
it("should deploy successfully and set correct owner", async function () {
const owner = await contract.owner();
expect(owner).to.equal(await signers.deployer.getAddress());
});
it("should have correct name and symbol", async function () {
const name = await contract.name();
expect(name).to.equal("ConfidentialToken");
const symbol = await contract.symbol();
expect(symbol).to.equal("CTK");
});
it("should have zero initial balance", async function () {
const initialBalance = await contract.confidentialBalanceOf(signers.deployer.address);
const initialBalanceHex = ethersjs.hexlify(initialBalance);
expect(initialBalanceHex).to.equal(ethers.ZeroHash);
});
it("should test FHE encryption and decryption", async function () {
// 全流程用deployer
const clearAmount = 100;
const user = signers.deployer;
console.log("[FHE] Using user:", await user.getAddress());
const encryptedAmount = await fhevm
.createEncryptedInput(contractAddress, await user.getAddress())
.add64(clearAmount)
.encrypt();
const handleHex = ethersjs.hexlify(encryptedAmount.handles[0]);
console.log("[FHE] handleHex:", handleHex);
console.log("[FHE] inputProof:", Buffer.from(encryptedAmount.inputProof).toString("hex"));
const decryptedAmount = await fhevm.userDecryptEuint(
FhevmType.euint64,
handleHex,
contractAddress,
user
);
console.log("[FHE] decryptedAmount:", decryptedAmount);
expect(decryptedAmount).to.equal(clearAmount);
});
it("should allow minting of confidential tokens by owner", async function () {
const clearAmount = 1000;
const user = signers.deployer;
console.log("[MINT] Using user:", await user.getAddress());
const encryptedAmount = await fhevm
.createEncryptedInput(contractAddress, await user.getAddress())
.add64(clearAmount)
.encrypt();
const handleHex = ethersjs.hexlify(encryptedAmount.handles[0]);
console.log("[MINT] handleHex:", handleHex);
console.log("[MINT] inputProof:", Buffer.from(encryptedAmount.inputProof).toString("hex"));
const tx = await contract
.connect(signers.deployer)
.mint(await user.getAddress(), encryptedAmount.handles[0], encryptedAmount.inputProof);
await tx.wait();
const encryptedBalance = await contract.confidentialBalanceOf(await user.getAddress());
const balanceHex = ethersjs.hexlify(encryptedBalance);
console.log("[MINT] balanceHex:", balanceHex);
const clearBalance = await fhevm.userDecryptEuint(
FhevmType.euint64,
balanceHex,
contractAddress,
user
);
console.log("[MINT] clearBalance:", clearBalance);
expect(clearBalance).to.equal(clearAmount);
});
it("should allow burning of confidential tokens by owner", async function () {
const mintAmount = 1000;
const burnAmount = 500;
const user = signers.deployer;
console.log("[BURN] Using user:", await user.getAddress());
const encryptedMintAmount = await fhevm
.createEncryptedInput(contractAddress, await user.getAddress())
.add64(mintAmount)
.encrypt();
const handleHexMint = ethersjs.hexlify(encryptedMintAmount.handles[0]);
console.log("[BURN] handleHexMint:", handleHexMint);
await contract
.connect(signers.deployer)
.mint(await user.getAddress(), encryptedMintAmount.handles[0], encryptedMintAmount.inputProof);
const encryptedBurnAmount = await fhevm
.createEncryptedInput(contractAddress, await user.getAddress())
.add64(burnAmount)
.encrypt();
const handleHexBurn = ethersjs.hexlify(encryptedBurnAmount.handles[0]);
console.log("[BURN] handleHexBurn:", handleHexBurn);
const tx = await contract
.connect(signers.deployer)
.burn(await user.getAddress(), encryptedBurnAmount.handles[0], encryptedBurnAmount.inputProof);
await tx.wait();
const encryptedBalance = await contract.confidentialBalanceOf(await user.getAddress());
const balanceHex = ethersjs.hexlify(encryptedBalance);
console.log("[BURN] balanceHex:", balanceHex);
const clearBalance = await fhevm.userDecryptEuint(
FhevmType.euint64,
balanceHex,
contractAddress,
user
);
console.log("[BURN] clearBalance:", clearBalance);
expect(clearBalance).to.equal(mintAmount - burnAmount);
});
it("should allow swapping of confidential tokens", async function () {
const mintAmount = 1000;
const swapAmount = 300;
const user = signers.deployer;
console.log("[SWAP] Using user:", await user.getAddress());
const encryptedMintAmount = await fhevm
.createEncryptedInput(contractAddress, await user.getAddress())
.add64(mintAmount)
.encrypt();
const handleHexMint = ethersjs.hexlify(encryptedMintAmount.handles[0]);
console.log("[SWAP] handleHexMint:", handleHexMint);
await contract
.connect(signers.deployer)
.mint(await user.getAddress(), encryptedMintAmount.handles[0], encryptedMintAmount.inputProof);
const tx1 = await contract
.connect(user)
.setOperator(contractAddress, Math.floor(Date.now() / 1000) + 3600);
await tx1.wait();
const encryptedSwapAmount = await fhevm
.createEncryptedInput(contractAddress, await user.getAddress())
.add64(swapAmount)
.encrypt();
const handleHexSwap = ethersjs.hexlify(encryptedSwapAmount.handles[0]);
console.log("[SWAP] handleHexSwap:", handleHexSwap);
const tx2 = await contract
.connect(user)
.swapConfidentialForConfidential(
contract,
contract,
encryptedSwapAmount.handles[0],
encryptedSwapAmount.inputProof
);
await tx2.wait();
const encryptedBalance = await contract.confidentialBalanceOf(await user.getAddress());
const balanceHex = ethersjs.hexlify(encryptedBalance);
console.log("[SWAP] balanceHex:", balanceHex);
const clearBalance = await fhevm.userDecryptEuint(
FhevmType.euint64,
balanceHex,
contractAddress,
user
);
console.log("[SWAP] clearBalance:", clearBalance);
expect(clearBalance).to.equal(mintAmount);
});
});
报错日志
yoona1020@localhost:~/Projects/zh-zama$ npx hardhat test
ConfidentialFungibleTokenMintableBurnable
✔ should deploy successfully and set correct owner
✔ should have correct name and symbol
✔ should have zero initial balance
[FHE] Using user: 0xc7b0D4dc5184b95Dda276b475dF59C3686d3E724
[FHE] handleHex: 0xcb61d40b70f7e2e88923cd08db153f3a3c474bfa66000000000000007a690500
[FHE] inputProof: 0101cb61d40b70f7e2e88923cd08db153f3a3c474bfa66000000000000007a6905001c06faf337e12eeda6c82127f1ff8085224cd13cda7fbdc6ddc49798631bf90d76fafdceec6296264f407b30e35d208a61876fe2e85b3ca3672eec6665ec03321b
1) should test FHE encryption and decryption
[MINT] Using user: 0xc7b0D4dc5184b95Dda276b475dF59C3686d3E724
[MINT] handleHex: 0xd3639cc273450387249eddcdaba06ebad76c7cdee6000000000000007a690500
[MINT] inputProof: 0101d3639cc273450387249eddcdaba06ebad76c7cdee6000000000000007a690500922c170eb530346dcdba411a383fe5d1d847d297df6fb3822344514905cee48627f9afd23df3563c18b00983a626a1e928d534fe82131acbfe4a4ee40f1d57771c
2) should allow minting of confidential tokens by owner
[BURN] Using user: 0xc7b0D4dc5184b95Dda276b475dF59C3686d3E724
[BURN] handleHexMint: 0xcff3f3161f37bd2608d21e408e7c4452d3397ed063000000000000007a690500
3) should allow burning of confidential tokens by owner
[SWAP] Using user: 0xc7b0D4dc5184b95Dda276b475dF59C3686d3E724
[SWAP] handleHexMint: 0x9f1cf54e18f91c401cc49519f550a4aae34a892992000000000000007a690500
4) should allow swapping of confidential tokens
3 passing (166ms)
4 failing
-
ConfidentialFungibleTokenMintableBurnable
should test FHE encryption and decryption:
Error: User 0xc7b0D4dc5184b95Dda276b475dF59C3686d3E724 is not authorized to user decrypt handle 0xcb61d40b70f7e2e88923cd08db153f3a3c474bfa66000000000000007a690500!
at /home/yoona1020/Projects/zh-zama/node_modules/@fhevm/mock-utils/fhevm/MockFhevmInstance.ts:451:15
at async Promise.all (index 0)
at async MockFhevmInstance.userDecrypt (node_modules/@fhevm/mock-utils/fhevm/MockFhevmInstance.ts:313:5)
at async userDecryptHandleBytes32 (node_modules/@fhevm/mock-utils/fhevm/userDecrypt.ts:91:46)
at async Proxy.userDecryptEuint (node_modules/@fhevm/hardhat-plugin/src/internal/FhevmExternalAPI.ts:277:48)
at async Context. (test/ConfidentialFungibleTokenMintableBurnable.ts:72:29) -
ConfidentialFungibleTokenMintableBurnable
should allow minting of confidential tokens by owner:
Error: Transaction reverted: function returned an unexpected amount of data
at ConfidentialFungibleTokenMintableBurnable.verify (@fhevm/solidity/lib/Impl.sol:637)
at ConfidentialFungibleTokenMintableBurnable.fromExternal (@fhevm/solidity/lib/FHE.sol:8286)
at ConfidentialFungibleTokenMintableBurnable.mint (contracts/ConfidentialFungibleTokenMintableBurnable.sol:26)
at EdrProviderWrapper.request (node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:359:41)
at async FhevmProviderExtender._handleEthSendTransaction (node_modules/@fhevm/hardhat-plugin/src/internal/provider/FhevmProviderExtender.ts:108:14)
at async HardhatEthersSigner.sendTransaction (node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:181:18)
at async send (node_modules/ethers/src.ts/contract/contract.ts:313:20)
at async Proxy.mint (node_modules/ethers/src.ts/contract/contract.ts:352:16)
at async Context. (test/ConfidentialFungibleTokenMintableBurnable.ts:93:16) -
ConfidentialFungibleTokenMintableBurnable
should allow burning of confidential tokens by owner:
Error: Transaction reverted: function returned an unexpected amount of data
at ConfidentialFungibleTokenMintableBurnable.verify (@fhevm/solidity/lib/Impl.sol:637)
at ConfidentialFungibleTokenMintableBurnable.fromExternal (@fhevm/solidity/lib/FHE.sol:8286)
at ConfidentialFungibleTokenMintableBurnable.mint (contracts/ConfidentialFungibleTokenMintableBurnable.sol:26)
at EdrProviderWrapper.request (node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:359:41)
at async FhevmProviderExtender._handleEthSendTransaction (node_modules/@fhevm/hardhat-plugin/src/internal/provider/FhevmProviderExtender.ts:108:14)
at async HardhatEthersSigner.sendTransaction (node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:181:18)
at async send (node_modules/ethers/src.ts/contract/contract.ts:313:20)
at async Proxy.mint (node_modules/ethers/src.ts/contract/contract.ts:352:16)
at async Context. (test/ConfidentialFungibleTokenMintableBurnable.ts:121:5) -
ConfidentialFungibleTokenMintableBurnable
should allow swapping of confidential tokens:
Error: Transaction reverted: function returned an unexpected amount of data
at ConfidentialFungibleTokenMintableBurnable.verify (@fhevm/solidity/lib/Impl.sol:637)
at ConfidentialFungibleTokenMintableBurnable.fromExternal (@fhevm/solidity/lib/FHE.sol:8286)
at ConfidentialFungibleTokenMintableBurnable.mint (contracts/ConfidentialFungibleTokenMintableBurnable.sol:26)
at EdrProviderWrapper.request (node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:359:41)
at async FhevmProviderExtender._handleEthSendTransaction (node_modules/@fhevm/hardhat-plugin/src/internal/provider/FhevmProviderExtender.ts:108:14)
at async HardhatEthersSigner.sendTransaction (node_modules/@nomicfoundation/hardhat-ethers/src/signers.ts:181:18)
at async send (node_modules/ethers/src.ts/contract/contract.ts:313:20)
at async Proxy.mint (node_modules/ethers/src.ts/contract/contract.ts:352:16)
at async Context. (test/ConfidentialFungibleTokenMintableBurnable.ts:158:5)
yoona1020@localhost:~/Projects/zh-zama$
备注:
全流程用同一个 signer,应该能通过 FHE handle 授权和 fromExternal 校验
FHECounter 合约和脚本能跑通,说明 mock 环境没坏,问题只在 OZ ConfidentialFungibleToken 体系和 mock 的 fromExternal 授权机制。